Python для Си-шника — 2

Полтора года назад я уже писал на эту тему (http://wp.me/p1H7g0-RZ). Я там рассказывал про питоновскую программу mkch для создания двух Си-шных файлов (.c и .h), имя которых задавалось пользователем.

Та программа отличалась простотой и была мягко говоря небезопасна в своём применении. Я имею в виду ситуацию, когда пользователь по невнимательности мог создать точно такую же пару файлов и похоронить наработанный в них текст. О чём говорить, если я и сам пару раз на ступал на эти грабли!

Вот так получилось, что сейчас я тружусь над проектом, в котором есть дополнительное условие — исходники должны быть документированы согласно требованиям doxygen.

В общем, интенсивное (за последние дни) использование этой утилиты подтолкнули меня к её рефакторингу. И, вот, пока я далеко не ушёл от темы, — публикую её новую версию.

Юзайте на здоровье!

Собственно, изменений немного. Сейчас утилита не затрёт существующие файлы. Изменен шаблонный код файлов — теперь в них присутствуют doxygen-овские заголовки.

К стати, обратите внимание, на строки 76 и 77 — в этих строках присутствует моё имя и моя почта. Вам, разумеется, нужно исправить их под свои реквизиты.

#!/usr/bin/env python
#coding:utf-8


'''
@brief   Утилита создает два Си-шных файла (.c и .h) с именем, заданным пользователем.
@version 0.2
@date    2016.05.31
@author  Жевак Александр
@email   zhevak@mail.ru
'''


import sys
import os
from datetime import date


def create_hfile(rcvs):
  hfile = open(rcvs[0], "w")
  hfile.write("/**\n")
  hfile.write("@file    " + rcvs[0] + '\n')
  hfile.write("@version 0.0\n")
  hfile.write("@date    " + rcvs[3] + '\n')
  hfile.write("@author  " + rcvs[4] + '\n')
  hfile.write("@email   " + rcvs[5] + '\n')
  hfile.write("@brief   вставьте сюда краткое описание файла\n")
  hfile.write("*/\n")
  hfile.write("\n\n")
  hfile.write("#ifndef " + rcvs[2] + '\n')
  hfile.write("#define " + rcvs[2] + '\n')
  hfile.write("\n\n")
  hfile.write("// Определения внешних переменных и функций\n")
  hfile.write("\n\n")
  hfile.write("#endif" + '\n')
  hfile.close()


def create_cfile(rcvs):
  cfile = open(rcvs[1], "w")
  cfile.write("/**\n")
  cfile.write("@file    " + rcvs[1] + '\n')
  cfile.write("@version 0.0\n")
  cfile.write("@date    " + rcvs[3] + '\n')
  cfile.write("@author  " + rcvs[4] + '\n')
  cfile.write("@email   " + rcvs[5] + '\n')
  cfile.write("@brief   вставьте сюда краткое описание файла\n")
  cfile.write("*/\n")
  cfile.write("\n\n")
  cfile.write("#include \"" + rcvs[0] + "\"\n")
  cfile.write("\n\n")
  cfile.write("// Допишите ниже определения глобальных переменных и функций\n")
  cfile.close()


def is_module_exists(rcvs):
  '''
  Убедимся, что файлы модуля не существуют
  '''
  if os.path.isfile(rcvs[0]):
    return True
  
  if os.path.isfile(rcvs[1]):
    return True

  return False # Нет ни одного файла модуля, всё чисто
    
  
def create_module(modname):
  # Сгенерим имена для файлов и другие реквизиты
  recvisities = (
    modname + ".h",
    modname + ".c",
    "__" + modname.upper() + "_H__",
    str(date.today()),
    "Жевак Александр",  # os.getlogin()
    "zhevak@mail.ru")

  if not is_module_exists(recvisities):
    # Создадим файлы модуля
    create_hfile(recvisities)
    create_cfile(recvisities)
  else:
    print 'ОШИБКА: оба файла модуля (или какой-то один из них) уже существуют.'


def show_help():
  print "Используйте: mkch <имя модуля>"


if __name__ == "__main__":
  try:
    modname = sys.argv[1]
    #print modname 
    create_module(modname)
  except IndexError:
    show_help()

К сожалению, мне не удалось полностью реализовать свои задумки. Я не смог найти простого способа получить полное имя пользователя. Функция getlogin() возвращает имя учётной записи (аккаунта), но не имя пользователя. А я хотел бы получать полное имя пользователя, то есть пятый параметр в строках в файле /etc/passwd.

Программа mkch не является шедевром, в ней не все гладко и красиво с академической точки зрения. Во всяком случае её можно (и нужно!) доработать. Если вам есть что сказать по этому поводу — не утаивайте от общества свои идеи! Иначе их обнародует кто-нибудь другой🙂

UPDATE 02.06.2016

Ниже ещё одна версия утилиты. Это немного изменённая версия Попова Руслана.

#!/usr/bin/env python
#coding: utf-8


'''
@file    mkch-0.2.2
@brief   Утилита создает два Си-шных файла (.c и .h) с именем, заданным пользователем.
@version 0.2.2
@date    2016.06.08
@author  Попов Руслан и Жевак Александр
@email   zhevak@mail.ru
'''


import os
import pwd
import sys
from datetime import datetime

EMAIL = u'zhevak@mail.ru'


TEMPLATE_HEADER = """/**
@file    {header}
@version 0.0
@date    {date}

@author  {author}
@email   {email}

@brief   вставьте сюда краткое описание файла
*/


#ifndef {label}
#define {label}

// Определения внешних переменных и функций

#endif  // {label}
"""

TEMPLATE_BODY = """/**
@file    {body}
@version 0.0
@date    {date}

@author  {author}
@email   {email}

@brief   вставьте сюда краткое описание файла
*/


#include "{header}"

// Допишите ниже определения глобальных переменных и функций
"""

def is_module_exists(files):
  for f in files:
    if os.path.isfile(f):
      print 'ОШИБКА: файл {} уже существует'.format(f)
      return True

  return False


def create_file(filename, template, params):
    with open(filename, 'w') as f:
       f.write(template.format(**params))


def get_user_info():
    login = os.getlogin()
    info = pwd.getpwnam(login)
    gecos = info.pw_gecos.split(',')

    username = os.environ.get('GIT_AUTHOR_NAME') or (login if len(gecos) == 1 else gecos[0])
    email = os.environ.get('GIT_AUTHOR_EMAIL') or EMAIL
    return username, email


def create_module(module_name):
    # Сгенерим имена для файлов и другие реквизиты
    header = '{}.h'.format(module_name)
    body = '{}.c'.format(module_name)
    username, email = get_user_info()
    params = {
        'header': header,
        'body': body,
        'label': '__{}_H__'.format(module_name.upper()),
        'date': datetime.now().strftime('%Y.%m.%d'),
        'author': username,
        'email': email
    }
    
    if not is_module_exists((header, body)):
      create_file(header, TEMPLATE_HEADER, params)
      create_file(body, TEMPLATE_BODY, params)


def show_help():
    print "Используйте: mkch <имя модуля>"


if __name__ == '__main__':
    try:
        module_name = sys.argv[1]
    except IndexError:
        show_help()
    else:
        create_module(module_name)

Теперь ещё такой момент. У меня в моём домашнем директории есть поддиректорий bin, куда я складываю вот такие утилиты как эта. Сейчас у меня в этом директории собралось уже три версии этой утилиты. Понятно, что у них уникальные имена:

mkch-1

Возникает проблема: альтернативные версии хотелось бы иметь под руками (то есть не желательно их удалять), но указывать при каждом наборе имени утилиты ещё и версию — получается несколько неудобно.

Хотелось бы иметь возможность вызывать утилиту по её короткому имени, и в то же время, хотелось бы иметь где-то рядом другие версии.

С одной стороны, для разрешения подобных вопросов Линукс предоставляет удобный механизм — жесткие и мягкие ссылки. А с другой, исполняемые файлы можно вызывать и по ссылкам на них. Ну, вы поняли!

Жесткая ссылка — это не совсем ссылка. Это — второе имя того же самого файла. А мягкая ссылка — это и есть самая настоящая ссылка. То есть это есть файл, внутри которого прописана ссылка на другой файл.

Жесткие и мягкие ссылки создаются одной и той же командой. Разница только в ключике -s, который указывается при создании мягкой ссылки. При создании жесткой ссылки ключ не задается.

Однако, хватит теории! Давайте перейдем к практике. Давайте создадим мягкую ссылку на последнюю версию утилиты.

Откроем окно псевдотерминала и введём команду:

$ ln -s ~/bin/mkch-0.2.1 ~/bin/mkch

Кого смущают тильды и длинные пути доступа можете сначала войти в поддиректорий bin

$ cd bin

и там выполнить более простой вариант команды:

$ ln -s mkch-0.2.1 mkch

Вне зависимости от варианта, у вас в директории bin появится мягкая ссылка:

mkch-2

Теперь можно работать над своим Си-шным проектом в его директории, а когда возникнет необходимость создать в проекте пару файлов нового модуля, то нужно тупо набрать команду:

$ mkch newmod

В результате в директории появятся два шаблонных файла newmod.c и newmod.h.

Думаю, что вы знаете, что дальше делать с этим файлами.🙂

UPDATE 08.06.2016

Чуть-чуть изменил шаблон программы. Оказалось, что система докуентирования Doxygen не воспринимает длинные конструкции типа:

/***************************************

а реагирует только на короткие:

/**

.

7 responses to “Python для Си-шника — 2

  1. Замечания:
    1) Лучше код вываливать на GitHub.
    2) Для доступа к Gecos у Python есть модуль pwd, я показал как им пользоваться.
    3) Но лучше читать переменные окружения GIT_AUTHOR_NAME и GIT_AUTHOR_EMAIL, тоже показал.
    4) Файлы лучше открывать через контекст.
    5) Файлы формировать удобнее через шаблоны, их можно вынести в отдельные файлы.

    Собственно код: https://gist.github.com/RaD/fb42d8588526294d24c7f6f6560e4fc9

    • Руслан, я посмотрел вашу версию.

      Ваша версия более правильная с точки зрения парадигмы Питона. Обе версии на писаны на Питоне, но моя версия написана в стиле Си, Ваша — в стиле Питона.

      Теперь, что касается логики программы. В программе есть один неоднозначный момент. Я не знаю как правильно должна вести себя прога, когда из двух файлов один уже имеется на диске. Должна ли она вывалится с ошибкой или должна создать недостающий файл. Однозначного ответа на этот вопрос нет.

      Следующий момент — у Вас в 9-ой строке указан мой и-мэйл. Мне кажется это не совсем правильно указывать мои реквизиты в чужой программе.

      2) Модуль pwd — это то, что я искал, но не нашёл! Мне кажется, что я о нём знал, но по прошествии большого куска времени крепко забыл. Мозг-собака избавляется ото всего, чем не пользуется длительное время…

      По остальным пунктам не то, что нет возражений, а скорее наоборот — я благодарен Вам за науку! Я всё-таки «унутре себя» Си-шник, поэтому и о программах думаю по Си-шному. Поэтому и программы на Питоне, получаются в стиле Си.

      • ruslanpopov

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

        > Следующий момент — у Вас в 9-ой строке указан мой и-мэйл.
        Я же не для себя писал🙂 Вообще, лучше использовать переменные окружения для подхватывания инфы о разработчике.

        > Обе версии на писаны на Питоне, но моя версия написана в стиле Си, Ваша — в стиле Питона.
        Я и на Си также пишу, коротко и максимально просто.
        https://github.com/halfakop/Teacup_Firmware/tree/ssd1306

      • > Однозначно останавливаться…
        Да. Это так. Но что делать в случае, когда в наличие имеется один файл из двух? Нужно ли создавать второй (недостающий) или остановиться с ошибкой.

        Я немного переделал Ваш код. Прога проверяет наличие хотя бы одного файла, и, если результат положителен, то останавливается. Прога создаст файлы только в случае, если в наличии нет ни одного.

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

        Можно их выуживать из ~/.gitconfig, но ведь не у каждого программиста (на кого рассчитана утилита mkch) установлен git. И у ж тем более не у каждого, кто пользуется git-ом в этом файле прописаны его реквизиты.

        Наверно единственный верный вариант — это собирать эту информацию по «закоулочкам». Дублируя эту информацию в программном коде в виде констант. Не очень красивое решение, но зато надёжное. Ну, то есть то, что Вы и предложили.

        Я не вижу других способов решить этот вопрос.

  2. > Можно их выуживать из ~/.gitconfig, но ведь не у каждого программиста (на кого рассчитана утилита mkch) установлен git. И у ж тем более не у каждого, кто пользуется git-ом в этом файле прописаны его реквизиты.

    Такие люди недостойны использовать вашу программу🙂
    Не используешь гит или не можешь его правильно настроить — в топку.

    • Какой Вы, однако, безжалостный и категоричный!

      • ruslanpopov

        Ну а как иначе? Умение использовать систему контроля версий — это как тест на профпригодность.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s