Как отобразить входной код Python вдоль бокового вывода - PullRequest
0 голосов
/ 05 июня 2018

Мне было интересно, можно ли было перенаправить ввод и вывод скрипта python в файл, чтобы они были чересстрочными.По сути, скажите, что у меня есть следующий код:

x = 1
print("hello, world")
print(x)
x = 2
print(x)

def myfun(Val):
    return(Val+1)
myfun(7)

Я бы хотел, чтобы в выходном файле было отображено следующее:

x = 1
print("hello, world")
"hello, world"
print(x)
1
x = 2
print(x)    
2

def myfun(Val):
    return(Val+1)
myfun(7)
8

Вот что я уже рассмотрел:

  • простое перенаправление файлов python myfile.py > output.log однако это не захватывает ввод

  • Модуль трассировки python -m trace myfile.py однако это также отображает путь слишком далекомного информации и раздувает время выполнения.Я не смог найти никакого очевидного способа ограничить это только кодом верхнего уровня (не каждый модуль и вызов функции)

  • ноутбуки Jupyter - я ценю, что они ясно отображают ввод и вывод, однакоЯ действительно хочу сохранить это для исполняемых скриптов командной строки с базовыми файлами ascii python.

В идеале я надеялся найти какой-нибудь bash wizardry с переадресацией, опцию командной строки python или общий модуль python, который способен это обрабатывать, но пока нетудача :(

РЕДАКТИРОВАТЬ: В качестве дополнительного поясняющего примера, по сути, я пытаюсь воссоздать следующую функциональность из R. Скажем, у нас есть программа R myprog.R как:

myfun <- function(x){
    x + 1
}
y <- myfun(7)

print("hello,world!")
print(y)
y + 1

Затем при запуске из командной строки R CMD BATCH myprog.R создается файл myprog.Rout, который выглядит как

myfun <- function(x){
    x + 1
}
y <- myfun(7)

print("hello,world!")
[1] "hello,world!"

print(y)
[1] 8

y + 1
[1] 9

Ответы [ 4 ]

0 голосов
/ 06 июня 2018

Одним из довольно простых способов было бы просто вставить код в интерактивную оболочку Python, то есть открыть свой файл, скопировать содержимое, запустить python без аргумента файла и вставить содержимое.

Примерfile:

def foo(n):
    if n % 2 == 0:
        return n**2
    else:
        return n**.5

x = 41
print(foo(x))
print(foo(2 * x))

Вывод:

>>> def foo(n):
...     if n % 2 == 0:
...         return n**2
...     else:
...         return n**.5
... 
>>> x = 41
>>> print(foo(x))
6.4031242374328485
>>> print(foo(2 * x))
6724

Это добавляет эти префиксы >>> и ... к строкам кода, но это может на самом деле улучшить читабельность вывода.Кроме того, файлы кода должны иметь отступы (без табуляции), а блоки (например, определения функций) должны заканчиваться лишней пустой строкой, иначе они не будут работать.

0 голосов
/ 05 июня 2018

Предполагая, что входной файл называется a.py, это перенаправляет стандартный вывод eval-вызова, чтобы его можно было перехватить, а затем записать в файл (out.py) вместе с источником.

Как сказал дпаролин, хотя это работает для простых сценариев, как только у вас появятся блоки, вещи распадутся.

import sys
from io import StringIO
import contextlib


filename="a.py"
f= open(filename,"r")
f_out= open("out.py","w+")

SCRIPT_LOCALS={}

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO()
    sys.stdout = stdout
    yield stdout
    sys.stdout = old

def eval_with_return(str_code):
    with stdoutIO() as s:
        exec(str_code,SCRIPT_LOCALS)
        return s.getvalue()


for line in f:
    orig_line=line if line.endswith("\n") else line+"\n"
    sys_out_res=eval_with_return(line)
    f_out.write(orig_line)
    f_out.write(sys_out_res)

f.close()
f_out.close()

Скопирование пришло от здесь .Перенаправление менеджера контекста пришло с здесь .

0 голосов
/ 06 июня 2018

В качестве альтернативы вы можете проанализировать вывод, созданный модулем трассировки, и отрезать куски, которые вам не нравятся.В этом примере я ограничился префиксами и распечатками модуля.Будьте осторожны, так как без надлежащим образом отформатированного (PEP8) кода это также легко отрежет определения функций, однако это может быть функцией, а не ошибкой ;-) При использовании трассировки по умолчанию использование функции не дает.Зависит от того, что именно вам нужно.

Сохраните следующее как trace_rt.py и используйте следующее

python -m trace -t yourfile.py | trace_rt.py

trace_rt.py:

import sys


def cutoff(in_line):
    """Removes trace prefix if present"""
    if "): " in line:
        return line.split("): ")[1]
    elif line.startswith("--- modulename:"):
        return ""
    return line


for line in sys.stdin:
    line = line.strip()
    if line:
        print(cutoff(line))
0 голосов
/ 05 июня 2018

[РЕДАКТИРОВАТЬ] Это очень ограниченный сценарий, к сожалению, он немедленно ломается, если ваш код становится более сложным (означает, что операторы занимают несколько строк), так же просто, как наличие функции.

Для вашего очень простого примераВ случае использования вы можете использовать промежуточный скрипт следующим образом (допустим, имя файла rt.py):

import sys

in_file = sys.argv[1]

with open(in_file) as script:
    for line in script:
        line = line.strip()
        if line:
            # Option to visually separate it by using repr
            # print(repr(line))
            print(line)
            # Another optional use a prefix
            # print(">>>", end="")
            exec(line)

Теперь вы можете передать его существующему сценарию

python rt.py somescript.py

Было бы невозможноулучшите это, чтобы подготовить сценарий на основе отступа, но это, вероятно, не стоит, и я надеюсь, что существуют другие решения, о которых я не знаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...