Как стать автором
Обновить

Комментарии 15

Я так и не понял, для каких задач необходим этот велосипед. os.environ — куда уж проще. subprocess для задач типа ls -l избыточен, но обёртка вокруг него 5 строк займет.

os.environ — куда уж проще.

Одна из идей класса ENV — убрать лишние манипуляции с преобразованиями типов, которые неизбежны при работе с os.environ (типичный пример — проверка переменной DEBUG в окружении). Меньше отвлекающих действий — чище код. Чище код — меньше потенциальных багов. Обе библиотеки, к слову, хорошо покрываются тестами (в версии 1.0.1 почти 100% покрытие выйдет).
subprocess для задач типа ls -l избыточен, но обёртка вокруг него 5 строк займет

А теперь представьте, что вы пишете скрипт автоматизации сборки или что-то в этом роде. Ладно, если «ls» вы замените на os.listdir(), ну а если понадобится «ls -R»? ПредлОжите девопсу разместить нечто вроде такого?
[os.path.join(dp, f) for dp, dn, fn in os.walk(os.path.expanduser("~/files")) for f in fn]

Ну и аналогично насчет проверок, если команда завершилась с ошибкой и прочие прелести нештатных ситуаций.
А если 90%+ скрипта все же будет завязано на использовании Shell, потому что это просто лаконичнее и удобнее, будет ли смысл писать os.listdir() вместо Shell.ls()?

Создавая эти библиотеки, я хотел дать возможность инженерам портировать (и одновременно стабилизировать) свои Bash-скрипты без необходимости глубоко изучать Python. Но есть и обратная сторона: Python-разработчикам, в случае необходимости, также придется меньше возиться с Bash.
А если 90%+ скрипта все же будет завязано на использовании Shell,

То лаконичнее и удобнее использовать чистый shell скрипт, а не Python.


ну а если понадобится «ls -R»

И потом парсить .output в python? Красиво получится, да. А если вместо предложенного варианта не использовать однострочник с list comprehension, то можно написать читаемый, понятный код.

То лаконичнее и удобнее использовать чистый shell скрипт, а не Python.

Вы вырвали фразу из контекста, причем потеряв по пути важную вторую часть.
Но добавлю по Вашему комментарию: Python может дать много возможностей, которые в Bash достигаются через боль, для манипуляций с данными + их передачей между командами Bash'а.

И потом парсить .output в python? Красиво получится, да. А если вместо предложенного варианта не использовать однострочник с list comprehension, то можно написать читаемый, понятный код.


Да, можно, но в примере с циклом Вам нужно будет прочитать больше строк кода, выполняющих то же самое, что 1-2 строчки с использованием Shell.

Библиотека должна одинаково хорошо и легко работать с любой Bash-командой (ls — это просто пример, не более), вплоть до сложных конструкций и последовательностей команд. И она будет это делать в будущих релизах.
ПредлОжите девопсу разместить..


Предложу писАть на понятном языке ;)
Или, если нужно писать на питоне — выучить какой-нибудь курс «питон за 10 уроков». Писать на питоне, и испытывать сложности с dictionary… Не нужно даже пытаться.

А теперь представьте, что вы пишете скрипт автоматизации сборки или что-то в этом роде.

Допустим..
def RunMeSimple(args,env_=None,cwd="."):
    pobj = subprocess.Popen(args, env=env_, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1)
    (stdoutdata, stderrdata)=pobj.communicate()
    code = pobj.wait()
    out=stdoutdata.splitlines()
    err=stderrdata.splitlines()
    mix=out.extend(err)
    return (out,err,mix,code)



Поставленную задачу (запуск процессов с параметрами и получение результатов) этот код выполняет. Зачем усложнять?

если в переменной окружения лежит сериализованный JSON-подобный объект или даже просто булевая константа


Если раньше это работало на bash — я бы хотел видеть (развидеть не смогу, знаю) ;)
Или, если нужно писать на питоне — выучить какой-нибудь курс «питон за 10 уроков». Писать на питоне, и испытывать сложности с dictionary… Не нужно даже пытаться.

Скорее всего, человек, которому поставлена задача смигрировать скрипты на Python, и так изучит его подобным образом. Но речь не о том, есть ли сложности со словарями или нет — вынуть переменную по ключу сможет любой хелловордщик. Если скрипт не предполагает интерпретацию значений из переменных окружения, то smart-env, вероятно, не даст ощутимой выгоды (кроме, может, как конфигурационный объект). Но с тем же успехом можно назвать Openstack «излишней сложностью», если рассматривать его в качестве площадки для поднятия вордпрессового блога на локалхосте.

Поставленную задачу (запуск процессов с параметрами и получение результатов) этот код выполняет. Зачем усложнять?


Посмотрим на Ваш пример с точки зрения качества кода:
1. Название команды само по себе не является аргументом, который следует класть в args. В Вашем примере объект, вызывающий RunMeSimple(), частично осведомлен о внутренностях вызываемой функции — явный признак того, что код надо переписать.
2. Функция возвращает кортеж из четырех (!) значений. Серьёзно? Получается, вызывающему надо будет ещё и правильно распилить полученный кортеж по переменным и, не дай Бог, не поменять stdout и stderr местами и потом гадать, почему docker ps сыпет текст в поток ошибок.
3. Возможно, я и излишне придрался к конкретному примеру, но, опять-таки, пока проект — это пара скриптов и десяток функций, то можно и subprocess дергать, а вот когда это будет мамонт на овер9000 строк — наверняка вспомните опыт других, но уже станет страшно даже чихнуть не в том месте, чтобы все не рухнуло.

Идея python-shell — это упрощение кода, а не усложнение. Ваш пример преобразовался бы в куда более понятное
cmd = Shell.mycommand(*args, env=env_from_somewhere, cwd='.')
# и где-то ниже, непосредственно там, где это нужно по логике программы
passToSomeFoo(cmd.output)
# и т.п.

причем даже без необходимости создавать под это отдельную функцию (в которой на 95.5% гарантированы копипасты блоков кода из аналогичных «функций-вызывалок»).

Вспомнилась фраза: «Это Си, тут Солнце выкатывают и закатывают вручную». Безусловно, можно продолжать городить кучи комбинаций Popen()+communicate()+<и т.д.> (втайне мечтая переехать на «новенький» 3.5 с 2.7, чтобы суметь в subprocess.run) и кушать кактус. Но зачем?

Если раньше это работало на bash — я бы хотел видеть (развидеть не смогу, знаю) ;)

Про JSON — это не портирование Bash, а новая возможность (хотя попытки ее применения, думаю, не новинка).
Про булевое значение: Вам никогда не приходилось интерпретировать значение переменной окружения на предмет того, True или не True?
# export DEBUG=True
# предположим, если переменная не выставлена, то считаем ее False
DEBUG = bool(os.environ.get('DEBUG'))

# export DEBUG=False
# export DEBUG=0
# и тут номер с невыставлением не проходит...
DEBUG = os.environ.get('DEBUG') not in ("False", "false", 0, None)  # красиво?


А теперь, согласно Вашей теории, «усложнённый» вариант:
# скажем, это наш config.py

from smart_env import ENV
ENV.enable_automatic_type_cast() 

DEBUG = ENV.DEBUG  # и весь парсинг уже сделан за нас - в конфиге только полезная информация!


Надеюсь, я ответил на Ваш вопрос. :)
Вам никогда не приходилось интерпретировать значение переменной окружения на предмет того, True или не True?


Не припомню ;) А зачем хотеть уметь
это?
DEBUG = os.environ.get('DEBUG') not in («False», «false», 0, None)


Следующим шагом захочется уметь yes/no, y/n, да/нет (ya/niht)… КМК, нужно стремиться использовать какой-то один стиль (если у вас python — логично True/False).
Не припомню ;) А зачем хотеть уметь это?

Жизнь — штука непредсказуемая. :)
Вообще, мне часто попадались веб-проекты, где через переменную окружения задавался режим отладки ( и это нормальное явление, имхо). Да и не только из окружения — раз источником переменных выступал key-value Консула. Получались те же Фаберже, только отдельным сервисом.

Следующим шагом захочется уметь yes/no, y/n, да/нет (ya/niht)… КМК, нужно стремиться использовать какой-то один стиль (если у вас python — логично True/False).

В smart-env стиль как раз четко очерчен: строковые константы двух видов — как в Python и как в классическом JSON (как самые потенциально ходовые).
втайне мечтая переехать на «новенький» 3.5 с 2.7, чтобы суметь в subprocess.run

Если subprocess.run устравает, то почему бы не использовать бэкпорт вместо создания нового API. Ведь переехать всё равно придётся рано или поздно.

«Рано или поздно» может превратиться в долгие годы, если в вашем распоряжении скриптодактиль на овер9000 строк с запутанной логикой благодаря коллегам с не очень прямыми крыльями и «художественным» образом мышления. Мне всего лишь год назад попался проект на Django 1.6 без шансов портировать даже на 1.11, а еще за полгода до того — на Web2Py (вот где «рано или поздно» просто не случится!). Но это так, кунсткамера из веб-разработки, а были и примеры из мира деплоймент-скриптов, где одной заменой print-ов не отделаешься.

Бэкпорт я видел, но он решает только конкретные несколько вопросов. Использовать у себя, к слову, не стал, т.к. чем меньше third-party в данном случае, тем лучше.

А python-shell позиционируется как куда более универсальная обертка, которая позволит значительно разгрузить скрипты на предмет импорта и использования различных модулей.
ПредлОжите девопсу разместить нечто вроде такого?

os.path — это очень низкоуровневая штука. Забудьте уже о ней. Уже давно в стандартной библиотеке есть pathlib и shutil, которые покрывают почти все операции с файлами.


Что вам нужно сделать? Просто получить все файлы в какой-то поддиректории в домашней директории?


files = (Path.home() / 'files').glob('*')

Если с рекурсивным спуском вглубь:


files = (Path.home() / 'files').rglob('*')

По-моему, очень читабельно и понятно.


Сравните создание директории:


  • в pathlib: Path('/tmp/new_folder').mkdir(parents=True)
  • в python-shell: Shell.mkdir('-p', '/tmp/new_folder')

По-моему, pathlib опять выигрывает в читабельности. А если учесть, что объекты Path очень легко объединять (например, Path('dir') / 'subdir' / 'file.txt'), легко извлекать и менять их компоненты (например, Path('dir/a/b/c').relative_to('dir/') даёт Path('b/c')) и их можно подавать в стандартные функции для работы с файлами без преобразования, то я вообще не вижу причин не использовать pathlib.


Для файловых операций есть shutil, в котором можно найти функции для копирования, перемещения и даже создания архивов.


Можете привести пример, когда python-shell выигрывает у этих модулей из стандартной библиотеки?

А мне больше нравится подход shellpy.
Он обсуждался на хабре.
Что может быть естественнее для девопса, чем:
result = `ls -l`
for line in result:
    print line
return result.returncode
А мне больше нравится подход shellpy.
Он обсуждался на хабре.

Спасибо за ссылку, почитаю.

Да, shellpy делает код очень похожим на pure bash, и в этом одновременно плюс и минус такого подхода. Плюс в том, что код действительно можно переносить практически через Ctrl+C — Ctrl+V. Но в чем тогда резон вообще переносить всё на Python, если скрипты будут выглядеть, как «нечто похожее на Bash, но не Bash»? Я к тому, что читабельности это не добавит, а багов может даже прибавить (на тех операциях, которые в Shell делаются под капотом).

Вообще, библиотека python-shell исповедует всё-таки больше Python-овый стиль, поэтому и была сделана попытка реализовать удобное решение на базе ООП. Вспомните ORM-решения — с ними ведь обычно приятнее и быстрее работать, чем с raw-SQL, который еще и отличается в разных диалектах.

Вообще, параметр '-p' в Shell.mkdir('-p', '/tmp/new_folder') не очень-то добавляет читабельности и не способствует отсутствию багов.

Параметр "-p" не относится к читабельности и багам. Если ваш скрипт предусматривает его использование — используйте. Библиотеки из поста тут ни при чём — это был просто пример.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории