Rich logging output in Jupyter IPython Notebook - jupyter-notebook

I am running Python notebooks on Jupyter Notebook server. I am using Python logging module for logging, currently wired up to log to stdout like any console application, do display logging messages in Jupyter Notebook output.
But the default stdout based logging output feels limited. There is so much more you can do with HTML output over plain text/ANSI output.
Are there any advanced Jupyter Notebook logging handlers and formatters that would understand that the output is HTML and adjust accordingly? E.g. offer richer formatting options with colors and font sizes and interactively explore logging message context parameters like Sentry allows one to do?

Never thought to try this before, but yes you can do this using IPython.display.display and a custom logging.Handler which calls it:
import logging
from IPython.display import display, HTML
class DisplayHandler(logging.Handler):
def emit(self, record):
message = self.format(record)
display(message)
This could be used to display anything the notebook can display, including HTML, Markdown, images???, audio???? (if you want your notebook to read your logs to you).
I combined this with a custom logging.Formatter that outputs an HTML object for passing to display(). It's not pretty but you can take the basic concept and improve it, or combine both classes into a single NotebookHTMLHandler class or something like that:
class HTMLFormatter(logging.Formatter):
level_colors = {
logging.DEBUG: 'lightblue',
logging.INFO: 'dodgerblue',
logging.WARNING: 'goldenrod',
logging.ERROR: 'crimson',
logging.CRITICAL: 'firebrick'
}
def __init__(self):
super().__init__(
'<span style="font-weight: bold; color: green">{asctime}</span> '
'[<span style="font-weight: bold; color: {levelcolor}">{levelname}</span>] '
'{message}',
style='{'
)
def format(self, record):
record.levelcolor = self.level_colors.get(record.levelno, 'black')
return HTML(super().format(record))
Put all together you can use it like this:
log = logging.getLogger()
handler = DisplayHandler()
handler.setFormatter(HTMLFormatter())
log.addHandler(handler)
log.setLevel(logging.DEBUG)
Here's an example of how it looks:
For the HTML version you could tidy this up further, if the HTML grows complicated enough, by combining with an HTML templating engine like Jinja, or add JavaScript/Widgets to make log messages expandable to show more of the log record context, per your idea. A full solution I think would be beyond the scope of this answer.

Assuming you know what type of formatted output you wish to print, you can use the IPython.core.display package.
For example, to print HTML-formatted output you could do something like this:
from IPython.core.display import HTML
HTML('link')
To print Markdown-formatted output you could do:
from IPython.core.display import Markdown
Markdown('# This will be an H1 title')
I'm not sure what exactly you mean by "exploring context parameters", so maybe an example here will clear things.

Related

NoSuchElementException: Using webdriver to get sequences from Uniprot

I am trying to download protein sequences from Uniprot with the following code.
driver = webdriver.Chrome(driver_location)
#get website
driver.get('https://www.uniprot.org/uniprotkb/P19515/entry#sequences')
#stall to load webpage
time.sleep(5)
#scroll webpage
#driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")
#create instance of button and click
button = driver.find_element_by_link_text("Copy sequence")
button.click()
Running the previous block of code returns the following error
NoSuchElementException: Message: no such element: Unable to locate element: {"method":"link text","selector":"Copy sequence"}
Additionally, here is the css layout
enter image description here
I assume the problem is the button is either dynamic or hidden in some way that the webdriver cannot locate the button. I know there is a Uniport API and probably other more efficient ways to download protein sequences but for the sake of learning how can I modify my code and why isn't the button clickable?
Link text only works if the locator has a hyperlink, so in this case it wont work since its a button which copies the the text into the clipboard and doesn't a href tag.
however you can modify your code to
button = driver.find_element(By.CSS_SELECTOR, "button.button.primary.tertiary")
and it will perform the click operation.
link_text only works for a tags, not button tags. But if you use the SeleniumBase framework on GitHub, there's a special TAG:contains("TEXT") selector that you can use to click the button.
Here's a working script: (After installing seleniumbase using pip install seleniumbase, run the script with python or pytest)
from seleniumbase import BaseCase
class RecorderTest(BaseCase):
def test_recording(self):
self.open("https://www.uniprot.org/uniprotkb/P19515/entry#sequences")
self.click('button:contains("Copy sequence")')
self.sleep(3)
if __name__ == "__main__":
from pytest import main
main([__file__])
You can use the SeleniumBase Recorder to generate a script like that from manual browser actions. The last part with if __name__ == "__main__": lets you run the script with python instead of just pytest.

Render docstring with Sphinx in Jupyter

Is there a way to make Jupyter notebooks render docstrings that contain Sphinx markup correctly in a Jupyter notebooks.
At the moment if I have a Foo class with Sphinx documentation and the user tries to get the docstring with
[1] Foo.bar?
They end up with the raw text. E.g.
Calls the :py:meth:`bar` method of the :py:class:`Foo` class.
Is there a way to make Jupyter automatically render the docstring correctly as Sphinx. Or anything that's easier to read than raw rst?
SageMath does this, I believe using its "sphinxify" code: https://github.com/sagemath/sage/blob/develop/src/sage/misc/sphinxify.py. That is not easy to use out of the box — it has various SageMath specific things in it — but maybe you can adapt it.

How to delete a Jupyter Notebook input cell programmatically, using its tag?

In our largest ML modeling pipeline notebook we need to delete a single input (code) cell (containing sensitive information which we cannot pass via other means when automating its execution).
The cell has been created (injected) by papermill.execute_notebook() executed in another notebook (controller) and has been auto-tagged with injected-parameters tag.
The solution (possibly not the only one?) is deleting the cell as soon as it gets executed.
If searching for a tag makes it extra difficult, than let's use solutions for just deleting the previous input cell (programmatically).
What did not work
Hiding the input cell is not good enough, as it would still get saved to the disk (this includes the report_only option in papermill's execute_notebook()). Also "converting" with nbconvert to HTML (which does allow to select cells for removal on the basis of their tags, as in this solution) would still preserve the original notebook with the encoded password inside.
I'm assuming you want to remove the cell cause it contains sensitive information (a password).
My first advice would be not to pass sensitive information in plain text. A (slightly) better and simple option would be to store the password in a environment variable, and read it from the notebook using os.environ.
Here's how to remove the cell:
Note: I wrote this code on the fly and didn't test it, might need small edits.
import nbformat
nb = nbformat.read('/path/to/your/notebook.ipynb')
index = None
# find index for the cell with the injected params
for i, c in enumerate(nb.cells):
cell_tags = c.metadata.get('tags')
if cell_tags:
if 'injected-parameters' in cell_tags:
index = i
# remove cell
if index is not None:
nb.cells.pop(index)
# save modified notebook
nbformat.write(nb, '/path/to/your/notebook.ipynb')
An answer seems to involve nbformat and it already exist on this site, but to a question asked in a such a technical language, that I think it is worth simplifying that question to my plain English version to help / allow others to discover it (I duly upvoted the other answer).
def perform_post_exec_cleanup(output_nb_name, tag_to_del='injected-parameters'):
import json
from traitlets.config import Config
from nbconvert import NotebookExporter
import nbformat
c = Config()
c.TagRemovePreprocessor.enabled=True # to enable the preprocessor
c.TagRemovePreprocessor.remove_cell_tags = [tag_to_del]
c.preprocessors = ['TagRemovePreprocessor'] # previously: c.NotebookExporter.preprocessors
nb_body, resources = NotebookExporter(config=c).from_filename(output_nb_name)
nbformat.write(nbformat.from_dict(json.loads(nb_body)), output_nb_name, 4)
Caveats
It is normally possible to do such notebook conversions / cell stripping in-place in the same notebook in which the stripping code is run. NOT in case of papermill - it will NOT work from within the output notebook, when its code execution is controlled using papermill's execute_notebook() function. It has to be run in the external (controller) notebook, after the function has finished or interrupted its execution. Because the output notebook has been incrementally saved to the disk during the process, if you want to make sure the injected-parameters cell does not get saved permantently, you need to run the above stripping code unconditionally, even if the papermill function failed, so put it in your finally section of try-except-finally.
[ based on: Run preprocessor using nbconvert as a library ]

ipywidget interactive plot in Html presentations ipython notebook

I would like to use the interactive function from ipywidget in an ipython notebook to make a presentation in notebook.
I have stored my data in a pickle file and what I want is to change the parameters interactively so that I can see my plot.
My code is like this.
def spin(model, power):
with open(path_cluster1+'SSHFS/Model'+str(model)+'/'+str(power)+'/spinpython2.pickle','rb') as f:
spin = pickle.load(f)
plt.plot(spin)
plt.title('Power'+str(power*0.1))
interactive(spin, model=(1,4,1), power=(70,101,1))
My collaborators are unfamiliar with python so in principle I would like to make the life easier for them to see my data just by changing a parameter in a html page. Maybe I have to save all the data in a pickle file but the question is if this can work in a html without running python.
Is something like this possible?
Have a look at https://www.nbinteract.com/. To quote from the website "nbinteract is a Python package that provides a command-line tool to generate interactive web pages from Jupyter notebooks."

Rstudio editor snippets

Does Rstudio have a mechanism to configure snippets of code, like Geany for example? For faster writing whole sections of user predefined frequent code.
It is not exactly the same as TAB completion already built in rstudio.
Example by mimicking geany snippets
While snippet definition is like line below:
fun = %cursor% <- function(x, ...)\s{\n\n}\n
the usage is like this:
fun<TAB> (like bash style completion)
# will end up in following insertion:
<- function(x, ...) {
}
so the user can then faster write the code by using her own snippets definition. And user can define ANY snippet of any size for completion by TAB.
It is not the Rstudio extract cmd, nieder Rstudio existing TAB context browser.
Code snippets are available in RStudio version 0.99.
https://support.rstudio.com/hc/en-us/articles/204463668-Code-Snippets
The "Extract Function" feature in RStudio may be what you're looking for. Scroll down to the Extract Function section and accompanying screenshot on this page of rstudio.com's documentation: http://www.rstudio.com/ide/docs/using/source
The text of the section reads, "RStudio can analyze a selection of code from within the source editor and automatically convert it into a re-usable function. Any 'free' variables within the selection (objects that are referenced but not created within the selection) are converted into function arguments."
Also see this screenshot: http://www.rstudio.com/images/screenshots/rstudio-code-transform.png
I do not know of such functionality. However, if you want to quickly want to implement functionality with small changes you could also achieve this using functions.
Ok, your question is now clear to me. To my knowledge, Rstudio currently does not have this kind of functionality. You could, however, post a request on their forum for this feature. They respond quite actively to these kinds of requests, so you could give it a try.

Resources