5

Suppose I have a Python script called my_script.py with a one function called my_func() inside. I import this script into a Jupyter Notebook so I can run my_func() in the cells.

My question is, in the definition of my_func(), how to find which cell number it is called from, so that I can log the cell number? Any API that I could use?

Something like

# my_script.py

def my_func():
    # find which cell this is called from
    cell_number_called_from = some_IPython_history_class_api.get_current_cell()

    # log the cell number and do the rest of my things
3
  • 1
    It's not exactly the same, but there are some instructions to find the identity of the caller here. Commented Jan 8, 2020 at 22:25
  • @AMC Thanks for your tip. It is working! Commented Jan 9, 2020 at 19:13
  • Does this answer your question? How to write a cell magic that knows the input number of the cell? Commented Apr 20, 2020 at 11:21

4 Answers 4

2

The following javascript snippet in your Jupyter Notebook's cell will give you that cell's index:

%%javascript
var output_area = this;
// find my cell element
var cell_element = output_area.element.parents('.cell');
// which cell is it?
var cell_idx = Jupyter.notebook.get_cell_elements().index(cell_element);
console.log(cell_idx)
Sign up to request clarification or add additional context in comments.

Comments

1

Here is my solution. Two limitations of this method are as follows:

  1. It works only on vscode. (This problem can be solved by modifying the code.)
  2. You must save the notebook file before execute.

It loads the current ipynb file, and find index of a cell that has the same source with the current input.

# my_script.py

import json
import IPython


def get_cell_idx():
  shell = IPython.get_ipython()
  In = shell.ev('In')

  depth = 0
  while True:
    try:
      with open(
          IPython.extract_module_locals(depth=depth)[1]['__vsc_ipynb_file__'],
          encoding='utf8') as f:
        nb = json.load(f)
        break
    except ValueError:
      return None
    except KeyError:
      depth += 1

  for idx, cell in enumerate(nb['cells']):
    if ''.join(cell['source']).strip() == In[-1]:
      return idx
# Cell

from my_script import get_cell_idx

get_cell_idx()

Comments

0

The idea of calee's solution is finding all Input cells, and then matching the source of code cell.

I enhance his solution in two aspects:

  1. Works in JupyterLab
  2. Works for duplicated cells (i.e., have same source content)

The idea is matching the cell id in metadata, which is unique for each cell.

You might need to install ipynbname since the script needs to get current notebook path in get_notebook_cells.

pip install ipynbname

Here is the script:

import nbformat
import ipynbname


def get_notebook_cells(notebook_path=None, cell_types=["markdown", "code", "raw"]):
    if not notebook_path:
        notebook_path = ipynbname.path()

    with open(notebook_path, "r", encoding="utf-8") as rf:
        nb = nbformat.read(rf, as_version=4)

    cells = [cell for i, cell in enumerate(nb.cells) if cell["cell_type"] in cell_types]
    return cells


def get_cell_id():
    cell_id = get_ipython().get_parent()["metadata"]["cellId"]
    return cell_id


def get_cell_index():
    cell_id = get_cell_id()
    cells = get_notebook_cells()
    for idx, cell in enumerate(cells):
        if cell["id"] == cell_id:
            return idx

print(get_cell_index())

Here is the example of execution:

get_cell_index

Notes:

  • In my case, I put the get_cell_index() in another file to make it easily re-used by other notebooks.
  • The cell index starts from 0.

Comments

0

By what I explained in this thread about how to use keywords to search and run jupyter cells, you have two options: 1) run cell A once to activate the widget and then enter my_func() as a keyword in the input widget. Then run cell B and find its cell number location as cell_n first element, or 2) place my_func() in cell A, run it once, modify text widget to any keyword you like or leave it empty, run cell B. my_func() cell number will be printed as: current code cell number: int. Specifically, it is the last element in cell_n list (cell_n[-1]).`

Cell A

### trying to make Jupyter ipynb to run cells containing especial keywords
# cell A >> preparing functions and the input widget
# kw is short form of keyword(s)

import numpy as np 
import codecs
import ipynbname  #pip install ipynbname
from IPython.display import Javascript, display
import ipywidgets as widgets
from ipywidgets import Layout, Textarea



ID = '5oG9gNy7myn/k86/+/8cemphdkEsK237SjQltj2GZGU=' # unique ID for finding current code cell number inside ipynb file


def indice_matcher(j01,j02): # if positive difference of j02[n] and j01[n] is less than j01[n] and j01[+1] then they have matching indices
    '''j01 has the bigger range '''
    jj = []
    for j1 in range(len(j01)-1):
        for j2 in range(len(j02)):
            if j02[j2] - j01[j1] < j01[j1+1] - j01[j1] and j02[j2] - j01[j1] > 0: 
                jj.append(j1)
            
    return jj  # returns list indices of matched indices

def kwsearch(x,char):  # x is the big string and char is the small string
    f = 0
    i = []
    f = x.find(char)
    i.append(f) 
    while f != -1:
        f = x[i[-1]+1:].find(char)
        i.append(f+1+i[-1]) 
    
    return i[:-1] # returns all indices of ONE searched character inside x string

def flat(lis):
    ''' # Converts a nested list into a flat list; 
    source : https://www.geeksforgeeks.org/python-convert-a-nested-list-into-a-flat-list/'''
    flatList = []
    # Iterate with outer list
    for element in lis:
        if type(element) is list:
            # Check if type is list than iterate through the sublist
            for item in element:
                flatList.append(item)
        else:
            flatList.append(element)
    return flatList

def kwrun(kw_string):
    kw_string.append(ID)
    N = len(kw_string)
    # nb_fname = ipynbname.name()  # current ipynb file's name
    nb_path = ipynbname.path()  # current ipynb file's path
    f=codecs.open(nb_path, 'r')
    current_ipynb = f.read()    

    kw_indices = [] # indices of all inputted keywords
    for n in range(N): # the loop repeats itself till N keywords are searched
        kw_indices.append(kwsearch(current_ipynb,kw_string[n]))

    cell_indices = kwsearch(current_ipynb,'"cell_type"')   # searching "cell_type" inside ipynb file helps us to find indices for all cells 
    # calculating selected cell number
    cell_n = [] 
    for n in range(N): # the loop repeats till N kewords are searched
        cell_n.append(indice_matcher(cell_indices,kw_indices[n]))  # returns list of cell numbers that should be run ; # cell numbers in this way is equal to cell_indices' number of each index
    cell_n = flat(cell_n)
    print(f'Running Cells: {cell_n[:-1]} \n current code cell number: {cell_n[-1]}')    
    return cell_n


w1 = widgets.Text(description='Keywords') # an elegant way to input keywords
display(w1)

Cell B

# cell B >> search and run
cell_n = kwrun((w1.value).split()) # w1.value is value of inputted words inside cell 1 widget; the keywords should be separated by space
for n in range(len(cell_n)-1): # the -1 in range removes loops to run current code cell number (cell A)
    display(Javascript(f'IPython.notebook.execute_cells([{str(cell_n[n])}])')) #if a name is not found, no cell will be run

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.