簡體   English   中英

在 jupyter/ipython notebook 中將命令行參數傳遞給 argv

[英]Passing command line arguments to argv in jupyter/ipython notebook

我想知道是否可以在 jupyter/ipython 筆記本中使用命令行參數填充sys.argv (或其他一些結構),類似於通過 python 腳本完成的方式。

例如,如果我要按如下方式運行 python 腳本:

python test.py False

然后sys.argv將包含參數False 但是如果我以類似的方式運行 jupyter notebook:

jupyter notebook test.ipynb False

然后命令行參數丟失。 有沒有辦法從筆記本內部訪問這個參數?

環顧四周后,我發現非常麻煩的自定義庫,但我用幾行代碼解決了它,我認為這些代碼非常巧妙。 我使用 nbconvert 最終得到一個 html 報告作為輸出,其中包含筆記本中的所有圖形和降價,但像往常一樣通過最小的 python 包裝器接受命令行參數:

python 文件 test_args.py(它正常使用命令行參數):

import sys,os
IPYNB_FILENAME = 'test_argv.ipynb'
CONFIG_FILENAME = '.config_ipynb'

def main(argv):
    with open(CONFIG_FILENAME,'w') as f:
        f.write(' '.join(argv))
    os.system('jupyter nbconvert --execute {:s} --to html'.format(IPYNB_FILENAME))
    return None

if __name__ == '__main__':
    main(sys.argv)

筆記本包含:

import sys,os,argparse
from IPython.display import HTML
CONFIG_FILE = '.config_ipynb'
if os.path.isfile(CONFIG_FILE):
    with open(CONFIG_FILE) as f:
        sys.argv = f.read().split()
else:
    sys.argv = ['test_args.py', 'input_file', '--int_param', '12']

parser = argparse.ArgumentParser()
parser.add_argument("input_file",help="Input image, directory, or npy.")
parser.add_argument("--int_param", type=int, default=4, help="an optional integer parameter.")
args = parser.parse_args()
p = args.int_param
print(args.input_file,p)

我可以像往常一樣運行帶有解析參數的python筆記本:

python test_args.py my_input_file --int_param 12

我傾向於將帶有 argparse 調用的塊粘貼到 python 包裝器中,以便 python 腳本捕獲命令行錯誤並且 -h 正常工作。

我發現有兩個項目可以滿足您的要求

  • Papermill ,將向您的筆記本添加一個單元格,其中包含您在命令行中傳遞給它的參數。 所以這很簡單,你在第一個單元格中定義你的默認值(應該有parameters標簽)
  • nbparameterise它是一個類似的概念,但你沒有用默認值標記你的單元格,它必須是第一個。

這是討論該問題的好資源: https : //github.com/jupyter/help/issues/218

我認為這個要點可以幫助你: https : //gist.github.com/gbishop/acf40b86a9bca2d571fa

這是一個簡單的參數解析器的嘗試,主要用於鍵=值對,可以在命令行和 IPython 筆記本中使用。 它支持筆記本 URL 中的查詢參數和筆記本的運行命令。

如果您使用 iPython 進行測試,那么將 argparse 轉換為類格式可能是一個像這樣的快速虛擬解決方案。

class Args:
  data = './data/penn'
  model = 'LSTM'
  emsize = 200
  nhid = 200

args=Args()

Github 頁面提供網絡轉換服務。 http://35.192.144.192:8000/arg2cls.html
希望對您的測試有所幫助。 2019 年 1 月 9 日修復了許多錯誤。

將 argparse 模塊轉換為類格式。 需要 Python3。

python3 [arg2cls.py] [argparse_script.py]

然后復制和粘貼類格式以替換 argparse 函數。

#!/usr/bin/env python3
from collections import OrderedDict
import sys
import re
DBG = False

#add_argument(), set_defaults() only available.
ListStartPatt = re.compile(r'\s*\[.*')
ListStartPatt2 = re.compile(r'\).*\[.*') # list out of function scope.
ListPatt = re.compile(r'(\[.*?\])')
GbgPatt = re.compile(r'(.*?)\)[^\)]+') # for float('inf') cmplx.
GbgPatt2 = re.compile(r'(.*?)\).*') # general gbg, ? for non greedy.
LpRegex = re.compile(r'\({1,}\s{0,}')
RpRegex = re.compile(r'\s{0,}\){1,}')
PrRegex = re.compile(r'\((.*)(\))(?!.*\))') # from \( to last \).
CmRegex = re.compile(r'\s{0,},\s{0,}')
StrRegex = re.compile(r'\'(.*?)\'')

# Argument dict : {arg_name : value}
argDct=OrderedDict()

# process 'default=' value.
def default_value(tval, dtype=''):
  # string pattern.
  regres = StrRegex.match(tval) 
  if regres and not re.search('int|float|long|bool|complex', dtype):
    if DBG:
      print('default_value: str patt found')
    tval = regres.group(0)
    return tval

  # typed pattern.
  CommaSeparated = CmRegex.split(tval)[0]
  if DBG:
    print('comma sepearated value:', CommaSeparated)

  if ListStartPatt.match(CommaSeparated) and not ListStartPatt2.match(CommaSeparated):
    lres = ListPatt.search(tval)
    if lres:
      tval = lres.group(1)
    if DBG:
      print('list patt exist tval: ', tval)
  else :
    tval = CmRegex.split(tval)[0]
    if DBG:
      print('no list format tval: ', tval)

  # if default value is not like - int('inf') , remove characters after ')' garbage chars.
  ires = RpRegex.split(tval)[0]
  if not (re.search('int|float|long|bool|complex', ires) and re.search(r'[a-z]+\(',ires)):
    if DBG:
      print('not int("inf") format. Rp removed tval : ', tval)
    tval = re.split(r'\s{0,}\){1,}',tval)[0]
    gbg = GbgPatt2.search(tval)
    if gbg:
      tval = gbg.group(1)  
      if DBG:
        print('garbage exist & removed. tval : ', tval)

  # int('inf') patt.
  else:
    if DBG:
      print('type("inf") value garbaging!')
    gbg = GbgPatt.search(tval)
    if gbg:
      if DBG:
        print('garbage found, extract!')
      tval = gbg.group(1)

  return tval

# Handling add_argument()
def add_argument(arg_line):
  global argDct
  if DBG:
    print('\nin add_argument : **Pre regex: ', arg_line)

  '''    
  argument name
  '''
  # argname = DdRegex.split(arg_line)[1] # Dash or regex for arg name.
  argname = re.search('\'--(.*?)\'', arg_line)
  if not argname:
    argname = re.search('\'-+(.*?)\'', arg_line)

  # dest= keyword handling.
  dest = re.search(r',\s*dest\s*=(.*)', arg_line)
  if dest:
    dval = dest.group(1)
    dval = default_value(dval)
    argname = StrRegex.search(dval)

  # hyphen(-) to underscore(_)
  if argname:
    argname = argname.group(1).replace('-', '_')
  else :
    # naive str argname.
    sres = StrRegex.match(arg_line)
    if sres:
      argname = sres.group(1)
    if not argname:
      return # no argument name 

  '''
  check for syntaxes (type=, default=, required=, action=, help=, choices=)
  '''
  dtype = ''
  dres = re.search(r',\s*type\s*=\s*(.*)', arg_line)
  if dres:
    dtype = dres.group(1)
    dtype = CmRegex.split(dtype)[0]

  dfult = re.search(r',\s*default\s*=\s*(.*)', arg_line)
  rquird = re.search(r',\s*required\s*=\s*(.*)', arg_line)
  action = re.search(r',\s*action\s*=\s*(.*)', arg_line)
  hlp = re.search(r',\s*help\s*=\s*(.*)', arg_line)
  chice = re.search(r',\s*choices\s*=\s*(.*)', arg_line)

  # help message
  hlp_msg = ''
  if hlp:
    thl = hlp.group(1)
    if DBG:
      print('handling help=')
    hlp_msg = default_value(thl)
    if hlp_msg:
      hlp_msg = 'help='+hlp_msg

  # choice message
  choice_msg = ''
  if chice:
    tch = chice.group(1)
    if DBG:
      print('handling choices=')
    choice_msg = default_value(tch)
    if choice_msg:
      choice_msg = 'choices='+choice_msg+' '

  '''
  argument value
  '''
  # tval: argument value.
  tval = ''
  # default exist.
  if dfult:
    tval = dfult.group(1)
    tval = default_value(tval, dtype)
    if DBG:
      print('value determined : ', tval)

  # action or required syntaxes exist.
  elif action or rquird:
    if DBG:
      print('in action/required handling')
    msg_str = ''
    if action:
      tval = action.group(1)
      msg_str = 'action'
    elif rquird:
      tval = rquird.group(1)
      msg_str = 'required'

    tval = default_value(tval)
    tval = ' ** ' + msg_str + ' '+tval+'; '+choice_msg+ hlp_msg

  # no default, action, required.
  else : 
    argDct[argname] = ' ** default not found; '+choice_msg+ hlp_msg

  # value found.
  if tval:
    argDct[argname] = tval

# Handling set_defaults()
def set_defaults(arg_line):
  global argDct
  if DBG:
    print('\nin set_defaults arg_line: ', arg_line)

  # arguments to process.
  tv='' 
  # arguments of set_default()
  SetPatt = re.compile(r'(.+=.+\)?)')
  sres = SetPatt.match(arg_line)
  if sres:
    tv = sres.group(1)
    if DBG:
      print("setPatt res: ", tv)
    tv = re.sub(r'\s+','', tv)
    if DBG:
      print('\nset_default values: ', tv)

  # one arguemnt regex.
  SetArgPatt = re.compile(r',?([^=]+=)[^=,]+,?')
  # handling multiple set_default() arguments. (may have a bug)
  while True:
    tname=''
    tval =''
    tnv=''
    # func closed.
    if re.match(r',*\).*',tv):
      tv=''
      break
    if DBG:
      print('set_default remaining: ', tv)

    nres = SetArgPatt.match(tv)
    if nres:
      tname = nres.group(1)
      if len(tv.split(tname, 1)) > 1:
        tval = tv.split(tname,1)[1]
        tval = default_value(tval)
        tnv=tname+tval
        tname = tname.rsplit('=',1)[0]

      if DBG:
        print('set_default tnam: ', tname)
        print('set_default tval: ', tval)
      if tname:
        argDct[tname] = tval

      # split with processed argument.
      tv = tv.split(tnv)
      if len(tv) > 1:
        tv = tv[1]
      # no more value to process
      else:
        break

    # no arg=value pattern found.
    else:
      break

# Remove empty line & Concatenate line-separated syntax.
def preprocess(fname):
  try :
    with open(fname, 'r', encoding='UTF8') as f:
      txt = f.read()
      t = txt.splitlines(True)
      t = list( filter(None, t) )

      # remove empty line
      t = [x for x in t if not re.match(r'\s{0,}\n',x)]
      # concatenate multiple lined arguments.
      # empl : lines to be deleted from t[].
      empl = []
      for i in range(len(t)-1, 0, -1):
        if not re.search('add_argument|set_defaults', t[i]):
          t[i-1] += t[i]
          t[i-1]=re.sub(r'\n{0,}','',t[i-1])
          t[i-1]=re.sub(r'\s{1,}',' ',t[i-1])
          empl.append(t[i])

      for d in empl:
        t.remove(d)
      for i, line in enumerate(t):
        t[i] = line.replace('\"', '\'').split('parse_args()')[0]
      return t

  except IOError:
    print('IOError : no such file.', fname)
    sys.exit()

def transform(fname):
  # t : list() contains add_argument|set_defaults lines.
  arg_line_list = preprocess(fname)

  for i, arg_line in enumerate(arg_line_list):
    t = PrRegex.search(arg_line)

    if t:
      t = t.group(1) # t: content of add_argument Parentheses.
    else :
      continue # nothing to parse.

    if re.search(r'add_argument\s*\(', arg_line):
      add_argument(t)
    elif re.search(r'set_defaults\s*\(',arg_line):
      set_defaults(t)
    else :
      # Nothing to parse.
      continue

  print('\nclass Args:')
  for i in argDct:
    print(' ',i, '=', argDct[i])
  print()
  print('args=Args()')

def main():
  if len(sys.argv) <2:
    print('Usage : python arg2cls.py [target.py] [target2.py(optional)] ...')
    sys.exit(0)
  sys.argv.pop(0)

  #handling multiple file input.
  for fname in sys.argv:
    transform(fname)

if(__name__ == "__main__"):
  main()

如果目標是運行帶有從命令行傳遞的可配置參數的筆記本,我認為最簡單的方法是使用環境變量,如下所示:

NB_ARGS=some_args jupyter nbconvert --execute --to html --template full some_notebook.ipynb

然后在筆記本中,您可以import os並使用os.environ['NB_ARGS'] 變量值可以是一些包含鍵值對或 json 的文本。

在 Jupyter 單元格的頂部,放置如下一行:

%%python - --option1 value1 --option2 value2 --etc

在你的例子中:

%%python - True

這將像在命令行中使用提供的參數一樣運行您的腳本。

例子:

%%python - --option1 value1 --option2 value2 --etc

import sys

if __name__ == '__main__':
    print(sys.argv)

將輸出:

['-', '--option1', 'value1', '--option2', 'value2', '--etc']

希望能幫助到你。

sys.argv產生一個list ,所以我使用

sys.argv.append('hello')

在 jupyter 筆記本中,它允許我附加額外的成員並假裝我是從命令行傳遞參數。

解決方法是讓 jupyter notebook 從文件中讀取參數。 從命令行修改文件並運行筆記本。

我假設您只想解析筆記本的一些參數,但沒有必要使用命令行。

如果你想解析類似的命令。

python script.py --a A --b B

您可以在筆記本中使用以下代碼:

cmd = '--a A --b B'
args = args = parser.parse_args(cmd)

對於parse_args ,您可以在此處找到更多信息。

您可以在筆記本中使用Jupyter內置的魔術命令%run

在此鏈接中 ,您可以使用:

%run -p [prof_opts] filename.py [args to program]

或類似%run -i script.py False東西%run -i script.py False

或者,如果您正在解析參數%run -i script.py --flag1 False --flag2 True

一個簡單而天真的解決方案是將以下代碼段放在程序的第一行:

import sys
sys.argv = "your expected command line arguments here".split()

執行此命令后,像argparse這樣的包將運行良好。

因此,您只需在 jupyter 實驗室服務器中運行您的腳本,而無需打開終端並輸入您的參數。

我嘗試了上面列出的答案,並提出了不同的解決方案。

我的原始代碼是

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="path to input image")
ap.add_argument("-y", "--yolo", required=True, help="base path to YOLO directory")
ap.add_argument("-c", "--confidence", type=float, default=0.5, help="minimum probability to filter weak detections")
ap.add_argument("-t", "--threshold", type=float, default=0.3, help="threshold when applying non-maxima suppression")
args = vars(ap.parse_args())

我試圖將一個類作為

Class Args():
    image='photo.jpg'
    yolo='yolo-coco'
    confidence=0.5
    threshold=0.3

args=Args()

但進一步的代碼片段產生了錯誤。

所以我在vars(ap.parse_args())之后打印了args ,發現是字典。

所以只需為原始參數創建一個字典:

args={"image":  'photo.jpg', "yolo":  'yolo-coco', "confidence": 0.5,"threshold": 0.3}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM