Данный модуль позволяет вызывать питон из луа и луа из питона. С точки зрения Far это даёт возможность пользоваться из макросов многочисленными библиотеками питона.
Пример установки (пути в примере указаны для Python 3.7 x86 - скорректируйте соответственно если у вас Python 3.4/3.6 и/или x64):
Установить Python.
Скопировать src\out\python37\32bit\python.dll в %FARPROFILE%\Macros\lib32
Скопировать демо-макрос src\examples\py-hash.lua в %FARPROFILE%\Macros\scripts. В макрофайле есть таблица Config, где можно указать используемые вами битности.
Оригинальная библиотека и документация - здесь.
Используемый форк этой библиотеки - здесь.
local pyscript = [=[
def CalcHash(filename):
import hashlib
from os.path import getsize
f_src = open(filename, "rb")
if not f_src : ErrorExit("Cannot open file:", filename)
hlist = hashlib.algorithms_guaranteed
# hlist = hashlib.algorithms_available
hh = [hashlib.new(name) for name in hlist]
CHUNK = 16 * 1024
nLeft = getsize(filename)
while nLeft > 0:
toRead = min(CHUNK, nLeft)
buf = f_src.read(toRead)
nRead = len(buf)
if nRead == 0:
break
nLeft = nLeft - nRead
for h in hh: h.update(buf)
f_src.close()
res = []
for name,h in zip(hlist,hh):
res.append(name)
res.append(h.hexdigest())
return res
]=]
Macro {
description="Lunatic Python - HASHES";
area="Shell"; key="CtrlShiftF1";
condition=function(key) return not (APanel.Plugin or APanel.Folder) end;
action=function()
require "python"
local pb = python.builtins()
python.execute(pyscript)
local CalcHash = python.eval("CalcHash")
local all = CalcHash(APanel.UNCPath.."\\"..APanel.Current)
local names, hashes, nums = {}, {}, {}
local maxlen = 0
for k=1,pb.len(all),2 do
nums[#nums+1] = #nums+1
names [#nums] = all[k-1]
hashes[#nums] = all[k]
maxlen = math.max(maxlen, names[#nums]:len())
end
table.sort(nums, function(a,b) return names[a]:lower()<names[b]:lower() end)
local items = { {"DI_DOUBLEBOX",3,1,Far.Width-11,#nums+4,0,0,0,0,APanel.Current} }
for i,v in ipairs(nums) do
items[#items+1] = {"DI_TEXT",5,i+1,0,0,0,0,0,0,names[v]}
items[#items+1] = {"DI_EDIT",6+maxlen,i+1,Far.Width-12,0,0,0,0,{DIF_READONLY=1,DIF_SELECTONENTRY=1},hashes[v]}
end
items[#items+1] = {"DI_TEXT",-1, #nums+2,0,0,0,0,0,"DIF_SEPARATOR",""}
items[#items+1] = {"DI_BUTTON",0,#nums+3,0,0,0,0,0,{DIF_DEFAULTBUTTON=1,DIF_CENTERGROUP=1},"OK"}
local guid = win.Uuid("3417EE88-021E-4BEC-9F46-E3EF8FE8CBE9")
far.Dialog (guid,-1,-1,Far.Width-7,#nums+6,nil,items)
end;
}
Вообще удивляет отсутствие интереса к данной библиотеке.
Lunatic Python
Posted: Tue 26 Jan, 2016 19:37
by Smitis
Shmuel wrote:Вообще удивляет отсутствие интереса к данной библиотеке.
С моей точки зрения, Питон весьма неудобный язык (как и moonscript) для написания программ. На компьютере как скриптовый язык он мне не нужен.
Но, как дополнительная библиотека для lua может сгодиться, для Питона много чего понаписали. Но у меня "мобильная" версия ФАРа, синхронизируемая на нескольких компьютерах, пихать в неё ещё кучу файлов не хочется, итак объёмы растут. Тут бы разобраться, что действительно для работы Питона нужно. Экперимент показывает, что для данного примера достаточно python34.dll и несколько модулей из Lib (как я понял, реально используемые модули Питон предварительно компилирует в файлы *.pyc и помещает в папки __pycache__).
К сожалению, когда Питону чего-либо не хватает, ФАР при вызове макроса улетает в неизвестном направлении не попрощавшись. Вот бы какую-нибудь диагностику в виде сообщений типа "Не могу загрузить модуль zzzz" без падения ФАРа, это реализуемо?
Lunatic Python
Posted: Tue 26 Jan, 2016 20:15
by Shmuel
Smitis wrote: С моей точки зрения, Питон весьма неудобный язык (как и moonscript) для написания программ.
С этим не соглашусь, но ... о вкусах не спорят.
Smitis wrote: К сожалению, когда Питону чего-либо не хватает, ФАР при вызове макроса улетает в неизвестном направлении не попрощавшись. Вот бы какую-нибудь диагностику в виде сообщений типа "Не могу загрузить модуль zzzz" без падения ФАРа, это реализуемо?
Думаю, что всё реализуемо при условии вложения в проект нужного количества человеко-часов. Другой вопрос, произойдёт ли это.
Lunatic Python
Posted: Sat 30 Jan, 2016 18:20
by Shmuel
Демо-макрос: добавим прогресс-бар и возможность прерывать процесс нажатием Esc.
local pyscript = [=[
def CalcHash(filename):
import hashlib
from os.path import getsize
import lua
f_src = open(filename, "rb")
if not f_src : ErrorExit("Cannot open file:", filename)
hlist = hashlib.algorithms_guaranteed
# hlist = hashlib.algorithms_available
hh = [hashlib.new(name) for name in hlist]
display_progress = lua.eval("python._display_progress")
CHUNK = 16 * 1024
nSize = getsize(filename)
nLeft = nSize
nDisplayCount = 0
while nLeft > 0:
toRead = (CHUNK > nLeft) and nLeft or CHUNK
buf = f_src.read(toRead)
nRead = len(buf)
if nRead == 0:
break
nLeft = nLeft - nRead
nDisplayCount += 1
for h in hh: h.update(buf)
if nSize > 4*1024*1024 and nDisplayCount%201==1:
if display_progress(filename, nSize-nLeft+0.0, nSize+0.0, 1.0-nLeft/nSize):
f_src.close()
return
f_src.close()
res = []
for name,h in zip(hlist,hh):
res.append(name)
res.append(h.hexdigest())
return res
]=]
local uchar = unicode.utf8.char
local function set_progress (LEN, ratio)
local uchar1, uchar2 = uchar(9608), uchar(9617)
local len = math.floor(ratio*LEN + 0.5)
return uchar1:rep(len) .. uchar2:rep(LEN-len) .. (" %3d%%"):format(ratio*100)
end
local function display_progress (fullname, cntFound, cntTotal, ratio)
if win.ExtractKey()=="ESCAPE" then
far.AdvControl("ACTL_REDRAWALL")
return true
end
local WID, W1 = 60, 3
local W2 = WID - W1 - 3
local len = fullname:len()
local s = len<=WID and fullname..(" "):rep(WID-len) or
fullname:sub(1,W1).. "..." .. fullname:sub(-W2)
far.Message(
(s.."\n") .. (set_progress(W2, ratio).."\n") .. ("Processed: "..cntFound.."/"..cntTotal),
"Working...", "")
end
Macro {
description="Lunatic Python - HASHES";
area="Shell"; key="CtrlShiftF1";
condition=function(key) return not (APanel.Plugin or APanel.Folder) end;
action=function()
require "python"
python._display_progress = display_progress
local pb = python.builtins()
python.execute(pyscript)
local CalcHash = python.eval("CalcHash")
local all = CalcHash(APanel.UNCPath.."\\"..APanel.Current)
if all then
local names, hashes, nums = {}, {}, {}
local maxlen = 0
for k=1,pb.len(all),2 do
nums[#nums+1] = #nums+1
names [#nums] = all[k-1]
hashes[#nums] = all[k]
maxlen = math.max(maxlen, names[#nums]:len())
end
table.sort(nums, function(a,b) return names[a]:lower()<names[b]:lower() end)
local items = { {"DI_DOUBLEBOX",3,1,Far.Width-11,#nums+4,0,0,0,0,APanel.Current} }
for i,v in ipairs(nums) do
items[#items+1] = {"DI_TEXT",5,i+1,0,0,0,0,0,0,names[v]}
items[#items+1] = {"DI_EDIT",6+maxlen,i+1,Far.Width-12,0,0,0,0,{DIF_READONLY=1,DIF_SELECTONENTRY=1},hashes[v]}
end
items[#items+1] = {"DI_TEXT",-1, #nums+2,0,0,0,0,0,"DIF_SEPARATOR",""}
items[#items+1] = {"DI_BUTTON",0,#nums+3,0,0,0,0,0,{DIF_DEFAULTBUTTON=1,DIF_CENTERGROUP=1},"OK"}
local guid = win.Uuid("3417EE88-021E-4BEC-9F46-E3EF8FE8CBE9")
far.Dialog (guid,-1,-1,Far.Width-7,#nums+6,nil,items)
end
end;
}
Lunatic Python
Posted: Mon 01 Feb, 2016 14:06
by Smitis
При запуска макроса с прогресс-баром, сначала показывается прогресс-бар, до конца не доходит и питон вылетает с выводом в консоль
local pyscript = [=[
def CalcHash(filename):
import hashlib
from os.path import getsize
import lua
f_src = open(filename, "rb")
if not f_src : ErrorExit("Cannot open file:", filename)
hlist = hashlib.algorithms_guaranteed
# hlist = hashlib.algorithms_available
hh = [hashlib.new(name) for name in hlist]
display_progress = lua.eval("python._display_progress")
CHUNK = 16 * 1024
nSize = getsize(filename)
nLeft = nSize
nDisplayCount = 0
while nLeft > 0:
buf = f_src.read((CHUNK < nLeft) and CHUNK or nLeft)
nRead = len(buf)
if nRead == 0:
break
nLeft = nLeft - nRead
nDisplayCount += 1
for h in hh: h.update(buf)
if nSize > 4*1024*1024 and nDisplayCount%201==1:
if display_progress(filename, nSize-nLeft+0.0, nSize+0.0, 1.0-nLeft/nSize):
f_src.close()
return
f_src.close()
res = []
for name,h in zip(hlist,hh):
res.append(name)
res.append(h.hexdigest())
return res
]=]
local uchar = unicode.utf8.char
local function set_progress (LEN, ratio)
local uchar1, uchar2 = uchar(9608), uchar(9617)
local len = math.floor(ratio*LEN + 0.5)
return uchar1:rep(len) .. uchar2:rep(LEN-len) .. (" %3d%%"):format(ratio*100)
end
local function FormatNum(num)
local s = tostring(num)
local len = #s
s = s:gsub("()", function(pos) return pos>1 and pos<len and (len-pos)%3==2 and "," end)
return s
end
local function display_progress (fullname, cntFound, cntTotal, ratio)
if win.ExtractKey()=="ESCAPE" then
far.AdvControl("ACTL_REDRAWALL")
return true
end
local WID, W1 = 60, 3
local W2 = WID - W1 - 3
local len = fullname:len()
local s = len<=WID and fullname..(" "):rep(WID-len) or
fullname:sub(1,W1).. "..." .. fullname:sub(-W2)
far.Message(
(s.."\n") .. (set_progress(W2, ratio).."\n") .. ("Processed: "..FormatNum(cntFound).."/"..FormatNum(cntTotal)),
"Working...", "")
end
Macro {
description="Lunatic Python - HASHES";
area="Shell"; key="CtrlShiftF1";
condition=function(key) return not (APanel.Plugin or APanel.Folder) end;
action=function()
require "python"
python._display_progress = display_progress
local pb = python.builtins()
python.execute(pyscript)
local CalcHash = python.eval("CalcHash")
local all = CalcHash(APanel.UNCPath.."\\"..APanel.Current)
if all then
local names, hashes, nums = {}, {}, {}
local maxlen = 0
for k=1,pb.len(all),2 do
nums[#nums+1] = #nums+1
names [#nums] = all[k-1]
hashes[#nums] = all[k]
maxlen = math.max(maxlen, names[#nums]:len())
end
table.sort(nums, function(a,b) return names[a]:lower()<names[b]:lower() end)
local items = { {"DI_DOUBLEBOX",3,1,Far.Width-11,#nums+4,0,0,0,0,APanel.Current} }
for i,v in ipairs(nums) do
items[#items+1] = {"DI_TEXT",5,i+1,0,0,0,0,0,0,names[v]}
items[#items+1] = {"DI_EDIT",6+maxlen,i+1,Far.Width-12,0,0,0,0,{DIF_READONLY=1,DIF_SELECTONENTRY=1},hashes[v]}
end
items[#items+1] = {"DI_TEXT",-1, #nums+2,0,0,0,0,0,"DIF_SEPARATOR",""}
items[#items+1] = {"DI_BUTTON",0,#nums+3,0,0,0,0,0,{DIF_DEFAULTBUTTON=1,DIF_CENTERGROUP=1},"OK"}
local guid = win.Uuid("3417EE88-021E-4BEC-9F46-E3EF8FE8CBE9")
far.Dialog (guid,-1,-1,Far.Width-7,#nums+6,nil,items)
end
end;
}
Что плохо - то, что макрос (если он уже запускался) не выдерживает перезагрузки макросов - запуск после перезагрузки приводит к вылету. Надеюсь разобраться с этим, но когда - не знаю.
Lunatic Python
Posted: Thu 10 Aug, 2017 11:44
by tstr
пару замечаний касательно портабельности:
1. для загрузки Lunatic Python расширения достаточно добавить python34.dll и python34.zip в %FARHOME%
2. если не хотим захламлять %FARHOME%, то можно добавить в %FARPROFILE%\Macros\modules\python\init.lua что-то типа временной уставновки переменной PATH и загрузки модуля из dll:
local profile = win.GetEnv("FARPROFILE")
local arch
if _G.jit.arch == "x64" then
arch = "AMD64"
else
arch = "IA32"
end
local path_bak = win.GetEnv("PATH")
local path = ("%s\\%s\\clib.%s;"):format(win.GetEnv("FARPROFILE"), "Macros", arch) .. ("%s\\%s\\lib.%s;%s"):format(win.GetEnv("FARPROFILE"), "Macros", arch, path_bak)
win.SetEnv("PATH", path)
local dll_name = 'python.dll'
local result = package.loadlib(dll_name, "luaopen_python")()
win.SetEnv("PATH", path_bak)
return result
- в данном случае добавляются временно %FARPROFILE%\Macros\clib.AMD64, %FARPROFILE%\Macros\lib.AMD64 или %FARPROFILE%\Macros\clib.IA32, %FARPROFILE%\Macros\lib.IA32 в зависимости от битности ФАР. в первой живёт Lunatic Python (python.dll), во второй - python34.dll и python34.zip
Lunatic Python
Posted: Thu 10 Aug, 2017 16:23
by Shmuel
tstr, дней 10 назад изменилось значение по умолчанию package.cpath. Поэтому 32-битный python.dll можно класть в %FARPROFILE%\Macros\lib32, а 64-битный - в %FARPROFILE%\Macros\lib64. Первый пост темы отредактирован с учётом этого изменения.
Lunatic Python
Posted: Thu 10 Aug, 2017 17:38
by tstr
Shmuel, я для себя ранее отметил указанные Вами изменения, но на подготовку локальной конфигурации как это нужно мне как пользователю (которая делалась ранее в _macroinit.lua , а затем перекочевала в luafar_init.lua, что исправило загрузку библиотек, используемых lfsearch и упростило схему) — кроме насильственного создания двух пустых папок, не повлияло. для пользователя - терпимо; для программиста, для определённости - хорошо. я указывал на ньюанс возможности перехвата загрузки модуля для подготовки окружения…
я понимаю насч. изначальной идеи совместимости с ХР а следовательно переносимости, но в свете pygin и возможностью настроек его загрузки с помощью python36._pth и pyvenv.cfg - может подправить Lunatic Python на использование “обёрточной” python3.dll ?
Lunatic Python
Posted: Thu 10 Aug, 2017 17:53
by Shmuel
tstr wrote: ↑Thu 10 Aug, 2017 17:38может подправить Lunatic Python на использование “обёрточной” python3.dll ?
А эта обёрточная где-то есть готовая? Не хотелось бы ещё один проект заводить.
Поскольку сам я с XP уже съехал (заставили на работе, а так мне было с ней очень хорошо), то лично я уже не озабочен совместимостью с XP. Может быть просто пересобрать библиотеку с Python 3.6 ?
Lunatic Python
Posted: Thu 10 Aug, 2017 18:36
by tstr
интерфейс python3.dll - "стабильное" (stable ABI) подмножество интерфейса python36.dll , его хватает в большинстве кейсов использования подобного нашим. прелесть в том, что теоретически должно работать без пересборки на всей современной 3-й ветке.
Lunatic Python
Posted: Thu 10 Aug, 2017 20:56
by DrKnS
tstr wrote: ↑Thu 10 Aug, 2017 17:38в свете pygin и возможностью настроек его загрузки с помощью python36._pth и pyvenv.cfg
А можно узнать подробности? И спасибо за информацию о python3.dll.
Lunatic Python
Posted: Thu 10 Aug, 2017 21:37
by Shmuel
tstr wrote: ↑Thu 10 Aug, 2017 18:36"стабильное" (stable ABI) подмножество интерфейса python36.dll , его хватает в большинстве кейсов использования подобного нашим
Но не хватает для библиотеки Lunatic Python (в том виде как она есть у нас сейчас). Например, используемая библиотекой функция PyUnicode_AsUTF8AndSize становится недоступной. (Всё это можно, наверное, решить, но я не уверен, что буду это делать).