GithubHelp home page GithubHelp logo

microsoft / jupyter-kqlmagic Goto Github PK

View Code? Open in Web Editor NEW
82.0 7.0 30.0 3.71 MB

Extension (Magic) to Jupyter notebook and Jupyter lab, that enable notebook experience working with Kusto, ApplicationInsights, and LogAnalytics data.

License: Other

Python 83.95% Jupyter Notebook 14.34% Shell 0.01% Batchfile 0.70% HTML 0.99%

jupyter-kqlmagic's Introduction

Kqlmagic: Microsoft Azure Monitor magic extension to Jupyter notebook.

PyPi Version Python Versions License

PyPI link: https://pypi.org/project/kqlmagic/, PePy download information:

Downloads Downloads Downloads

Kqlmagic magic extension enables notebook experience, exploring Microsoft Azure Monitor data: Azure Data Explorer (Kusto), ApplicationInsights, and LogAnalytics data, from Jupyter notebook (Python3 kernel), using kql (Kusto Query language).

Install

Option 1: Via PyPi

To install via the Python Package Index (PyPI), type:

pip install Kqlmagic

Option 2: Source Via Git

To get the source code of the SDK via git just type:

git clone git://github.com/Microsoft/jupyter-Kqlmagic.git
cd ./jupyter-Kqlmagic
python setup.py install

Option 3: Source Zip

Download a zip of the code via GitHub or PyPI. Then follow the same instructions as in option 2.

Dependencies control

You can control which Kqlmagic dependencies will be installed, by settimg installing KqlmagicCustom instead of Kqlmagic with extras_require. Features that require a missing dependency will be disabled. Following is the List of the Extras:

'default',
    'jupyter-all',
    'ipython-all',
    'python-all',

        'plotly',
        'pandas',
        'widgets',

        'extended',
            'jupyter-extended',
            'ipython-extended',
            'python-extended',

            'sso',
                'azcli_sso',
                'msi_sso',
                'vscode_sso',
                'msal_sso',
                'kqlmagic_sso',
                'kqlmagic_msal_sso',
                'kqlmagic_fernet_sso',

            'utils',
                'base_utils',
                'text_utils',
                'json_color',
                'auth_code_clipboard',
                'matplotlib_palettes',

            'basic',
                'jupyter-basic',
                'ipython-basic',
                'python-basic',

                    'naked',

Some packages are mandatory, and Kqlmagic won't start without them. Most packages are optional, and Kqlmagic will disable the functionality that is based on the missing dependencies.

for example: will install extended packages + pandas:

pip install KqlmagicCustom[extended, pandas]

will install extended basic packages that work with Jupyter, azcli single sign on and plotly:

pip install KqlmagicCustom[jupyter-basic,azcli_sso,plotly]

Versions

This library follows Semantic Versioning.

You can find the changes for each version under Releases.

Minimum Requirements

  • Python 3.6+
  • See setup.py for dependencies

Load

To load the Kqlmagic extension, run in notebook cell:

In [ ]: %reload_ext Kqlmagic

Authentication methods

  • AAD Username/password - Provide your AAD username and password.
  • AAD application - Provide your AAD tenant ID, AAD app ID and app secret.
  • AAD code - Provide only your AAD username, and authenticate yourself using a code, generated by ADAL.
  • certificate - Provide your AAD tenant ID, AAD app ID, certificate and certificate-thumbprint (supported only with Azure Data Explorer)
  • appid/appkey - Provide you application insight appid, and appkey (supported only with Application Insights)

Get Started Notebooks

Need Support?

  • Have a feature request for Kqlmagic? Please post it on User Voice to help us prioritize
  • Have a technical question? Ask on Stack Overflow with tag "Kqlmagic"
  • Need Support? Every customer with an active Azure subscription has access to support with guaranteed response time. Consider submitting a ticket and get assistance from Microsoft support team
  • Found a bug? Please help us fix it by thoroughly documenting it and filing an issue.

Contribute

We gladly accept community contributions.

  • Issues: Please report bugs using the Issues section of GitHub
  • Forums: Interact with the development teams on StackOverflow or the Microsoft Azure Forums
  • Source Code Contributions: If you would like to become an active contributor to this project please follow the instructions provided in Contributing.md.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

For general suggestions about Microsoft Azure please use our UserVoice forum.

jupyter-kqlmagic's People

Contributors

avitalfriedland avatar debonte avatar forresttrepte avatar jruales avatar lupino3 avatar mbnshtck avatar microsoftopensource avatar miflower avatar msftgits avatar neelaryan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

jupyter-kqlmagic's Issues

Python 3.10.0 breaks Kqlmagic dependency (AttributeError: module 'collections' has no attribute 'Iterator' exception)

When jupyter notebook's python kernel got automatically updated to 3.10.0 version (from 3.9.7), the update broke Kqlmagic dependency. Exception gets thrown on this command: %reload_ext Kqlmagic

Full exception trace:


AttributeError Traceback (most recent call last)
/tmp/ipykernel_6965/107116530.py in
1 # Connect to Kusto using cached az cli creds if they exist
2 get_ipython().run_line_magic('env', 'KQLMAGIC_LOAD_MODE=silent')
----> 3 get_ipython().run_line_magic('reload_ext', 'Kqlmagic')
4 get_ipython().run_line_magic('kql', "AzureDataExplorer://code;cluster='VSO';database='VSO' -try_vscode_login")

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line, _stack_depth)
2349 kwargs['local_ns'] = self.get_local_scope(stack_depth)
2350 with self.builtin_trap:
-> 2351 result = fn(*args, **kwargs)
2352 return result
2353

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/decorator.py in fun(*args, **kw)
230 if not kwsyntax:
231 args, kw = fix(args, kw, sig)
--> 232 return caller(func, *(extras + args), **kw)
233 fun.name = func.name
234 fun.doc = func.doc

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/IPython/core/magic.py in (f, *a, **k)
185 # but it's overkill for just that one bit of state.
186 def magic_deco(arg):
--> 187 call = lambda f, *a, **k: f(*a, **k)
188
189 if callable(arg):

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/IPython/core/magics/extension.py in reload_ext(self, module_str)
61 if not module_str:
62 raise UsageError('Missing module name.')
---> 63 self.shell.extension_manager.reload_extension(module_str)

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/IPython/core/extensions.py in reload_extension(self, module_str)
128 self.loaded.add(module_str)
129 else:
--> 130 self.load_extension(module_str)
131
132 def _call_load_ipython_extension(self, mod):

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/IPython/core/extensions.py in load_extension(self, module_str)
78 if module_str not in sys.modules:
79 with prepended_to_syspath(self.ipython_extension_dir):
---> 80 mod = import_module(module_str)
81 if mod.file.startswith(self.ipython_extension_dir):
82 print(("Loading extensions from {dir} is deprecated. "

/usr/local/lib/python3.10/importlib/init.py in import_module(name, package)
124 break
125 level += 1
--> 126 return _bootstrap._gcd_import(name[level:], package, level)
127
128

/usr/local/lib/python3.10/importlib/_bootstrap.py in _gcd_import(name, package, level)

/usr/local/lib/python3.10/importlib/_bootstrap.py in find_and_load(name, import)

/usr/local/lib/python3.10/importlib/_bootstrap.py in find_and_load_unlocked(name, import)

/usr/local/lib/python3.10/importlib/_bootstrap.py in _load_unlocked(spec)

/usr/local/lib/python3.10/importlib/_bootstrap_external.py in exec_module(self, module)

/usr/local/lib/python3.10/importlib/_bootstrap.py in _call_with_frames_removed(f, *args, **kwds)

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/init.py in
12 pass
13
---> 14 from .magic_extension import load_ipython_extension, unload_ipython_extension
15 from .version import version
16 from .kql_magic import kql, kql_stop

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/magic_extension.py in
7 from .constants import Constants
8 from .log import logger
----> 9 from .kql_magic import Kqlmagic
10
11

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/kql_magic.py in
24 from .ipython_api import IPythonAPI
25
---> 26 from .kql_magic_core import Kqlmagic_core
27 from .constants import Constants, Cloud
28 from .palette import Palettes, Palette

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/kql_magic_core.py in
24
25 from .ipython_api import IPythonAPI
---> 26 from .sso_storage import clear_sso_store
27 from ._version import version
28 from .version import get_pypi_latest_version, is_stable_version, pre_version_label, compare_version, execute_version_command, validate_required_python_version_running

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/sso_storage.py in
11
12
---> 13 from .parser import Parser
14 from .display import Display
15 from .ipython_api import IPythonAPI

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/parser.py in
19 from .constants import Constants, Schema
20 from .my_utils import split_lex, adjust_path, is_env_var, get_env_var, is_collection, strip_if_quoted
---> 21 from .engine import Engine
22
23

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/engine.py in
8
9
---> 10 from .kql_response import KqlQueryResponse, KqlSchemaResponse
11 from .kql_proxy import KqlResponse
12 from .kql_client import KqlClient

/workspaces/c2c-actions-abuse/.venv/lib/python3.10/site-packages/Kqlmagic/kql_response.py in
49
50
---> 51 class KqlResponseTable(collections.Iterator):
52 """ Iterator over returned rows """
53

AttributeError: module 'collections' has no attribute 'Iterator'

Also attaching an image with exception in IDE. There's a 'Runtime dependency of PyGObject is missing.' error as well, but it's a non-blocking error, please ignore.
Screen Shot 2021-10-28 at 3 42 13 PM

KQLMagic silently truncates records returned without warning

While attempting a large query, KQLMagic silently truncates records and it is unclear if some limit, e.g. data size or timeout might be causing the truncation.

Given the following adjustment to defaults, with enable_suppress_result and timeout left on the default:

%env KQLMAGIC_CONFIGURATION='auto_limit=1000000;display_limit=100'

And then a parameterized limit:

kql_parms = {
    'p_limit': 1000000
}

Followed by a Kusto query that should be able to return 1,000,000 records, but not output them (trailing semi-colon):

%%kql -params_dict kql_parms
let _limit_ = p_limit;
ThreatIntelligenceIndicator
| limit _limit_
;

Only a few more than 100K records were returned, but the ThreatIntelligenceIndicator does have more than 1 million records.

Screenshot:

KQLMagic query with large limit has records truncated

_kql_raw_result_.feedback_warning was an empty list. This is the main part of the issue. It may be reasonable to enforce resource limits, but at least provide feedack/warnings when doing so.

I attempted to inspect _kql_raw_result_.completion_query_info, but it was an empty JSON object, and is the only hint that something went wrong and the query did not actually complete.

{}

I inspected _kql_raw_result_.completion_query_resource_consumption

{
    "ExecutionTime": 9.1875081,
    "dataset_statistics": [
        {
            "table_row_count": 114138,
            "table_size": 66346649
        }
    ],
    "input_dataset_statistics": {
        "extents": {
            "scanned": 135,
            "scanned_max_datetime": "2021-09-27T10:34:33.0395942Z",
            "scanned_min_datetime": "2021-07-12T06:48:28.3176520Z",
            "total": 135
        },
        "rows": {
            "scanned": 142882578,
            "total": 142882578
        },
        "rowstores": {
            "scanned_rows": 0,
            "scanned_values_size": 0
        },
        "shards": {
            "queries_generic": 0,
            "queries_specialized": 0
        }
    },
    "resource_usage": {
        "cache": {
            "disk": {
                "hits": 0,
                "misses": 0,
                "total": 0
            },
            "memory": {
                "hits": 0,
                "misses": 0,
                "total": 0
            },
            "shards": {
                "bypassbytes": 0,
                "cold": {
                    "hitbytes": 0,
                    "missbytes": 0,
                    "retrievebytes": 0
                },
                "hot": {
                    "hitbytes": 0,
                    "missbytes": 0,
                    "retrievebytes": 0
                }
            }
        },
        "cpu": {
            "kernel": "00:00:00.2343750",
            "total cpu": "00:00:00.2500000",
            "user": "00:00:00.0156250"
        },
        "memory": {
            "peak_per_node": 3565520
        }
    }
}

From the above, it's unclear if any memory or other such resource limits are hit and causing any problem.

When I ran a smaller query limit, e.g. just 10K instead of 1M:

%%kql
ThreatIntelligenceIndicator
| limit 10000
;

Then _kql_raw_result_.completion_query_info is not an empty JSON object:

{
    "Count": 4,
    "StatusCode": 0,
    "StatusDescription": "Query completed successfully"
}

Perhaps the connection/paging of data has some limit, or the back-end resources have some limit, but no warning or feedback is provided in the output of the notebook to make it clear records have been truncated and why.

Feature Request: Simpler Query Parameterization

Currently, it's necessary to add let statements in a very specific way at the start of the query in order to use Python variables within a KQLMagic query. This can feel unnatural and can cause the query to become verbose. Would it be possible to have a simpler way to parameterize queries?

One way that comes to mind is the way that Kusto.Explorer does it, which is to surround parameters with curly braces. basically, anything that matches something like the regex /{[\w-]+}/ would be replaced with the Python variable of the same name, so the query %kql print {message1} + {message2} would work, instead of having to do %kql let message1 = message1; let message2 = message2; print message1 + message2

Running kqlmagic in a python script.

expectation: is to use kql function in a python script.

from Kqlmagic import kql

kql("""appinsights://appid='xxx'; appkey='xxx'; alias='xxx' """)
page_view = f'''pageViews '''

kql(page_view).to_dataframe())

This works, however, is there a way to suppress the popup schema message (html) after authentication ?

opens in file location C:/Users/xxx/kqlmagic/temp_files/xxx/xxx/xxx.html

ImportError: this version of pandas is incompatible with numpy < 1.12.0 on databricks cluster python 3

I used Kqlmagic 0.1.106.post2 with and Azure Databricks 5.5 cluster (python 3)

`ImportError: this version of pandas is incompatible with numpy < 1.12.0

ImportError Traceback (most recent call last)
in ()
----> 1 get_ipython().magic('reload_ext Kqlmagic')

/databricks/python/lib/python3.5/site-packages/IPython/core/interactiveshell.py in magic(self, arg_s)
2203 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
2204 """
-> 2205
2206 # Inject names into builtin so we can complete on the added names.
2207 with self.builtin_trap:

/databricks/python/lib/python3.5/site-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line)
2124
2125 def _indent_current_str(self):
-> 2126 """return the current level of indentation as a string"""
2127 return self.input_splitter.get_indent_spaces() * ' '
2128

in reload_ext(self, module_str)
`

Cannot map connection string

Kusto quick start does not provide a way to map my connection string to Kqlmagic.

Using Azure Data Explorer or Kusto.Explorer I connect to my cluster using a connection string with the following parts

Data Source=https://foobar.westeurope.kusto.windows.net:443;Initial Catalog=NetDefaultDB;AAD Federated Security=True;Authority Id=BIGLONG0-GUID-WITH-LOTS-ONUMBERYBITS

While I can guess what the database name might be from Kusto.Explorer, nothing hints at how to map the rest of the values.

I tried the following

%kql azure_data-Explorer://code;cluster='foobar.westeurope';database='dbname';tenant='BIGLONG0-GUID-WITH-LOTS-ONUMBERYBITS'

but after copying the device code and logging in I received the following error

FileNotFoundError: [Errno 2] No such file or directory: '/mnt/c/repos/log-tools/kqlmagic/temp_files/7786fe28-07e9-4b53-a830-c8ff435900e5/_dbname_at_foobar_westeurope_schema.html'

ImportError: The plotly.plotly module is deprecated; while loading Kqlmagic

I'm running a Microsoft Azure Notebook. After updating the pip (!pip install --upgrade pip) and installing Kqlmagic (!pip3 install --ignore-installed Kqlmagic --no-cache-dir --upgrade), I was trying to load the Kqlmagic (%reload_ext Kqlmagic) but I've received the following error:

`

ImportError Traceback (most recent call last)
in ()
----> 1 get_ipython().run_line_magic('reload_ext', 'Kqlmagic')

~/anaconda3_420/lib/python3.5/site-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line, _stack_depth)
2093 def showindentationerror(self):
2094 """Called by _run_cell when there's an IndentationError in code entered
-> 2095 at the prompt.
2096
2097 This is overridden in TerminalInteractiveShell to show a message about

</home/nbuser/anaconda3_420/lib/python3.5/site-packages/decorator.py:decorator-gen-67> in reload_ext(self, module_str)

~/anaconda3_420/lib/python3.5/site-packages/IPython/core/magic.py in (f, *a, **k)
185 # but it's overkill for just that one bit of state.
186 def magic_deco(arg):
--> 187 call = lambda f, *a, **k: f(*a, **k)
188
189 if callable(arg):

~/anaconda3_420/lib/python3.5/site-packages/IPython/core/magics/extension.py in reload_ext(self, module_str)
61 if not module_str:
62 raise UsageError('Missing module name.')
---> 63 self.shell.extension_manager.reload_extension(module_str)

~/anaconda3_420/lib/python3.5/site-packages/IPython/core/extensions.py in reload_extension(self, module_str)
133 if hasattr(mod, 'load_ipython_extension'):
134 mod.load_ipython_extension(self.shell)
--> 135 return True
136
137 def _call_unload_ipython_extension(self, mod):

~/anaconda3_420/lib/python3.5/site-packages/IPython/core/extensions.py in load_extension(self, module_str)
83 "We recommend managing extensions like any "
84 "other Python packages, in site-packages.").format(
---> 85 dir=compress_user(self.ipython_extension_dir)))
86 mod = sys.modules[module_str]
87 if self._call_load_ipython_extension(mod):

~/anaconda3_420/lib/python3.5/importlib/init.py in import_module(name, package)
124 break
125 level += 1
--> 126 return _bootstrap._gcd_import(name[level:], package, level)
127
128

~/anaconda3_420/lib/python3.5/importlib/_bootstrap.py in _gcd_import(name, package, level)

~/anaconda3_420/lib/python3.5/importlib/_bootstrap.py in find_and_load(name, import)

~/anaconda3_420/lib/python3.5/importlib/_bootstrap.py in find_and_load_unlocked(name, import)

~/anaconda3_420/lib/python3.5/importlib/_bootstrap.py in _load_unlocked(spec)

~/anaconda3_420/lib/python3.5/importlib/_bootstrap_external.py in exec_module(self, module)

~/anaconda3_420/lib/python3.5/importlib/_bootstrap.py in _call_with_frames_removed(f, *args, **kwds)

~/anaconda3_420/lib/python3.5/site-packages/Kqlmagic/init.py in ()
5 # --------------------------------------------------------------------------
6
----> 7 from .magic_extension import *
8 from .version import VERSION
9

~/anaconda3_420/lib/python3.5/site-packages/Kqlmagic/magic_extension.py in ()
6
7 from Kqlmagic.constants import Constants
----> 8 from Kqlmagic.kql_magic import Kqlmagic as Magic
9
10

~/anaconda3_420/lib/python3.5/site-packages/Kqlmagic/kql_magic.py in ()
28 from azure.kusto.data.exceptions import KustoError
29
---> 30 from Kqlmagic.results import ResultSet
31 from Kqlmagic.parser import Parser
32 from Kqlmagic.parameterizer import Parameterizer

~/anaconda3_420/lib/python3.5/site-packages/Kqlmagic/results.py in ()
24 import plotly
25
---> 26 import plotly.plotly as py
27 import plotly.graph_objs as go
28

~/anaconda3_420/lib/python3.5/site-packages/plotly/plotly/init.py in ()
2 from plotly_future import _chart_studio_error
3
----> 4 _chart_studio_error("plotly")

~/anaconda3_420/lib/python3.5/site-packages/plotly_future/init.py in _chart_studio_error(submodule)
47 chart_studio.{submodule} module instead.
48 """.format(
---> 49 submodule=submodule
50 )
51 )

ImportError:
The plotly.plotly module is deprecated,
please install the chart-studio package and use the
chart_studio.plotly module instead.
`

should document the options for KQLMAGIC_NOTEBOOK_APP and what it means

The quickstart says KQLMAGIC_NOTEBOOK_APP - set the notebook application (default jupyternotebook).

It would be helpful to document:

  1. What are the allowed options for this setting? (jupyternotebook, visualstudiocode, ipython, jupyterlab, ...?)
  2. What is the meaning of this setting? How should I know which value to choose? What does it do? (You should set this to match the application user interface in which you are running Jupyter? Otherwise the authentication step may fail?)

azcli_login AuthenticationError

The azcli_login function doesn't seem to be working--when I run this:

%kql azureDataExplorer://code;cluster='mycluster';database='MyDatabase' -try_azcli_login

I get this error message, which doesn't help me figure out the problem:

AuthenticationError: AuthenticationError('azcli_login', 'TypeError('catching classes that do not inherit from BaseException is not allowed')', '{'authority': 'azcli_login', 'authentication_method': 'azcli_login', 'resource': 'https://mycluster.kusto.windows.net'}')

I'd really like to get azcli authentication working so I don't have to do a multi-step device code auth process every time I re-run my notebook. Any help would be much appreciated.

Help menu injections prevents automatic reloading

This function

def _add_kql_ref_to_help(self, **options):
prevents kqlmagic from being automatically loaded into the notebooks via ipython_config.py (add code to execute %reload_ext Kqlmagic) in Jupyter. Oddly enough it works fine in the ipython cli interface, but the kernel is started from inside of Jupyter Labs it throws and error because Help_html.add_menu_item is not defined. I'd suggest wrapping the code inside of some conditional or removing it entirely (which is what I did).

MFA doesn't work in VS code's Jupyter extension

Hi,

I'm using KqlMaggic version 0.1.113. I can successfully do MFA in Jupyter notebook running in a browser. But when try it in VS code's Jupyter extension, it doesn't work.

The "Copy code to clipboard and authenticate" button appears, after clicking it the code was copied into clipboard but no page was opened for authentication. Not sure if it's an issue of KqlMargic or the Jupyter extension.

Thanks,
Feibo

No documentation on querying without magic %%kql

A magic %%kql cell runs a multiline query and puts the result into _kql_raw_result_. How does one perform the same query without using a magic command? There's no documentation of any of Kqlmagic's classes.

`assign_var` and `last_raw_result_var` options do not assign result to specified user variables as defined in specs

I need to assign results of the query to a variable within a method because it's a multi-threaded call and I cannot use the global kql_raw_result variable.

Example query:
%kql -query queryStr -assign_var "res" -enable_suppress_result ;

I believe that's the intended use of the -assign_var option. When used like so, the res variable gets defined within the python method but nevertheless is always an empty string. So is the _ variable. The _kql_raw_result is still initialized with the query result.

The same is true for the last_raw_result_var option.

Button doesn't copy new connection code

When attempting to connect to a resource, the Jupyter Notebook shows an output cell with a code and a button to connect

immagine

If I attempt to connect to a new source like shown in the picture, the button doesn't copy the correct code but leaves the old one in the clipboard.

immagine

Kqlmagic bug report #475d7641-83a9-4804-8ed4-8368fa5a7e10

Please copy the bug report from the notebook and paste it to the template below

BEFORE YOU SUBMIT IT, MAKE SURE THAT ANY SENSITIVE INFORMATION IS OBFUSCATED

Installed KqlmagicCustom with [jupyter-basic] extra.
Environment variable KQLMAGIC_EXTRAS_REQUIRES, set to "jupyter-basic"
Expected no warnings about missing imports but got these:

Please wait. Loading Kqlmagic extension...

ImportWarning: failed to import 'pyperclip' from 'pyperclip', copy/paste feature will be disabled in device code authentication
ImportWarning: failed to import 'plotly' from 'plotly', won't display charts with plotly
ImportWarning: failed to import 'plotly.graph_objs' from 'plotly', won't display charts with plotly
MissingEnvironmentVariable: KQLMAGIC_AZUREML_COMPUTE is not set. Popup windows might not work properly (copy/paste compute host value from the address bar of the notebook when activated in azureml Jupyter mode)
{
    ๏ฟฝ[94m"Kqlmagic_last_execution"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
    ๏ฟฝ[94m"kqlmagic"๏ฟฝ[39;49;00m: {
        ๏ฟฝ[94m"version"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"0.1.114.dev25"๏ฟฝ[39;49;00m
    },
    ๏ฟฝ[94m"kqlmagic_connections:"๏ฟฝ[39;49;00m: {},
    ๏ฟฝ[94m"kqlmagic_default_env"๏ฟฝ[39;49;00m: {
        ๏ฟฝ[94m"KQLMAGIC_CONFIGURATION"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"try_azcli_login=True"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"KQLMAGIC_EXTRAS_REQUIRES"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"jupyter-basic"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"KQLMAGIC_LOAD_MODE"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"silent"๏ฟฝ[39;49;00m
    },
    ๏ฟฝ[94m"kqlmagic_default_options"๏ฟฝ[39;49;00m: {
        ๏ฟฝ[94m"add_kql_ref_to_help"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"add_schema_to_help"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"allow_single_line_cell"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"assign_var"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"auth_token_warnings"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"auto_dataframe"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"auto_limit"๏ฟฝ[39;49;00m: ๏ฟฝ[34m0๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"auto_popup_schema"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"cache"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"cache_folder_name"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"cache_files"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"check_magic_version"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"cloud"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"public"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"columns_to_local_vars"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"cursor_var"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"debug"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"device_code_login_notification"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"auto"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"device_code_notification_email"๏ฟฝ[39;49;00m: ๏ฟฝ[33m""๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"display_limit"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"dsn_filename"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"odbc.ini"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"dynamic_to_dataframe"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"object"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"enable_curly_brackets_params"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"enable_sso"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"enable_suppress_result"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"export_folder_name"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"exported_files"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"extras_require"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"default"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"feedback"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"is_magic"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"json_display"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"formatted"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"kernel_id"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"51390d22-5634-4d63-910c-5166edf25d91"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"kernel_location"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"remote"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"kqlmagic_kernel"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"last_raw_result_var"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"_kql_raw_result_"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"notebook_app"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"azuremljupyterlab"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"notebook_service_address"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"palette_colors"๏ฟฝ[39;49;00m: ๏ฟฝ[34m10๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"palette_desaturation"๏ฟฝ[39;49;00m: ๏ฟฝ[34m1.0๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"palette_name"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"tab10"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"popup_interaction"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"auto"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"prettytable_style"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"DEFAULT"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"query_link_destination"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"Kusto.WebExplorer"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"request_app_tag"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"request_cache_max_age"๏ฟฝ[39;49;00m: ๏ฟฝ[34m0๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"request_id_tag"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"request_user_tag"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"schema_json_display"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"auto"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"short_errors"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"show_conn_info"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"current"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"show_init_banner"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"show_query"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"show_query_link"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"show_query_time"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"show_what_new"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"sso_db_gc_interval"๏ฟฝ[39;49;00m: ๏ฟฝ[34m168๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"table_package"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"prettytable"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"temp_files_server"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"auto"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"temp_files_server_address"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"temp_folder_location"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"starting_dir"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"temp_folder_name"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"temp_files"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"test_notebook_app"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"none"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"timeout"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"try_azcli_login"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"try_azcli_login_subscription"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"try_kqlmaic_sso"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"try_msi"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"try_token"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"try_vscode_login"๏ฟฝ[39;49;00m: ๏ฟฝ[34mfalse๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"use_cache"๏ฟฝ[39;49;00m: ๏ฟฝ[34mnull๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"validate_connection_string"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"warn_missing_dependencies"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"warn_missing_env_variables"๏ฟฝ[39;49;00m: ๏ฟฝ[34mtrue๏ฟฝ[39;49;00m
    },
    ๏ฟฝ[94m"packages"๏ฟฝ[39;49;00m: {
        ๏ฟฝ[94m"azure-common"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"1.1.26"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"azure-identity"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"1.5.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"beautifulsoup4"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"4.9.3"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"cryptography"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"3.2"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"flask"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"1.1.2"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"ipykernel"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"5.5.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"ipython"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"7.16.1"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"ipywidgets"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"7.6.3"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"isodate"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"0.6.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"lxml"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"4.6.3"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"markdown"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"3.3.4"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"matplotlib"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"3.2.1"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"msal"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"1.9.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"msrestazure"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"0.6.4"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"pandas"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"0.25.3"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"prettytable"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"2.1.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"psutil"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"5.8.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"pygments"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"2.8.1"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"python-dateutil"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"2.8.1"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"requests"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"2.25.1"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"setuptools"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"50.3.0"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"traitlets"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"4.3.3"๏ฟฝ[39;49;00m
    },
    ๏ฟฝ[94m"platform"๏ฟฝ[39;49;00m: {
        ๏ฟฝ[94m"release"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"5.4.0-1040-azure"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"system"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"Linux"๏ฟฝ[39;49;00m
    },
    ๏ฟฝ[94m"python"๏ฟฝ[39;49;00m: {
        ๏ฟฝ[94m"name"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"CPython"๏ฟฝ[39;49;00m,
        ๏ฟฝ[94m"version"๏ฟฝ[39;49;00m: ๏ฟฝ[33m"3.6.9"๏ฟฝ[39;49;00m
    }
}

`raw_json` is a misnomer given it's a special formatted object, not actual raw JSON

If a user wants to inspect and work with the raw JSON response while debugging, _kql_raw_result_.raw_json does not provide a a raw JSON string.

Instead, it's a FormattedJsonDict object.

Perhaps _kql_raw_result_.raw_json should be the actual raw JSON? And a new attribute _kql_raw_result_.formatted_json would then be a better name for what is actually given.

E.g. I was attempting to understand if Azure log analytics response limits return something in the up to ~61MB JSON response, and it's not practical to view _kql_raw_result_.raw_json at this size, and it breaks the option to use something such as jmespath.

This can't be done.

import json

test = json.loads(_kql_raw_result_.raw_json)

Nor can this:

import jmespath

jmespath.search(
    expression='error',
    data=_kql_raw_result_.raw_json
)

As per:

print([a for a in dir(_kql_raw_result_) if 'json' in a.lower()])

There are two JSON related attributes, but neither are proper JSON that can be debugged as JSON using standard python JSON libs. They're all already preprocessed.

['_json_response', 'raw_json']

Even _json_response is poorly named given it's actually a python dict, not a JSON string. If it was name _response, it would not invoke the assumption that the type is a JSON string.

Feature Request: Being able to override "Application" metadata

Currently, all queries run by Kqlmagic appear as having an "Application" of "Kqlmagic" when we see the KQL queries that it has executed by using ".show queries". We would like to request the ability to override this "Application" metadata property with a custom value. This would be set for Kqlmagic globally, not per-query.

Context:
We have a service that runs Jupyter notebooks automatically using an AAD app for authentication. Because of this, all KQL queries made by the Jupyter notebooks appear with the same App ID and Application, which makes it difficult for us to identify the origin of expensive queries that might affect the cluster health. We would like to use the Application field to include the ID of the job that is running the notebook. This way, it will be very straightforward for us to find jobs that are using too many resources.

Kqlmagic not installed in Azure Notebooks by default

The tutorial "Use a Jupyter Notebook and Kqlmagic extension to analyze data in Azure Data Explorer" states that it's not necessary to install Kqlmagic on Azure Notebooks.

However, when I try to run %reload_ext Kqlmagic from a fresh Azure Notebook, I get an error: ImportError: No module named 'Kqlmagic'

the service loganalytics is not supported in cloud None

I try to configure Kqlmagic using environment variables
I have setup the connectionstring and checked if they work.
While running %reload_ext Kqlmagic the error "the service loganalytics is not supported in cloud None" is returned.

Unable to connect to Log Analytics using username/password

So, according to the docs and example one should be able to connect using the username/password like this:

%kql loganalytics://username='<username>';password='<password>';workspace='<workspace-id>';alias='<workspace-friendly-name>'

But as soon as I try that I get the following error:

invalid connection string: loganalytics://username='xxx';password='yyy';workspace='zzz';alias='www', missing keys.

What is up with that?

"popup schema" button doesn't work when path has spaces

When running a notebook that is located in a file path that has spaces in it, the temporary html files that are generated with the table schema have the spaces replaced with underscores. However, when clicking on the "popup schema" button, the URL that is generated doesn't have the replacement of spaces to underscores, so this causes a 404 page to be displayed.

Kqlmagic bug report #0e964ab0-267f-4084-b0f2-d4062de292ed

When I run a KQL query from Jupyter Lab that takes longer than a couple of minutes, I started getting the following error. This was not an issue and I could run long queries without any problems until last week. The details of the bug report can be found below.

ChunkedEncodingError: ("Connection broken: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)", ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))

{
    "Kqlmagic_last_execution": {
        "args": {
            "cell": "-params_dict site_dict\nlet signal1 = 'site_instant_power';\nlet signal2 = 'battery_instant_power';\nlet signal3  = 'battery_instant_reactive_power';\nlet site = site_name;\nlet Signal1 = RawTelemetry_dd | where assetId == site and signal_name == signal1\n  | project timestamp , signal1_value = value;\nlet Signal2 = RawTelemetry_dd | where assetId == site and signal_name == signal2\n  | project timestamp, signal2_value = value;\nlet Signal3 = RawTelemetry_dd | where assetId == site and signal_name == signal3\n  | project timestamp, signal3_value = value;\nSignal1 | join kind= inner (Signal2) on timestamp | join kind = inner (Signal3) on timestamp\n| where signal1_value > 0\n| extend battery_va = sqrt(pow(signal2_value,2) + pow(signal3_value,2))\n| sort by battery_va desc\n| project timestamp, battery_va, signal1_value, signal2_value, signal3_value\n| take 10000;\n",
            "line": "",
            "override_connection": null,
            "override_options": null,
            "override_query_properties": null,
            "override_result_set": null,
            "override_vars": null
        },
        "connection": "canvasvppdb@canvascluster_australiaeast",
        "error": "('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))",
        "log": [
            "Kqlmagic_core::execute - input: \n\rline: \n\rcell:\n\r-params_dict site_dict\nlet signal1 = 'site_instant_power';\nlet signal2 = 'battery_instant_power';\nlet signal3  = 'battery_instant_reactive_power';\nlet site = site_name;\nlet Signal1 = RawTelemetry_dd | where assetId == site and signal_name == signal1\n  | project timestamp , signal1_value = value;\nlet Signal2 = RawTelemetry_dd | where assetId == site and signal_name == signal2\n  | project timestamp, signal2_value = value;\nlet Signal3 = RawTelemetry_dd | where assetId == site and signal_name == signal3\n  | project timestamp, signal3_value = value;\nSignal1 | join kind= inner (Signal2) on timestamp | join kind = inner (Signal3) on timestamp\n| where signal1_value > 0\n| extend battery_va = sqrt(pow(signal2_value,2) + pow(signal3_value,2))\n| sort by battery_va desc\n| project timestamp, battery_va, signal1_value, signal2_value, signal3_value\n| take 10000;\n",
            "Kqlmagic_core::execute - parsed_queries: [{'connection_string': '', 'query': \"let signal1 = 'site_instant_power';\\nlet signal2 = 'battery_instant_power';\\nlet signal3  = 'battery_instant_reactive_power';\\nlet site = site_name;\\nlet Signal1 = RawTelemetry_dd | where assetId == site and signal_name == signal1\\n  | project timestamp , signal1_value = value;\\nlet Signal2 = RawTelemetry_dd | where assetId == site and signal_name == signal2\\n  | project timestamp, signal2_value = value;\\nlet Signal3 = RawTelemetry_dd | where assetId == site and signal_name == signal3\\n  | project timestamp, signal3_value = value;\\nSignal1 | join kind= inner (Signal2) on timestamp | join kind = inner (Signal3) on timestamp\\n| where signal1_value > 0\\n| extend battery_va = sqrt(pow(signal2_value,2) + pow(signal3_value,2))\\n| sort by battery_va desc\\n| project timestamp, battery_va, signal1_value, signal2_value, signal3_value\\n| take 10000;\", 'options': {'auto_dataframe': False, 'short_errors': True, 'feedback': True, 'show_conn_info': 'current', 'columns_to_local_vars': False, 'show_query_time': True, 'show_query': False, 'show_query_link': False, 'query_link_destination': 'Kusto.WebExplorer', 'enable_suppress_result': True, 'plotly_fs_includejs': False, 'popup_window': False, 'auto_limit': 0, 'display_limit': None, 'timeout': None, 'prettytable_style': 'DEFAULT', 'last_raw_result_var': '_kql_raw_result_', 'table_package': 'prettytable', 'plot_package': 'plotly', 'dsn_filename': 'odbc.ini', 'validate_connection_string': True, 'auto_popup_schema': True, 'json_display': 'formatted', 'schema_json_display': 'auto', 'palette_desaturation': 1.0, 'params_dict': {'site_name': 'TeslaGridLogicb11bcafc-5ebd-4370-bc80-b1c9cfacb83a'}, 'palette_name': 'tab10', 'cache': None, 'use_cache': None, 'temp_folder_name': 'temp_files', 'cache_folder_name': 'cache_files', 'export_folder_name': 'exported_files', 'add_kql_ref_to_help': True, 'add_schema_to_help': True, 'notebook_app': 'jupyternotebook', 'debug': False, 'check_magic_version': True, 'show_what_new': True, 'show_init_banner': True, 'warn_missing_dependencies': True, 'warn_missing_env_variables': True, 'allow_single_line_cell': True, 'kqlmagic_kernel': False, 'extras_require': 'default', 'test_notebook_app': 'none', 'cloud': 'public', 'enable_sso': False, 'sso_db_gc_interval': 168, 'try_azcli_login': False, 'try_vscode_login': False, 'try_azcli_login_subscription': None, 'try_token': None, 'try_msi': None, 'request_id_tag': None, 'request_app_tag': None, 'request_user_tag': None, 'request_cache_max_age': 0, 'device_code_login_notification': 'auto', 'device_code_notification_email': '', 'save_as': None, 'save_to': None, 'query_properties': {}, 'palette_colors': 10, 'palette_reverse': False, 'popup_schema': False, 'display_id': False, 'display_handlers': {'acquire_token': None}, 'popup_interaction': 'auto', 'temp_files_server': 'auto', 'temp_files_server_address': None, 'kernel_location': 'auto', 'kernel_id': 'e51ec051-cbaa-4dbe-ae31-9502e0d9833e', 'notebook_service_address': None, 'dynamic_to_dataframe': 'object', 'temp_folder_location': 'starting_dir', 'plotly_layout': None, 'auth_token_warnings': False, 'enable_curly_brackets_params': False, 'nop': False, 'assign_var': None, 'cursor_var': None, 'is_magic': True}, 'command': {}, 'last_query': True}]",
            "Account(s) exists in cache, probably with token too. Let's try.",
            "Account(s) exists in cache, probably with token too. Let's try.",
            "KustoClient::execute - POST request - url: https://canvascluster.australiaeast.kusto.windows.net/v2/rest/query, headers: {'Accept': 'application/json', 'Accept-Encoding': 'gzip,deflate', 'Content-Type': 'application/json; charset=utf-8', 'x-ms-client-version': 'Kqlmagic.Python.Client:0.1.114.dev25', 'x-ms-client-request-id': 'Kqlmagic.execute;8820effb-7a9a-4fdc-86d1-68c350c34716/05f373ad-0e2f-4cb3-af8e-a74f7456ecaf/AzureDataExplorer', 'x-ms-app': 'Kqlmagic;jupyternotebook', 'Authorization': '...', 'Fed': 'True', 'Cache-Control': 'no-cache'}, payload: {'db': 'canvasvppdb', 'csl': \"let site_name = 'TeslaGridLogicb11bcafc-5ebd-4370-bc80-b1c9cfacb83a';let signal1 = 'site_instant_power';\\nlet signal2 = 'battery_instant_power';\\nlet signal3  = 'battery_instant_reactive_power';\\nlet site = site_name;\\nlet Signal1 = RawTelemetry_dd | where assetId == site and signal_name == signal1\\n  | project timestamp , signal1_value = value;\\nlet Signal2 = RawTelemetry_dd | where assetId == site and signal_name == signal2\\n  | project timestamp, signal2_value = value;\\nlet Signal3 = RawTelemetry_dd | where assetId == site and signal_name == signal3\\n  | project timestamp, signal3_value = value;\\nSignal1 | join kind= inner (Signal2) on timestamp | join kind = inner (Signal3) on timestamp\\n| where signal1_value > 0\\n| extend battery_va = sqrt(pow(signal2_value,2) + pow(signal3_value,2))\\n| sort by battery_va desc\\n| project timestamp, battery_va, signal1_value, signal2_value, signal3_value\\n| take 10000;\"}, timeout: None",
            "Kqlmagic_core::execute - failed - command: None param: None"
        ],
        "query": {
            "request": {
                "endpoint": "https://canvascluster.australiaeast.kusto.windows.net/v2/rest/query",
                "headers": {
                    "Accept": "application/json",
                    "Accept-Encoding": "gzip,deflate",
                    "Authorization": "...",
                    "Cache-Control": "no-cache",
                    "Content-Type": "application/json; charset=utf-8",
                    "Fed": "True",
                    "x-ms-app": "Kqlmagic;jupyternotebook",
                    "x-ms-client-request-id": "Kqlmagic.execute;8820effb-7a9a-4fdc-86d1-68c350c34716/05f373ad-0e2f-4cb3-af8e-a74f7456ecaf/AzureDataExplorer",
                    "x-ms-client-version": "Kqlmagic.Python.Client:0.1.114.dev25"
                },
                "payload": {
                    "csl": "let site_name = 'TeslaGridLogicb11bcafc-5ebd-4370-bc80-b1c9cfacb83a';let signal1 = 'site_instant_power';\nlet signal2 = 'battery_instant_power';\nlet signal3  = 'battery_instant_reactive_power';\nlet site = site_name;\nlet Signal1 = RawTelemetry_dd | where assetId == site and signal_name == signal1\n  | project timestamp , signal1_value = value;\nlet Signal2 = RawTelemetry_dd | where assetId == site and signal_name == signal2\n  | project timestamp, signal2_value = value;\nlet Signal3 = RawTelemetry_dd | where assetId == site and signal_name == signal3\n  | project timestamp, signal3_value = value;\nSignal1 | join kind= inner (Signal2) on timestamp | join kind = inner (Signal3) on timestamp\n| where signal1_value > 0\n| extend battery_va = sqrt(pow(signal2_value,2) + pow(signal3_value,2))\n| sort by battery_va desc\n| project timestamp, battery_va, signal1_value, signal2_value, signal3_value\n| take 10000;",
                    "db": "canvasvppdb"
                },
                "timeout": null
            }
        },
        "traceback": [
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 677, in urlopen",
            "    chunked=chunked,",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 426, in _make_request",
            "    six.raise_from(e, None)",
            "  File \"<string>\", line 3, in raise_from",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 421, in _make_request",
            "    httplib_response = conn.getresponse()",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\http\\client.py\", line 1321, in getresponse",
            "    response.begin()",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\http\\client.py\", line 296, in begin",
            "    version, status, reason = self._read_status()",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\http\\client.py\", line 257, in _read_status",
            "    line = str(self.fp.readline(_MAXLINE + 1), \"iso-8859-1\")",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\socket.py\", line 589, in readinto",
            "    return self._sock.recv_into(b)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\ssl.py\", line 1052, in recv_into",
            "    return self.read(nbytes, buffer)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\ssl.py\", line 911, in read",
            "    return self._sslobj.read(len, buffer)",
            "ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host",
            "",
            "During handling of the above exception, another exception occurred:",
            "",
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\requests\\adapters.py\", line 449, in send",
            "    timeout=timeout",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 727, in urlopen",
            "    method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\util\\retry.py\", line 410, in increment",
            "    raise six.reraise(type(error), error, _stacktrace)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\packages\\six.py\", line 734, in reraise",
            "    raise value.with_traceback(tb)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 677, in urlopen",
            "    chunked=chunked,",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 426, in _make_request",
            "    six.raise_from(e, None)",
            "  File \"<string>\", line 3, in raise_from",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\urllib3\\connectionpool.py\", line 421, in _make_request",
            "    httplib_response = conn.getresponse()",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\http\\client.py\", line 1321, in getresponse",
            "    response.begin()",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\http\\client.py\", line 296, in begin",
            "    version, status, reason = self._read_status()",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\http\\client.py\", line 257, in _read_status",
            "    line = str(self.fp.readline(_MAXLINE + 1), \"iso-8859-1\")",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\socket.py\", line 589, in readinto",
            "    return self._sock.recv_into(b)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\ssl.py\", line 1052, in recv_into",
            "    return self.read(nbytes, buffer)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\ssl.py\", line 911, in read",
            "    return self._sslobj.read(len, buffer)",
            "urllib3.exceptions.ProtocolError: ('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))",
            "",
            "During handling of the above exception, another exception occurred:",
            "",
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 1337, in _execute_query",
            "    raise e",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 1327, in _execute_query",
            "    raw_query_result = engine.execute(parametrized_query, user_ns, **options)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_engine.py\", line 221, in execute",
            "    response = self.client_execute(query, user_namespace, database, **options)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_engine.py\", line 216, in client_execute",
            "    return client.execute(database or self.get_client_database_name(), query, accept_partial_results=False, **options)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kusto_client.py\", line 284, in execute",
            "    response = requests.post(endpoint, headers=request_headers, json=request_payload, timeout=options.get(\"timeout\"))",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\requests\\api.py\", line 119, in post",
            "    return request('post', url, data=data, json=json, **kwargs)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\requests\\api.py\", line 61, in request",
            "    return session.request(method=method, url=url, **kwargs)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\requests\\sessions.py\", line 542, in request",
            "    resp = self.send(prep, **send_kwargs)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\requests\\sessions.py\", line 655, in send",
            "    r = adapter.send(request, **kwargs)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\requests\\adapters.py\", line 498, in send",
            "    raise ConnectionError(err, request=request)",
            "requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))",
            "",
            "During handling of the above exception, another exception occurred:",
            "",
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 828, in execute",
            "    result = self._execute_query(parsed, user_ns, result_set=override_result_set, override_vars=override_vars)",
            "  File \"C:\\Users\\baran\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 1464, in _execute_query",
            "    raise ShortError(e, messages)",
            "Kqlmagic.kql_magic_core.ShortError: ('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))"
        ]
    },
    "kqlmagic": {
        "version": "0.1.114.dev25"
    },
    "kqlmagic_connections:": {
        "canvasvppdb@canvascluster_australiaeast": {
            "auth": {
                "aad_login_url": "https://login.microsoftonline.com",
                "authentication_method": "aad_device_login",
                "authority_uri": "https://login.microsoftonline.com/3ff6cfa4-e715-48db-b8e1-0867b9f9fba3",
                "client_app_type": "public",
                "client_id": "db662dc1-0cfe-4e1c-a843-19a68e65be58",
                "scopes": [
                    "['https://canvascluster.australiaeast.kusto.windows.net']/.default"
                ],
                "username": "[email protected]"
            },
            "cluster_name": "canvascluster.australiaeast",
            "database_name": "canvasvppdb",
            "parsed_conn": {
                "cluster": "canvascluster.australiaeast",
                "code": "",
                "database": "canvasvppdb"
            }
        }
    },
    "kqlmagic_default_env": {},
    "kqlmagic_default_options": {
        "add_kql_ref_to_help": true,
        "add_schema_to_help": true,
        "allow_single_line_cell": true,
        "assign_var": null,
        "auth_token_warnings": false,
        "auto_dataframe": false,
        "auto_limit": 0,
        "auto_popup_schema": true,
        "cache": null,
        "cache_folder_name": "cache_files",
        "check_magic_version": true,
        "cloud": "public",
        "columns_to_local_vars": false,
        "cursor_var": null,
        "debug": false,
        "device_code_login_notification": "auto",
        "device_code_notification_email": "",
        "display_limit": null,
        "dsn_filename": "odbc.ini",
        "dynamic_to_dataframe": "object",
        "enable_curly_brackets_params": false,
        "enable_sso": false,
        "enable_suppress_result": true,
        "export_folder_name": "exported_files",
        "extras_require": "default",
        "feedback": true,
        "is_magic": true,
        "json_display": "formatted",
        "kernel_id": "e51ec051-cbaa-4dbe-ae31-9502e0d9833e",
        "kernel_location": "auto",
        "kqlmagic_kernel": false,
        "last_raw_result_var": "_kql_raw_result_",
        "notebook_app": "jupyternotebook",
        "notebook_service_address": null,
        "palette_colors": 10,
        "palette_desaturation": 1.0,
        "palette_name": "tab10",
        "plot_package": "plotly",
        "plotly_config": null,
        "plotly_fs_includejs": false,
        "plotly_layout": null,
        "popup_interaction": "auto",
        "prettytable_style": "DEFAULT",
        "query_link_destination": "Kusto.WebExplorer",
        "request_app_tag": null,
        "request_cache_max_age": 0,
        "request_id_tag": null,
        "request_user_tag": null,
        "schema_json_display": "auto",
        "short_errors": true,
        "show_conn_info": "current",
        "show_init_banner": true,
        "show_query": false,
        "show_query_link": false,
        "show_query_time": true,
        "show_what_new": true,
        "sso_db_gc_interval": 168,
        "table_package": "prettytable",
        "temp_files_server": "auto",
        "temp_files_server_address": null,
        "temp_folder_location": "starting_dir",
        "temp_folder_name": "temp_files",
        "test_notebook_app": "none",
        "timeout": null,
        "try_azcli_login": false,
        "try_azcli_login_subscription": null,
        "try_kqlmaic_sso": false,
        "try_msi": null,
        "try_token": null,
        "try_vscode_login": false,
        "use_cache": null,
        "validate_connection_string": true,
        "warn_missing_dependencies": true,
        "warn_missing_env_variables": true
    },
    "packages": {
        "azure-common": "1.1.26",
        "azure-identity": "1.5.0",
        "beautifulsoup4": "4.9.3",
        "cryptography": "3.2.1",
        "flask": "1.1.2",
        "ipykernel": "5.3.4",
        "ipython": "7.19.0",
        "ipywidgets": "7.5.1",
        "isodate": "0.6.0",
        "lxml": "4.6.2",
        "markdown": "3.3.3",
        "matplotlib": "3.3.2",
        "msal": "1.10.0",
        "msal_extensions": "0.3.0",
        "msrestazure": "0.6.4",
        "pandas": "1.1.3",
        "password_strength": "0.0.3.post2",
        "plotly": "4.14.3",
        "prettytable": "2.0.0",
        "psutil": "5.7.2",
        "pygments": "2.7.2",
        "pyperclip": "1.8.1",
        "python-dateutil": "2.8.1",
        "requests": "2.25.0",
        "setuptools": "50.3.2.post20201201",
        "traitlets": "5.0.5"
    },
    "platform": {
        "release": "10",
        "system": "Windows"
    },
    "python": {
        "name": "CPython",
        "version": "3.7.1"
    }
}

"Timechart" renders points out of order in Plotly

I'm getting points that are out of order:
image

This is the equivalent graph, correctly rendered in Azure Data Explorer:
image

Connection string:

%kql AzureDataExplorer://code;cluster='help';database='Samples'

Query:

%%kql

StormEvents
| summarize event_count=count() by bin(StartTime, 28d)
| render timechart

display_limit is incorrectly ignored when display_limit = 0

%config Kqlmagic.display_limit = 0 should silence all rows, leaving only the Done (time): X records printed. Instead, display_limit is completely ignored, and all records are printed. Please fix this.

Occurs in version 0.1.114.post8 and probably earlier versions.

Kqlmagic bug report #4837d071-e5e7-4dc5-af3a-19d185dc78ba

Please copy the bug report from the notebook and paste it to the template below

BEFORE YOU SUBMIT IT, MAKE SURE THAT ANY SENSITIVE INFORMATION IS OBFUSCATED

Query from certain table of my database failed with following error:
ChunkedEncodingError: ("Connection broken: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)", ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))

{
    "Kqlmagic_last_execution": {
        "args": {
            "cell": "",
            "line": "",
            "override_connection": null,
            "override_options": null,
            "override_query_properties": null,
            "override_result_set": null,
            "override_vars": null
        },
        "connection": "",
        "error": "(\"Connection broken: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)\", ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))",
        "log": [
            "Kqlmagic_core::execute - input: ",
            "Kqlmagic_core::execute - parsed_queries: [{'connection_string': '', 'query': '', 'options': {'auto_dataframe': False, 'short_errors': True, 'feedback': True, 'show_conn_info': 'current', 'columns_to_local_vars': False, 'show_query_time': True, 'show_query': False, 'show_query_link': False, 'query_link_destination': 'Kusto.WebExplorer', 'enable_suppress_result': True, 'plotly_fs_includejs': False, 'popup_window': False, 'auto_limit': 0, 'display_limit': None, 'timeout': 1800, 'prettytable_style': 'DEFAULT', 'last_raw_result_var': '_kql_raw_result_', 'table_package': 'prettytable', 'plot_package': 'plotly', 'dsn_filename': 'odbc.ini', 'validate_connection_string': True, 'auto_popup_schema': True, 'json_display': 'formatted', 'schema_json_display': 'auto', 'palette_desaturation': 1.0, 'params_dict': None, 'palette_name': 'tab10', 'cache': None, 'use_cache': None, 'temp_folder_name': 'temp_files', 'cache_folder_name': 'cache_files', 'export_folder_name': 'exported_files', 'add_kql_ref_to_help': True, 'add_schema_to_help': True, 'notebook_app': 'azuremljupyternotebook', 'debug': False, 'check_magic_version': True, 'show_what_new': True, 'show_init_banner': True, 'warn_missing_dependencies': True, 'warn_missing_env_variables': True, 'allow_single_line_cell': True, 'allow_py_comments_before_cell': True, 'kqlmagic_kernel': False, 'extras_require': 'default', 'test_notebook_app': 'none', 'cloud': 'public', 'enable_sso': False, 'sso_db_gc_interval': 168, 'try_azcli_login': False, 'try_azcli_login_by_profile': False, 'try_vscode_login': False, 'try_azcli_login_subscription': None, 'try_token': None, 'try_msi': None, 'request_id_tag': None, 'request_app_tag': None, 'request_user_tag': None, 'request_cache_max_age': 0, 'device_code_login_notification': 'auto', 'device_code_notification_email': '', 'save_as': None, 'save_to': None, 'query_properties': {}, 'palette_colors': 10, 'palette_reverse': False, 'popup_schema': False, 'display_id': False, 'display_handlers': {'acquire_token': None, 'conn_info': None, 'table_or_chart': None, 'feedback_info': None}, 'popup_interaction': 'auto', 'temp_files_server': 'auto', 'temp_files_server_address': None, 'kernel_location': 'remote', 'kernel_id': 'fe56cb3f-f93a-4e63-99b0-df2467e6c795', 'notebook_service_address': None, 'dynamic_to_dataframe': 'object', 'temp_folder_location': 'starting_dir', 'plotly_layout': None, 'auth_token_warnings': False, 'enable_curly_brackets_params': False, 'nop': False, 'assign_var': None, 'cursor_var': None, 'is_magic': True, 'code_auth_interactive_mode': 'device_code'}, 'command': {}, 'last_query': True}]",
            "Account(s) exists in cache, probably with token too. Let's try.",
            "Account(s) exists in cache, probably with token too. Let's try.",
            "KustoClient::execute - POST request - url: https://KUSTO_DB/v2/rest/query, headers: {'Accept': 'application/json', 'Accept-Encoding': 'gzip,deflate', 'Content-Type': 'application/json; charset=utf-8', 'x-ms-client-version': 'Kqlmagic.Python.Client:0.1.114.post3', 'x-ms-client-request-id': 'Kqlmagic.execute;c1dd8a82-e964-4b43-9316-19feb187c1b4/466169e9-b351-4fb1-9dee-b27c86cd6f18/AzureDataExplorer', 'x-ms-app': 'Kqlmagic;azuremljupyternotebook', 'Authorization': '...', 'Fed': 'True', 'Cache-Control': 'no-cache'}, payload: {'db': 'DB_NAME', 'csl': 'QUERY_SCRIPT'}, timeout: 1800",
            "Kqlmagic_core::execute - failed - command: None param: None"
        ],
        "query": {
            "request": {
                "endpoint": "https://KUSTO_DB/v2/rest/query",
                "headers": {
                    "Accept": "application/json",
                    "Accept-Encoding": "gzip,deflate",
                    "Authorization": "...",
                    "Cache-Control": "no-cache",
                    "Content-Type": "application/json; charset=utf-8",
                    "Fed": "True",
                    "x-ms-app": "Kqlmagic;azuremljupyternotebook",
                    "x-ms-client-request-id": "Kqlmagic.execute;c1dd8a82-e964-4b43-9316-19feb187c1b4/466169e9-b351-4fb1-9dee-b27c86cd6f18/AzureDataExplorer",
                    "x-ms-client-version": "Kqlmagic.Python.Client:0.1.114.post3"
                },
                "timeout": 1800
            }
        },
        "traceback": [
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\urllib3\\response.py\", line 436, in _error_catcher",
            "    yield",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\urllib3\\response.py\", line 763, in read_chunked",
            "    self._update_chunk_length()",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\urllib3\\response.py\", line 693, in _update_chunk_length",
            "    line = self._fp.fp.readline()",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\socket.py\", line 669, in readinto",
            "    return self._sock.recv_into(b)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\ssl.py\", line 1241, in recv_into",
            "    return self.read(nbytes, buffer)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\ssl.py\", line 1099, in read",
            "    return self._sslobj.read(len, buffer)",
            "ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host",
            "",
            "During handling of the above exception, another exception occurred:",
            "",
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\models.py\", line 751, in generate",
            "    for chunk in self.raw.stream(chunk_size, decode_content=True):",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\urllib3\\response.py\", line 571, in stream",
            "    for line in self.read_chunked(amt, decode_content=decode_content):",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\urllib3\\response.py\", line 792, in read_chunked",
            "    self._original_response.close()",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\contextlib.py\", line 131, in __exit__",
            "    self.gen.throw(type, value, traceback)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\urllib3\\response.py\", line 454, in _error_catcher",
            "    raise ProtocolError(\"Connection broken: %r\" % e, e)",
            "urllib3.exceptions.ProtocolError: (\"Connection broken: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)\", ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))",
            "",
            "During handling of the above exception, another exception occurred:",
            "",
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 1349, in _execute_query",
            "    raise e",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 1339, in _execute_query",
            "    raw_query_result = engine.execute(parametrized_query, user_ns, **options)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_engine.py\", line 221, in execute",
            "    response = self.client_execute(query, user_namespace, database, **options)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_engine.py\", line 216, in client_execute",
            "    return client.execute(database or self.get_client_database_name(), query, accept_partial_results=False, **options)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kusto_client.py\", line 284, in execute",
            "    response = requests.post(endpoint, headers=request_headers, json=request_payload, timeout=options.get(\"timeout\"))",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\api.py\", line 119, in post",
            "    return request('post', url, data=data, json=json, **kwargs)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\api.py\", line 61, in request",
            "    return session.request(method=method, url=url, **kwargs)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\sessions.py\", line 530, in request",
            "    resp = self.send(prep, **send_kwargs)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\sessions.py\", line 685, in send",
            "    r.content",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\models.py\", line 829, in content",
            "    self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b''",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\requests\\models.py\", line 754, in generate",
            "    raise ChunkedEncodingError(e)",
            "requests.exceptions.ChunkedEncodingError: (\"Connection broken: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)\", ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))",
            "",
            "During handling of the above exception, another exception occurred:",
            "",
            "Traceback (most recent call last):",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 840, in execute",
            "    result = self._execute_query(parsed, user_ns, result_set=override_result_set, override_vars=override_vars)",
            "  File \"C:\\Users\\USER_NAME_HIDDEN\\Anaconda3\\lib\\site-packages\\Kqlmagic\\kql_magic_core.py\", line 1476, in _execute_query",
            "    raise ShortError(e, messages)",
            "Kqlmagic.kql_magic_core.ShortError: (\"Connection broken: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)\", ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))"
        ]
    },
    "kqlmagic": {
        "version": "0.1.114.post3"
    },
    "kqlmagic_connections:": {
       
    },
    "kqlmagic_default_env": {},
    "kqlmagic_default_options": {
        "add_kql_ref_to_help": true,
        "add_schema_to_help": true,
        "allow_py_comments_before_cell": true,
        "allow_single_line_cell": true,
        "assign_var": null,
        "auth_token_warnings": false,
        "auto_dataframe": false,
        "auto_limit": 0,
        "auto_popup_schema": true,
        "cache": null,
        "cache_folder_name": "cache_files",
        "check_magic_version": true,
        "cloud": "public",
        "code_auth_interactive_mode": "device_code",
        "columns_to_local_vars": false,
        "cursor_var": null,
        "debug": false,
        "device_code_login_notification": "auto",
        "device_code_notification_email": "",
        "display_limit": null,
        "dsn_filename": "odbc.ini",
        "dynamic_to_dataframe": "object",
        "enable_curly_brackets_params": false,
        "enable_sso": false,
        "enable_suppress_result": true,
        "export_folder_name": "exported_files",
        "extras_require": "default",
        "feedback": true,
        "is_magic": true,
        "json_display": "formatted",
        "kernel_id": "fe56cb3f-f93a-4e63-99b0-df2467e6c795",
        "kernel_location": "remote",
        "kqlmagic_kernel": false,
        "last_raw_result_var": "_kql_raw_result_",
        "notebook_app": "azuremljupyternotebook",
        "notebook_service_address": null,
        "palette_colors": 10,
        "palette_desaturation": 1.0,
        "palette_name": "tab10",
        "plot_package": "plotly",
        "plotly_config": null,
        "plotly_fs_includejs": false,
        "plotly_layout": null,
        "popup_interaction": "auto",
        "prettytable_style": "DEFAULT",
        "query_link_destination": "Kusto.WebExplorer",
        "request_app_tag": null,
        "request_cache_max_age": 0,
        "request_id_tag": null,
        "request_user_tag": null,
        "schema_json_display": "auto",
        "short_errors": true,
        "show_conn_info": "current",
        "show_init_banner": true,
        "show_query": false,
        "show_query_link": false,
        "show_query_time": true,
        "show_what_new": true,
        "sso_db_gc_interval": 168,
        "table_package": "prettytable",
        "temp_files_server": "auto",
        "temp_files_server_address": null,
        "temp_folder_location": "starting_dir",
        "temp_folder_name": "temp_files",
        "test_notebook_app": "none",
        "timeout": 1800,
        "try_azcli_login": false,
        "try_azcli_login_by_profile": false,
        "try_azcli_login_subscription": null,
        "try_kqlmaic_sso": false,
        "try_msi": null,
        "try_token": null,
        "try_vscode_login": false,
        "use_cache": null,
        "validate_connection_string": true,
        "warn_missing_dependencies": true,
        "warn_missing_env_variables": true
    },
    "packages": {
        "azure-common": "1.1.26",
        "azure-identity": "1.7.1",
        "beautifulsoup4": "4.9.3",
        "cryptography": "3.1.1",
        "flask": "1.1.2",
        "ipykernel": "5.3.4",
        "ipython": "7.19.0",
        "ipywidgets": "7.5.1",
        "isodate": "0.6.0",
        "lxml": "4.6.1",
        "markdown": "3.3.6",
        "matplotlib": "3.5.0",
        "msal": "1.16.0",
        "msal_extensions": "0.3.0",
        "msrestazure": "0.6.4",
        "pandas": "1.1.3",
        "password_strength": "0.0.3.post2",
        "plotly": "5.4.0",
        "prettytable": "2.4.0",
        "psutil": "5.7.2",
        "pygments": "2.7.2",
        "pyperclip": "1.8.2",
        "python-dateutil": "2.8.1",
        "requests": "2.24.0",
        "setuptools": "50.3.1.post20201107",
        "traitlets": "5.0.5"
    },
    "platform": {
        "release": "10",
        "system": "Windows"
    },
    "python": {
        "name": "CPython",
        "version": "3.8.5"
    }
}

KQL reference help link points to Azure Data Explorer instead of KQL reference

The information that is printed out when kqlmagic is initialized includes:
kql reference: Click on 'Help' tab > and Select 'kql reference' or execute '%kql --help "kql"'

However, selecting kql reference or running %kql --help "kql" leads to the page https://docs.microsoft.com/en-us/azure/data-explorer/?v=20220206113248. This is general Azure Data Explorer documentation with a lot of information that doesn't seem to be directly about KQL.

Would it be more helpful to the Kusto Query Language (KQL) overview page?

KQL Parser Doesn't Handle Kusto Lambdas

It looks like it just gets rid of the lambda expression and doesn't send it to the server.

%%kql
let PrettyDuration = (duration:timespan) {
    //strcat()
    format_timespan(duration, 'm')
};

MonLogin
| where logical_server_name  == ""
| where database_name =~ "" and event == "process_login_finish"
| where originalEventTimestamp  > ago(2d)
| where is_success == true and is_user_error == false
| summarize count=count(), last_ts=max(originalEventTimestamp) by ['state'], error, state_desc, NodeRole, package, lookup_state, AppName
| extend time_since = PrettyDuration(now() - last_ts)
| project-reorder ['count'], *, last_ts

The above query works when I run it with Kusto.Explorer but fails when running with Kqlmagic.

Kqlmagic bug report #fe64277c-889f-4842-904e-7297f1856365

{
"kqlmagic": {
"version": "0.1.113.post2"
},
"platform": {
"system": "Windows",
"release": "10"
},
"packages": {
"traitlets": "4.3.3",
"requests": "2.24.0",
"msal": "1.0.0",
"msal_extensions": "0.1.3",
"pandas": "1.1.0",
"ipython": "7.16.1",
"ipykernel": "5.3.4",
"pygments": "2.6.1",
"pyperclip": "1.8.0",
"azure-common": "1.1.25",
"msrestazure": "0.6.4",
"psutil": "5.7.2",
"prettytable": "0.7.2",
"tabulate": "0.8.7",
"matplotlib": "3.3.0",
"setuptools": "46.1.3",
"plotly": "4.9.0",
"flask": "1.1.2",
"isodate": "0.6.0",
"python-dateutil": "2.8.1",
"markdown": "3.2.2",
"beautifulsoup4": "4.9.1",
"lxml": "4.5.2",
"ipywidgets": "7.5.1",
"cryptography": "3.0",
"password_strength": "0.0.3.post2"
},
"python": {
"name": "CPython",
"version": "3.6.10"
},
"kqlmagic_default_options": {
"add_kql_ref_to_help": true,
"add_schema_to_help": true,
"auth_token_warnings": false,
"auto_dataframe": false,
"auto_limit": 0,
"auto_popup_schema": true,
"cache": null,
"cache_folder_name": "cache_files",
"check_magic_version": true,
"cloud": "public",
"columns_to_local_vars": false,
"device_code_login_notification": "auto",
"device_code_notification_email": "",
"display_limit": null,
"dsn_filename": "odbc.ini",
"dynamic_to_dataframe": "object",
"enable_curly_brackets_params": false,
"enable_sso": false,
"enable_suppress_result": true,
"export_folder_name": "exported_files",
"feedback": true,
"is_saw_installation": false,
"json_display": "formatted",
"kernel_id": "27a228be-2c2c-41b9-bc5c-d0f0ac7c7a88",
"kernel_location": "local",
"last_raw_result_var": "kql_raw_result",
"not_installed_packages": null,
"notebook_app": "jupyternotebook",
"notebook_service_address": null,
"palette_colors": 10,
"palette_desaturation": 1.0,
"palette_name": "tab10",
"plot_package": "plotly",
"plotly_config": null,
"plotly_fs_includejs": false,
"plotly_layout": null,
"popup_interaction": "auto",
"prettytable_style": "DEFAULT",
"query_link_destination": "Kusto.WebExplorer",
"request_app_tag": null,
"request_id_tag": null,
"request_user_tag": null,
"schema_json_display": "auto",
"short_errors": true,
"show_conn_info": "current",
"show_init_banner": true,
"show_query": false,
"show_query_link": false,
"show_query_time": true,
"show_what_new": true,
"sso_db_gc_interval": 168,
"table_package": "prettytable",
"temp_files_server": "auto",
"temp_files_server_address": null,
"temp_folder_location": "starting_dir",
"temp_folder_name": "temp_files",
"test_notebook_app": "none",
"timeout": null,
"try_azcli_login": false,
"try_azcli_login_subscription": null,
"try_msi": null,
"try_token": null,
"use_cache": null,
"validate_connection_string": true,
"warn_missing_dependencies": true
},
"kqlmagic_connections:": {},
"kqlmagic_default_env": {},
"Kqlmagic_last_execution": {
"args": {
"line": "--usage",
"cell": "",
"override_vars": null,
"override_options": null,
"override_query_properties": null,
"override_connection": null,
"override_result_set": null
}
}
}

Displayed KQL error messages are difficult to read and act upon

When a query has a syntax error, the output message is very difficult to read because of all the "\n" literals and the fact that it's a flat JSON text. I wish that Kqlmagic made it easier to understand these errors. For example, Kqlmagic could parse the JSON and print the different JSON properties separately, using the correct newline replacements and so on.

image

Feature Request: Error feedback for incorrect queries

Currently when I submit an incorrect query to Kusto through kqlmagic, I get a bad request error back. Although this is technically correct, it would be very helpful to get the same error messages I get in Azure Data Explorer when I submit a bad request.
2022-06-15 15_43_34-โ— TestRedline ipynb - JupyterTests - Visual Studio Code

Exception: "format() argument 2 must be str, not int"

Root cause: Kqlmagic fails to parse timespan columns with milliseconds due to typo in string formatting of ticks variable in timedelta_to_timespan() inside my_utils.py

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-b1a86ae756c8> in <module>
----> 1 get_ipython().run_cell_magic('kql', '', "cluster('****').database('****').****\n| take 1\n")

/opt/conda/lib/python3.8/site-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
   2389             with self.builtin_trap:
   2390                 args = (magic_arg_s, cell)
-> 2391                 result = fn(*args, **kwargs)
   2392             return result
   2393 

<decorator-gen-118> in execute(self, line, cell, local_ns, override_vars, override_options, override_query_properties, override_connection, override_result_set)

/opt/conda/lib/python3.8/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

<decorator-gen-117> in execute(self, line, cell, local_ns, override_vars, override_options, override_query_properties, override_connection, override_result_set)

/opt/conda/lib/python3.8/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

/opt/conda/lib/python3.8/site-packages/Kqlmagic/kql_magic.py in execute(self, line, cell, local_ns, override_vars, override_options, override_query_properties, override_connection, override_result_set)
    663         #
    664 
--> 665         result = kql_core_obj.execute(
    666             line=line,
    667             cell=cell,

/opt/conda/lib/python3.8/site-packages/Kqlmagic/kql_magic_core.py in execute(self, line, cell, local_ns, override_vars, override_options, override_query_properties, override_connection, override_result_set)
    683                         # can't just return result as is, it fails when used with table package pandas_show_schema
    684                         # it will first show the result, and will suppres show by result._repr_html
--> 685                         result.show_result(suppress_next_repr_html_=True)
    686                 else:
    687                     param = parsed["command"].get("param")

/opt/conda/lib/python3.8/site-packages/Kqlmagic/results.py in show_result(self, suppress_next_repr_html_)
    498                 self.show_chart(**self.options, display_handler_name='table_or_chart')
    499             else:
--> 500                 self.show_table(**self.options, display_handler_name='table_or_chart')
    501 
    502             Display.showInfoMessage(feedback_info, display_handler_name='feedback_info', **self.options)

/opt/conda/lib/python3.8/site-packages/Kqlmagic/results.py in show_table(self, display_handler_name, **kwargs)
    585         pandas_display_html_table_schema = None
    586         pandas__repr_data_resource_patched = False
--> 587         if not options.get("popup_window") and len(self) == 1 and len(self[0]) == 1  and (isinstance(self[0][0], dict) or isinstance(self[0][0], list)):
    588             content = Display.to_json_styled_class(self[0][0], options=options)
    589         else:

/opt/conda/lib/python3.8/site-packages/Kqlmagic/results.py in __getitem__(self, key)
    732                 return item
    733 
--> 734         return Display.to_json_styled_class(item, options=self.options)
    735 
    736 

/opt/conda/lib/python3.8/site-packages/Kqlmagic/display.py in to_json_styled_class(item, style, options)
    346         if json_display_style != "raw" and (isinstance(item, list) or isinstance(item, dict) or type(item).__name__ == "KqlRow"):
    347             if json_display_style == "formatted":
--> 348                 return _getitem_FormattedJson(item)
    349             else:
    350                 return _getitem_JSON(item)

/opt/conda/lib/python3.8/site-packages/Kqlmagic/display.py in _getitem_FormattedJson(item, key)
     89         return FormattedJsonList(item, key)
     90     elif isinstance(item, dict) or type(item).__name__ == "KqlRow":
---> 91         return FormattedJsonDict(item)
     92     else:
     93         return item

/opt/conda/lib/python3.8/site-packages/Kqlmagic/display.py in __init__(self, item, *args, **kwargs)
     39         if len(item) == 1 and isinstance(self.get(" "), list):
     40             _dict = self.get(" ")
---> 41         formatted_json = json_dumps(_dict, indent=4, sort_keys=True)
     42         self.colorful_json = highlight(formatted_json.encode("UTF-8"), JsonLexer(), TerminalFormatter())
     43 

/opt/conda/lib/python3.8/site-packages/Kqlmagic/my_utils.py in json_dumps(_dict, **kwargs)
    173 
    174 def json_dumps(_dict:dict, **kwargs)->str:
--> 175     return json.dumps(_dict, default=json_defaults, **kwargs)
    176 
    177 

/opt/conda/lib/python3.8/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    232     if cls is None:
    233         cls = JSONEncoder
--> 234     return cls(
    235         skipkeys=skipkeys, ensure_ascii=ensure_ascii,
    236         check_circular=check_circular, allow_nan=allow_nan, indent=indent,

/opt/conda/lib/python3.8/json/encoder.py in encode(self, o)
    199         chunks = self.iterencode(o, _one_shot=True)
    200         if not isinstance(chunks, (list, tuple)):
--> 201             chunks = list(chunks)
    202         return ''.join(chunks)
    203 

/opt/conda/lib/python3.8/json/encoder.py in _iterencode(o, _current_indent_level)
    429             yield from _iterencode_list(o, _current_indent_level)
    430         elif isinstance(o, dict):
--> 431             yield from _iterencode_dict(o, _current_indent_level)
    432         else:
    433             if markers is not None:

/opt/conda/lib/python3.8/json/encoder.py in _iterencode_dict(dct, _current_indent_level)
    403                 else:
    404                     chunks = _iterencode(value, _current_indent_level)
--> 405                 yield from chunks
    406         if newline_indent is not None:
    407             _current_indent_level -= 1

/opt/conda/lib/python3.8/json/encoder.py in _iterencode(o, _current_indent_level)
    436                     raise ValueError("Circular reference detected")
    437                 markers[markerid] = o
--> 438             o = _default(o)
    439             yield from _iterencode(o, _current_indent_level)
    440             if markers is not None:

/opt/conda/lib/python3.8/site-packages/Kqlmagic/my_utils.py in json_defaults(obj)
    162         return obj.isoformat()
    163     elif isinstance(obj, datetime.timedelta):
--> 164         return timedelta_to_timespan(obj, minimal=True)
    165         # return isodate.duration_isoformat(obj)
    166         # return (datetime.datetime.min + obj).time().isoformat()

/opt/conda/lib/python3.8/site-packages/Kqlmagic/my_utils.py in timedelta_to_timespan(_timedelta, minimal)
    196             result = "{0:01}.{1}".format(int(days), result)
    197         if ticks > 0:
--> 198             result = "{0}.{1:07}",format(result, int(ticks))
    199     else:
    200         result = "{0:01}.{1:02}:{2:02}:{3:02}.{4:07}".format(int(days), int(hours), int(minutes), int(seconds), int(ticks))

TypeError: format() argument 2 must be str, not int

Notebook failed with error (AttributeError: module 'plotly.graph_objs' has no attribute 'FigureWidget')

Running "%kql StormEvents | summarize count() by State | sort by count_ | limit 10 | render piechart title='my apple pie'" fails with error. It seems plotly version related issue. Any workaround ?

Kqlmagic Quick Start


AttributeError Traceback (most recent call last)
~/.local/lib/python3.6/site-packages/IPython/core/formatters.py in call(self, obj)
343 method = get_real_method(obj, self.print_method)
344 if method is not None:
--> 345 return method()
346 return None
347 else:

~/miniconda3/envs/kqlmagic/lib/python3.6/site-packages/Kqlmagic/results.py in repr_html(self)
340
341 if self.is_chart():
--> 342 self.show_chart(**self.options)
343 else:
344 self.show_table(**self.options)

~/miniconda3/envs/kqlmagic/lib/python3.6/site-packages/Kqlmagic/results.py in show_chart(self, **kwargs)
460 if window_mode and not options.get("button_text"):
461 options["button_text"] = "popup " + self.visualization + ((" - " + self.title) if self.title else "") + " "
--> 462 c = self._getChartHtml(window_mode)
463 if c.get("body") or c.get("head"):
464 html = Display.toHtml(**c)

~/miniconda3/envs/kqlmagic/lib/python3.6/site-packages/Kqlmagic/results.py in _getChartHtml(self, window_mode)
541 # First column is color-axis, second column is numeric
542 if self.visualization == VisualizationValues.PIE_CHART:
--> 543 figure_or_data = self._render_piechart_plotly(self.visualization_properties, " ")
544 # chart = self._render_pie(self.visualization_properties, " ")
545

~/miniconda3/envs/kqlmagic/lib/python3.6/site-packages/Kqlmagic/results.py in _render_piechart_plotly(self, properties, key_word_sep, **kwargs)
1149 ],
1150 )
-> 1151 fig = go.FigureWidget(data=data, layout=layout)
1152 return fig
1153

AttributeError: module 'plotly.graph_objs' has no attribute 'FigureWidget'

[{'State': 'TEXAS', 'count_': 4701},
{'State': 'KANSAS', 'count_': 3166},
{'State': 'IOWA', 'count_': 2337},
{'State': 'ILLINOIS', 'count_': 2022},
{'State': 'MISSOURI', 'count_': 2016},
{'State': 'GEORGIA', 'count_': 1983},
{'State': 'MINNESOTA', 'count_': 1881},
{'State': 'WISCONSIN', 'count_': 1850},
{'State': 'NEBRASKA', 'count_': 1766},
{'State': 'NEW YORK', 'count_': 1750}]

image

fork_result(1) doesn't change once set

Create a cell after initializing kql magic and run:

%kql x << datatable(foo:int) [1,2,3] | fork (summarize count()) (summarize max(foo))
display(x.fork_result(0))
display(x.fork_result(1))

Actual and Expected Output: count_ as 3 and max_foo as 3

Update the query as below and rerun the cell:

%kql x << datatable(foo:int) [1,2,3] | fork (summarize min(foo)) (summarize sum(foo))
display(x.fork_result(0))
display(x.fork_result(1))

Actual Output: min_foo as 1 and max_foo as 3.
Expected Output: min_foo as 1 and sum_foo as 6.

Parsing fails when trying to connect to database with space in name

Trying to connect to a database that has a space in the name fails. Example:

%kql azuredataexplorer://code;cluster='my-cluster';database='Database WithSpaceInName';Alias='IDontCare'

returns
failed to set database, due to invalid str value 'Database.

Note that connecting to this database works correctly in other applications. If I have some free bandwidth, I'll try to dig into why this happens - but I'd just like to bring it to your attention now.

Stackoverflow tag does not exist

From the documentation:

Have a technical question? Ask on Stack Overflow with tag "Kqlmagic"

I tried but I get this error message

Creating the new tag 'kql-magic' requires at least 1500 reputation. Try something from the existing tags list instead.

For now, I've posted my question with a kql tag instead

Kqlmagic | AAD Device Code Authentication fails in JetBrains DataSpell because ipython_api.display buffers output

Try executing any query using any Azure Data Explorer connection string:

  • "azuredataexplorer://tenant='microsoft.com';cluster='';code;database=''"

First time through:

Observe that the first time through, two fields are written as a prompt:

  • Copy code to clipboard and authenticate

Try to copy the device-code and observe that you cannot copy it. Follow the link behind "Copy code to clipboard and authenticate". Observe that an empty dialog box pops.

Expected:

  • Device code can be copied

  • "Copy code to clipboard and authenticate" pops an operable dialog box.

  • Authentication complete successfully based on interaction with the dialog box.

Subsequent times through:

  • This prompt does not appear unless you quit DataSpell and--when prompted--terminate the Jupyter server and then restart DataSpell:

    Copy code to clipboard and authenticate

    You will then find the prompt in the cell output.

  • Restarting the Kernel or stopping cell execution does not reveal the prompt

Expected:

  • Device code can be copied

  • "Copy code to clipboard and authenticate" pops an operable dialog box.

  • Authentication complete successfully based on interaction with the dialog box.

JetBrains DataSpell Information

DS-221.5591.55, JRE 11.0.14.1+1-b2043.45x64 JetBrains s.r.o., OS Mac OS X(aarch64) v12.3.1, screens 2336.0x1510.0, 6016.0x3384.0; Retina

JetBrains issue: https://youtrack.jetbrains.com/issue/DS-3448/Kqlmagic-AAD-Device-Code-Authentication-fails-because-ipython-ap

Kqlmagic logs "ERROR:root:argument of type 'NoneType' is not iterable" when setting one of these options....

The offending option is Kqlmagic.plotly_config which produces this warning message:

ERROR:root:argument of type 'NoneType' is not iterable

Here are all the options I set in my notebook:

%config Kqlmagic.plotly_layout = {'autosize': True, 'template': 'plotly_dark', 'height': 900, 'yaxis': {'rangemode': 'nonnegative'}}
%config Kqlmagic.plotly_config = {'displaylogo': False}
%config Kqlmagic.enable_curly_brackets_params = False
%config Kqlmagic.enable_suppress_result = True
%config Kqlmagic.json_display = 'formatted'
%config Kqlmagic.schema_json_display = 'formatted'
%config Kqlmagic.show_query = False
%config Kqlmagic.table_package = 'pandas'

The warning message originates in the call to IPython traitlets.config.configurable._load_config at line 200 in the version install on my virtualenv. The warning occurs the first time down this code path.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.