#!/usr/bin/env python
# coding: utf-8
#
# ["**Onetrigg**"](http://bobuk.github.com/onetrigg.py) это мой способ обеспечить удобство тестового деплоймента.
# Ну... в моем понимании этого слова. Довольно часто я использую маленькие скрипты,
# которые слегка по разному должны вести себя в тестовом и боевом окружении.
# Например в тестовом деплойменте веб-сервиса обязательно должны показываться тестовые
# сообщения, а в боевом - конечно же нет. Большинство людей использует для этого конфиг.
# Но мы не ищем легких путей, поэтому я поступаю иначе.

# Вот так выглядит типовой образец моего скрипта, где-нибудь посреди кода.
# В умолчальном состоянии этот код работает "как есть", т.е. в данном случае
# запрещает дебаггинг и скрывает системные файлы, как это следует из названий переменных.
def testFunc1():
    use_debugger = False#True
    hide_system_files = True#False

# Однако для отладки я делаю так
#       onetrigg inplace work.py
# и вижу совсем другие результаты в коде
def testFunc1():
    use_debugger = True#False
    hide_system_files = False#True
#
# В общем да, идея крайне простая, менять в коде Слово1#Слово2 и не заморачиваться за конфиги
# Для деплоймента это ужасно удобно. Попробуйте. Привыкните - потом за уши не оттащить будет :)
# Код утилиты грязный, но вполне читаемый.
# * * *

# Итак приступим.
import sys

# Для парсинга файлов я по привычке использую regular expression
import re

# Типовой регекс для нахождения подобных выражений в питоне выглядит читабельным.
# Любое ненулевое число букв, символ решетки и сразу за ним еще ненулевое число букв.
# Обратите внимание, нормальные люди всегда используют пробел перед символом комментария.
# Таким образом перепутать интересный нам токен с простым коментарием практически невозможно.
SE_TOKEN = re.compile("\w+#\w+", re.L | re.UNICODE)

# Функция инвертирует токен вида Слово1#Солво2 вокруг символа решетки
def rotate_token(token):
    # Если в к токене отсутствует, то выбрасывается исключение SyntaxError.
    # Однако я не знаю каким образом это может произойти вообще.
    if '#' not in token:
        raise SyntaxError('No hash symbol in token. But how?')
    # Я выбрал самый простой способ для любого, кто писал на лиспе.
    # Он же - самый быстрый. У вас есть более интерсные методы?
    return '#'.join(
                reversed(
                    token.split('#', 1)
                )
            )

# В этой функции мы найдем все строки, похожие на токены и попытаемся заменить их на обратный.
# Конечно функция будет генератором.
def replace_tokens(source):
    # В питоне2 строка  немутабельный тип. Т.е. по сути, в него нельзя ничего записать, он константен.
    # Поэтому первое, что надо сделать - создать копию строки в виде списка/массива.
    worce = list(source)
    for sub in SE_TOKEN.finditer(source):
        (h_from, h_to) = sub.span()
        worce[h_from:h_to] = rotate_token(source[h_from:h_to])
    # Самый быстрый способ вернуть массив обратно к строке действительно такой. Я проверял.
    return ''.join(worce)

# Обратите внимание, функция возвращает -1
def help():
    sys.stderr.write('onetrigg [inplace] file1 file2 file3...\n')
    return -1

# Это основной цикл программы. Так как я очень ленивый - для парсинга аргументов не исользуется optparse,
# потому что мой собственный способ задавать опции мне нравится больше.
def main(args):
    options = []
    # Если аргументы не заданы - показываем хелп и завершаем работу
    if not args:
        return help()
    # Если первым аргументом стоит слово inplace, то исправления будут внесены прямо в файл.
    if args[0] == 'inplace':
        options.append(args[0])
        args = args[1:]
    # Просмотрим все аргументы...
    for arg in args:
        sys.stderr.write('processing %s...' % arg)
        with open(arg, 'r') as fread:
            result = replace_tokens(fread.read())
        # Если в списке опций присутствует аргумент inplace - заменим содержимое файла исправленным вариантом.
        if 'inplace' in options:
            sys.stderr.write('inplace ')
            with open(arg, 'w') as fwrite:
                fwrite.write(result)
        else:
            sys.stdout.write(result)
        sys.stderr.write(' done\n')
    # Чтобы обеспечить нулевой код возвращения из программы, функция возвращает 0
    return 0

if __name__ == '__main__':
    sys.exit(
        main(sys.argv[1:])
    )
# Вот и всё, ребята. Увидимся.
