py2html.html

Процедура py2html() по своему действию противоположна процедуре html2py(). Если html2py() преобразует html-страницы в сценарии Python, превращая html-разметку в комментарии, то py2html() убирает все добавленные комментарии, восстанавливая оформление исходного html-файла.

Процедуры html2py() и py2html() написаны на Python 2. Для их работы нужен дополнительный модуль code_tag.py. html-файлы со встроенными сценариями Python должны быть оформлены по особым правилам.

Есть четыре варианта запуска процедуры py2html():
py2html('',0,0)
холостой запуск: html-файл не создается, и не показывается в браузере
html2py('',0,1)
холостой запуск: html-файл не создается, и не показывается в браузере
html2py('',1,0)
html-файл генерируется, но в браузере не показывается
html2py('',1,1)
html-файл генерируется и выводится в веб-браузер
Код модуля code_tag.py (не является частью этого файла):
def code_start1():
    return '<CODE>'

def code_start2():
    return '<code>'

def code_end1():
    return '</CODE>'

def code_end2():
    return '</code>'
В модуле code_tag.py хранятся сочетания символов '<CODE>', '<code>', '</CODE>' и '</code>'. В данном файле они - часть кода Python. Использовать их в явном виде, равно как заменить угловые скобки на символы &lt; и &gt; нельзя: программа будет работать неправильно.

# -*- coding: UTF-8 -*-
# Этот код получен из файла py2html.html с помощью утилиты html2py.py
import code_tag
from Tkinter import *
import tkFileDialog
import tkMessageBox
import os
import StringIO
import webbrowser

Функция choice()

Функция choice выводит на экран стандартное окна выбора файла и возвращает имя файла, выбранного пользователем. Команда root.withdraw() убирает с экрана пустое окно Tk.

В файле html2py.ini хранится имя последнего сконвертированного и записанного на диск файла; окно выбора файла открывается именно в каталоге этого файла. Если файл html2py.ini по какой-то причине не удается открыть, выбор файла производится из рабочего каталога программы.

Переменная ifile содержит имя файла с расширением *.py, который в последний раз был сохранен утилитой html2py. При каждом сохранении сконвертированного в сценарий Python файла его имя записывается в служебный файл html2py.ini.

Параметр initialdir - название каталога, в котором открывается диалоговое окно; оно соответствует последнему записанному на диск сценарию, обработанному утилитой html2py.py. Функция os.path.split() возвращает кортеж строк, содержащих адрес каталога и имя файла.


def choice():
    root=Tk()
    root.withdraw()
    try:
        filedd=open("py2html.ini", "r")
        ifile=filedd.readline() 
        filedd.close()
    except IOError:
        ifile=""
    myfiletypes=[('Файлы Python', '*.py'),('Все файлы', '*')]
    filetext=tkFileDialog.Open(title='Запуск...',initialdir=os.path.split(ifile)[0],filetypes=myfiletypes)
    return filetext

Процедура py2html(py_name,file_html,run)

Главная процедура py2html получает три входных параметра, определяющих ее дальнейшие действия. Причем file_html!=0 говорит, что будет создан файл с расширением .html; run!=0 означает, что сгенерированный html-файл будет показан в веб-браузере. py_name - это имя входного файла, которое может быть задано явно, как параметр py2html. В этом случае окно выбора файла не выводится.

Переменная code показывает, содержится ли в данной строке код Python (code=1) или комментарий (code=0)Переключатель switch включает вывод текстовой информации во временный файл a. Пока не появится первый тег <CODE>, ничего выводиться не будет.

a - файл, куда временно записывается генерируемый html-файл.

  
def py2html(py_name,file_html,run):
    global code
    code=0
    a = StringIO.StringIO()

    if py_name=='':
        b=open(choice().show(), 'r')
    else:
        b=open(py_name,'r')
Считываем открытый файл по одной строке и суммируем эти строки в переменной b1. Счетчик строк k вводится только по одной причине. Для подключения поддержки русского языка процедура html2.py() всякий раз принудительно вставляет в начало генерируемого сценария строку '# -*- coding: UTF-8 -*-\n'. Поэтому при обратной конвертации сценария в html-файл эту строку нужно удалить. Удаление происходит только в том случае, если эта строка - первая.

    b1=''
    k=0
    for line in b.readlines():
        if (k==0) & (line[:23]=='# -*- coding: UTF-8 -*-'):
            k=k+1
            continue
        k=k+1
        code_start=0
        code_end=0
Переменная цикла line по очереди ссылается на все строки считываемого файла. Мы будем побайтно перебирать каждую из них в поисках тегов <CODE> и </CODE>. Условия i<(len(b1)-5) и i<(len(b1)-6) нужны, чтобы при проверке мы не вылезали за конец строки. Числа 5 и 6 здесь соответствуют длине строк '<CODE>' и '</CODE>' (+1). Переменная start_tag представляет собой что-то вроде окошечка (см. рисунок), которое посимвольно перемещается по строке слева направо, пока в окошечко не попадет сочетание символов <CODE>. Тогда переключателю code присваивается ненулевое значение (область кода Python), а метке code_start - значение, равное 1 (в строке обнаружен стартовый тег <CODE>).

Затем в том же шаге цикла проводится аналогичный поиск тега </CODE>. Если он обнаруживается, меняются значения переменных code и code_end, и удаляется значок "##", стоящий перед этим тегом. Строка line разбивается на две части методом split(); границей раздела является комбинация символов </CODE>.



Условие if (start_tag==code_tag.code_start1()) | (start_tag==code_tag.code_start2()): идентично условию if (start_tag=='<CODE>') | (start_tag=='<code>'): и if (end_tag==code_tag.code_end1()) | (end_tag==code_tag.code_end2()): идентично if (end_tag=='</CODE>') | (end_tag=='</code>'):. Здесь теги <CODE> и </CODE> - часть кода Python, и заменить угловые скобки на их символические обозначения (&lt; и &gt;) нельзя. Поэтому код Python, где явно присутствуют сочетания символов <CODE> и </CODE>, вынесен за пределы этого файла в отдельный модуль code_tag.py.


        for i in range(len(line)):
            if (i<(len(line)-5)):
                start_tag=line[i]+line[i+1]+line[i+2]+line[i+3]+line[i+4]+line[i+5]
                if (start_tag==code_tag.code_start1()) | (start_tag==code_tag.code_start2()):
                    code=1
                    code_start=1
            if (i<(len(line)-6)):
                end_tag=line[i]+line[i+1]+line[i+2]+line[i+3]+line[i+4]+line[i+5]+line[i+6]
                if (end_tag==code_tag.code_end1()) | (end_tag==code_tag.code_end2()):
                    code=0
                    code_end=1
                    line1=line.split(end_tag)[0]
                    line2=line.split(end_tag)[1]
                    if (line1[-1]+line1[-2])=='##':
                        line1=line1[:-2]
                    line=line1+end_tag+line2
                    break
В зависимости от результатов поиска тегов <CODE> и </CODE> рассматриваемая строка или приплюсовывается к строке b1 без изменений, или, если строка не относится к зоне действия тега <CODE>, после проверки из ее начала удаляются символы "##". После того как в цикле будут обработаны все строки файла, итоговая строковая переменная b1 записывается во временный файл a.

        if (code_end==0):
            if (code==0) ^ (code_start==1):
                if (line[0]+line[1])=='##':
                    line=line[2:]
        b1=b1+line
    a.write(b1)
В переменной dir_file хранится имя директории, в которой находится обрабатываемый сценарий Python и куда будет записан сгенерированный из него html-файл.

Если файл с расширением .html создавать нужно (if file_html!=0:), то, прежде всего нужно заменить расширение исходного файла с .py на .html. Новое имя файла определяется выражением file_title[0]+'/'+name_html[0]+'.html'. Если файл с таким названием уже существует, то надо спросить, перезаписывать его или нет, с помощью функции tkMessageBox.askquestion().

    
    dir_file=os.path.split(b.name)[0]
    if file_html!=0:
        file_title=os.path.split(b.name)
        name_html=file_title[1].split(".")
        if os.path.isfile(file_title[0]+'/'+name_html[0]+'.html')!=0:
            if tkMessageBox.askquestion("Сохранить как",\
            file_title[0]+'/'+name_html[0]+'.html'+ u' уже существует. Заменить?')=="yes":
Содержимое полученного html-файла можно вызвать с помощью команды a.getvalue(). Кроме перезаписи существующего html-файла нужно еще записать название обрабатываемого сценария Python во вспомогательный ini-файл. Если полученный html-файл нужно запустить (if run!=0:), выводим его в веб-браузер с помощью команды webbrowser.open_new_tab(filew.name).

                ini_file=open("py2html.ini","w")
                ini_file.write(b.name)
                ini_file.close()
                open(file_title[0]+'/'+name_html[0]+'.html','w').write(a.getvalue())
                if run!=0:
                    filew=open(file_title[0]+'/'+name_html[0]+'.html', "r")
                    webbrowser.open_new_tab(filew.name)
                    filew.close()
                else:
                    pass
            else:
                pass
Если файла с таким именем нет, создаем его без лишних вопросов, не забывая прежде записать его имя во вспомогательный ini-файл. Если полученный html-файл нужно запустить (if run!=0:), выводим его в веб-браузер.

        else:
            ini_file=open("py2html.ini","w")
            ini_file.write(b.name)
            ini_file.close()
            open(file_title[0]+'/'+name_html[0]+'.html','w').write(a.getvalue())
            if run!=0:
                filew=open(file_title[0]+'/'+name_html[0]+'.html', "r")
                webbrowser.open_new_tab(filew.name)
                filew.close()
            else:
                pass
Если файл с расширением .html создавать не требуется, то никакие дальнейшие действия не производятся.

    else:
        pass
    a.close()
       
if __name__ == '__main__':
    py2html('',1,1)


Данный html-файл содержит в себе сценарий Python, который может быть извлечен и/или запущен на выполнение с помощью утилиты html2py.py.

Автор: Филипп Занько
Web-адрес: http://www.russianlutheran.org/python/python.html
Лицензия: Creative Commons Attribution-ShareAlike 3.0 Unported License

О своих предложениях, замеченных ошибках, неточностях, опечатках просьба сообщать по электронному адресу:
russianlutheran@gmail.com