<commit_before>#!/usr/bin/env python3
# coding: utf-8
from Rules import *

########################
### Initialize rules ###
########################
# Currently hardcoded for DE language
rules = [
    #Coordinate separated by comma instead of |
    SimpleRegexRule("Comma in integral coordinate", r"\$\(\d+\s*\,\s*\d+\)\$"),
    SimpleRegexRule("Comma in non-integral coordinate", r"\$\(\d+[\.,]\d+\s*\,\s*\d+[\.,]\d+\)\$"),
    SimpleRegexRule("{\ } instead of {\,} inside number", r"\d+\{\\\s+\}\d+"),
    #The most simple case of using a decimal point instead
    SimpleRegexRule("Simple number with decimal point instead of comma", r"\$-?\s*\d+\.-?\d+\s*\$"),
    #Simple currency value in dollar (matches comma separated and decimal point)
    SimpleRegexRule("Value with embedded dollar symbol", r"\$\s*\\\\\$\s*\d+([.,]\d+)?\s*\$"),
    #Errors in thousands separation
    SimpleRegexRule("Value with multiple or mixed commata or dots", r"(\d+(\.|\{,\})){2,}\d+"), #Should be space. Comma without {} ignored.
    #Dollar not embedded as a symbol 234$ dollar
    SimpleRegexRule("Value suffixed by dollar", r"\d+\$\s*dollars?"),
    # Translator missed english-only world
    SimpleRegexRule("Occurrence of untranslated 'year'", r"(?<!%\()[Yy]ear(?!\)s)"), # These are lookbehind/lookhead assertions ;-)
    SimpleRegexRule("Occurrence of untranslated 'time'", r"\s+[tT]imes?(?![A-Za-z])"),
    SimpleRegexRule("Occurrence of untranslated 'is'", r"\b[Ii]s\b"),
    SimpleRegexRule("Occurrence of untranslated 'and'", r"\b[A]nd\b"),
    SimpleRegexRule("Occurrence of untranslated 'great(er)'", r"\b[Gg]reat(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'less'", r"\b[L]ess\b"),
    SimpleRegexRule("Occurrence of untranslated 'few(er)'", r"\b[Ff]ew(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'equal(s)'", r"\b[Ee]quals?\b"),
    SimpleRegexRule("Occurrence of untranslated 'equivalent", r"\b[Ee]quivalent\b"),
    SimpleRegexRule("Occurrence of untranslated 'piece(s)", r"\b[Pp]ieces?\b"),
    SimpleRegexRule("Occurrence of untranslated 'percent'", r"\b[Pp]ercents?\b"),
    SimpleRegexRule("Occurrence of untranslated 'pair'", r"\b[Pp]airs?\b"),
    SimpleRegexRule("Occurrence of untranslated 'has/had'", r"\b[Hh]a[sd]\b"),
    IgnoreByFilenameListWrapper(["de/4_low_priority/about.team.pot"],
            SimpleRegexRule("Occurrence of untranslated 'school'", r"(?<!Old-)(?<!High[- ])(?<!World[- ])\b[S]chool\b")),
    SimpleRegexRule("Occurrence of untranslated 'shade(d)'", r"(\b|-)[Ss]haded?\b"),
    SimpleRegexRule("Occurrence of untranslated 'to'", r"(?<!\\)\b[Tt]o\b"),
    SimpleRegexRule("Occurrence of untranslated 'not'", r"(?<!\\)(?<!in)\s*\bnot\b"),
    SimpleRegexRule("Occurrence of untranslated 'does", r"(?<!\\)\b[Dd]oesn?\b"),
    SimpleRegexRule("Occurrence of untranslated 'axis'", r"\b[Aa]xis\b"),
    SimpleRegexRule("Occurrence of untranslated 'multiply'", r"\b[Mm]ultiply\b"),
    SimpleRegexRule("Occurrence of untranslated 'multiplier'", r"\b[Mm]ultiplier\b"),
    SimpleRegexRule("Occurrence of untranslated 'since'", r"\b[Ss]ince\b"),
    SimpleRegexRule("Occurrence of untranslated 'value'", r"(?<!%\()\b[Vv]alues?\b(?!\)s)"),
    SimpleRegexRule("Occurrence of untranslated 'numerator'", r"\b[Nn]umerator\b"),
    SimpleRegexRule("Occurrence of untranslated 'denominator'", r"\b[Dd]enominator\b"),
    SimpleRegexRule("Occurrence of untranslated 'diameter'", r"\b[Dd]iameter\b"),
    SimpleRegexRule("Occurrence of untranslated 'perimeter'", r"(?<!SHAPE\.)\b[Pp]erimeter\b"),
    SimpleRegexRule("Occurrence of untranslated 'measurement'", r"\b[Mm]easurement\b"),
    SimpleRegexRule("Occurrence of untranslated 'marking'", r"\b[Mm]arkings?\b"),
    SimpleRegexRule("Occurrence of untranslated 'low(er)'", r"\b[Ll]ow(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'mass'", r"\b[Mm]ass\b"),
    SimpleRegexRule("Occurrence of untranslated 'high(er)'", r"\b[Hh]igh(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'Its'", r"\b[Dd]It'?s\b"),
    SimpleRegexRule("Occurrence of untranslated 'dot(s)", r"\b[Dd]dots?\b"),
    SimpleRegexRule("Occurrence of untranslated '(counter)clockwise", r"\b([Cc]ounter)?-?[Cc]clockwise\b"),
    SimpleRegexRule("Occurrence of untranslated 'inverse' (case-sensitive)", r"\binverse\b"),
    SimpleRegexRule("Occurrence of untranslated 'blue' (not as color specifier)", r"(?<!\\)\b[Bb]lue\b"),
    SimpleRegexRule("Occurrence of untranslated 'purple' (not as color specifier)", r"(?<!\\)\b[Pp]urple\b"),
    SimpleRegexRule("Occurrence of untranslated 'red' (not as color specifier)", r"(?<!\\)\b[Rr]ed\b"),
    IgnoreByMsgidRegexWrapper(r"", SimpleRegexRule("Occurrence of untranslated 'of'", r"\b[Oo]f\b")),
    IgnoreByMsgidRegexWrapper(r"(Summer|Hour|Art|Lots)\s+of\s+(Code|Scripting|Webpage|Databases|Problem|Fun)",
        SimpleRegexRule("Occurrence of untranslated 'green' (not as color specifier)",
            r"(?<!\\)\b[Gg]reen\b")),
    SimpleRegexRule("Occurrence of untranslated 'pink' (not as color specifier)", r"(?<!\\)\b[Pp]ink\b"),
    SimpleRegexRule("Occurrence of dollar as string", r"(?<!US-)[Dd]ollars?(?!ville)"), #US-Dollars? & Dollarville allowed
    SimpleSubstringRule("Escaped dollar symbol", r"\\+$"),
    SimpleRegexRule("'Sie' instead of 'Du'", r"\bSie\b"),
    SimpleRegexRule("'Ihre' instead of 'Deine'", r"\bIhre[rms]?\b"),
    #Recommended translations
    TranslationConstraintRule("'word problems' not translated to 'Textaufgaben'", r"word\s+problem", r"textaufgabe", flags=re.UNICODE | re.IGNORECASE),
    TranslationConstraintRule("'Coordinate Plane' not translated to 'Koordinatensystem'", r"coordinate\s+plane", r"Koordinatensystem", flags=re.UNICODE | re.IGNORECASE),
    TranslationConstraintRule("'Inequality' not translated to 'Ungleichung'", r"Inequality", r"Ungleichung", flags=re.UNICODE | re.IGNORECASE),
    TranslationConstraintRule("'inverse function' not translated to 'Umkehrfunktion'", r"inverse\s+function", r"Umkehrfunktion", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'shaded' translated to 'schraffiert' instead of 'eingefärbt'", r"shaded", r"schraffiert", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'shaded' translated to 'schattiert' instead of 'eingefärbt'", r"shaded", r"schattiert", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'scientific notation' translated to 'wissenschaftliche Schreibweise' instead of 'Exponentialschreibweise'", r"scientific\s+notation", r"wissenschaftliche\s+schreibweise", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'Coach' translated to 'Trainer' instead of 'Coach'", r"coach", r"trainer", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'Challenge' translated to 'Herausforderung' instead of 'challenge'", r"challenge", r"herausforderung", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'Post' translated to 'Post' instead of 'Beitrag'", r"post(?!card)", r"post(?!karte)(?!amt)", flags=re.UNICODE | re.IGNORECASE),
    # E-Mail must be written exactly "E-Mail". Exceptions: {{email}}, %(error_email), %(email), {{ email }}
    SimpleRegexRule("Wrong syntax of E-Mail", r"(?<!%\()(?<!%\(error_)(?<!\{\{)(?<!\{\{ )(eMail|email|Email|EMail|e-Mail|e-mail)s?"),
    #Bing issues
    SimpleRegexRule("Bing (1)", r"!\[\]\s+\("),
    SimpleRegexRule("Bing (2)", r"!\s+\[\]\("),
    NegativeTranslationConstraintRule("False Bing translation of interactive-graphic", r"☃\s+interactive-graph", r"[Ii]nteraktive\s+Grafik"),
    SimpleRegexRule("False Bing translation of Radio", r"Radio\b"),
    SimpleRegexRule("False Bing translation of input-number", r"[Ee]ingabe-Zahl"),
    SimpleRegexRule("False Bing translation of input-number", r"[Ee]ingabe-Nummer"),
    SimpleRegexRule("False Bing translation of numeric-input", r"[Nn]umerische[-\s]+Eingabe"),
    SimpleRegexRule("False Bing translation of numeric-input", r"[Nn]umerische[-\s]+Eingang"),
    SimpleRegexRule("False Bing translation of image", r"☃\s+Bild"),
    SimpleRegexRule("Missing translation of **How", r"\*\*[Hh]ow"),
    SimpleRegexRule("Missing translation of **What", r"\*\*[Ww]hat"),
    SimpleRegexRule("Missing translation of ones", r"\\text\{\s*ones\}\}"),
    SimpleRegexRule("Missing translation of ten(s)", r"\\text\{\s*tens?\}\}"),
    SimpleRegexRule("Missing translation of hundred(s)", r"\\text\{\s*hundreds?\}\}"),
    # Machine-readable stuff must be identical in the translation
    ExactCopyRule("All image URLs must match in order", r"!\[\]\s*\([^\)]+\)"),
    ExactCopyRule("All GUI elements must match in order", r"\[\[☃\s+[a-z-]+\s*\d*\]\]"),
    # Unsorted rules
]

if __name__ == "__main__":
    #Rule tests. python3 Rules.py to run
    assert(findRule(rules, "Comma in integral coordinate")("$(12,3)$", ""))
    assert(findRule(rules, "Simple number with decimal point instead of comma")("$12.3$", ""))
    assert(findRule(rules, "Value with embedded dollar symbol")("$\\\\$12$", ""))
    assert(findRule(rules, "Value with embedded dollar symbol")("$\\\\$12.5$", ""))
    assert(findRule(rules, "Value with embedded dollar symbol")("$\\\\$12,5$", ""))
    assert(findRule(rules, "Value suffixed by dollar")("$1,234$ dollar", ""))
<commit_msg>Allow posten & post inside word<commit_after>#!/usr/bin/env python3
# coding: utf-8
from Rules import *

########################
### Initialize rules ###
########################
# Currently hardcoded for DE language
rules = [
    #Coordinate separated by comma instead of |
    SimpleRegexRule("Comma in integral coordinate", r"\$\(\d+\s*\,\s*\d+\)\$"),
    SimpleRegexRule("Comma in non-integral coordinate", r"\$\(\d+[\.,]\d+\s*\,\s*\d+[\.,]\d+\)\$"),
    SimpleRegexRule("{\ } instead of {\,} inside number", r"\d+\{\\\s+\}\d+"),
    #The most simple case of using a decimal point instead
    SimpleRegexRule("Simple number with decimal point instead of comma", r"\$-?\s*\d+\.-?\d+\s*\$"),
    #Simple currency value in dollar (matches comma separated and decimal point)
    SimpleRegexRule("Value with embedded dollar symbol", r"\$\s*\\\\\$\s*\d+([.,]\d+)?\s*\$"),
    #Errors in thousands separation
    SimpleRegexRule("Value with multiple or mixed commata or dots", r"(\d+(\.|\{,\})){2,}\d+"), #Should be space. Comma without {} ignored.
    #Dollar not embedded as a symbol 234$ dollar
    SimpleRegexRule("Value suffixed by dollar", r"\d+\$\s*dollars?"),
    # Translator missed english-only world
    SimpleRegexRule("Occurrence of untranslated 'year'", r"(?<!%\()[Yy]ear(?!\)s)"), # These are lookbehind/lookhead assertions ;-)
    SimpleRegexRule("Occurrence of untranslated 'time'", r"\s+[tT]imes?(?![A-Za-z])"),
    SimpleRegexRule("Occurrence of untranslated 'is'", r"\b[Ii]s\b"),
    SimpleRegexRule("Occurrence of untranslated 'and'", r"\b[A]nd\b"),
    SimpleRegexRule("Occurrence of untranslated 'great(er)'", r"\b[Gg]reat(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'less'", r"\b[L]ess\b"),
    SimpleRegexRule("Occurrence of untranslated 'few(er)'", r"\b[Ff]ew(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'equal(s)'", r"\b[Ee]quals?\b"),
    SimpleRegexRule("Occurrence of untranslated 'equivalent", r"\b[Ee]quivalent\b"),
    SimpleRegexRule("Occurrence of untranslated 'piece(s)", r"\b[Pp]ieces?\b"),
    SimpleRegexRule("Occurrence of untranslated 'percent'", r"\b[Pp]ercents?\b"),
    SimpleRegexRule("Occurrence of untranslated 'pair'", r"\b[Pp]airs?\b"),
    SimpleRegexRule("Occurrence of untranslated 'has/had'", r"\b[Hh]a[sd]\b"),
    IgnoreByFilenameListWrapper(["de/4_low_priority/about.team.pot"],
            SimpleRegexRule("Occurrence of untranslated 'school'", r"(?<!Old-)(?<!High[- ])(?<!World[- ])\b[S]chool\b")),
    SimpleRegexRule("Occurrence of untranslated 'shade(d)'", r"(\b|-)[Ss]haded?\b"),
    SimpleRegexRule("Occurrence of untranslated 'to'", r"(?<!\\)\b[Tt]o\b"),
    SimpleRegexRule("Occurrence of untranslated 'not'", r"(?<!\\)(?<!in)\s*\bnot\b"),
    SimpleRegexRule("Occurrence of untranslated 'does", r"(?<!\\)\b[Dd]oesn?\b"),
    SimpleRegexRule("Occurrence of untranslated 'axis'", r"\b[Aa]xis\b"),
    SimpleRegexRule("Occurrence of untranslated 'multiply'", r"\b[Mm]ultiply\b"),
    SimpleRegexRule("Occurrence of untranslated 'multiplier'", r"\b[Mm]ultiplier\b"),
    SimpleRegexRule("Occurrence of untranslated 'since'", r"\b[Ss]ince\b"),
    SimpleRegexRule("Occurrence of untranslated 'value'", r"(?<!%\()\b[Vv]alues?\b(?!\)s)"),
    SimpleRegexRule("Occurrence of untranslated 'numerator'", r"\b[Nn]umerator\b"),
    SimpleRegexRule("Occurrence of untranslated 'denominator'", r"\b[Dd]enominator\b"),
    SimpleRegexRule("Occurrence of untranslated 'diameter'", r"\b[Dd]iameter\b"),
    SimpleRegexRule("Occurrence of untranslated 'perimeter'", r"(?<!SHAPE\.)\b[Pp]erimeter\b"),
    SimpleRegexRule("Occurrence of untranslated 'measurement'", r"\b[Mm]easurement\b"),
    SimpleRegexRule("Occurrence of untranslated 'marking'", r"\b[Mm]arkings?\b"),
    SimpleRegexRule("Occurrence of untranslated 'low(er)'", r"\b[Ll]ow(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'mass'", r"\b[Mm]ass\b"),
    SimpleRegexRule("Occurrence of untranslated 'high(er)'", r"\b[Hh]igh(er)?\b"),
    SimpleRegexRule("Occurrence of untranslated 'Its'", r"\b[Dd]It'?s\b"),
    SimpleRegexRule("Occurrence of untranslated 'dot(s)", r"\b[Dd]dots?\b"),
    SimpleRegexRule("Occurrence of untranslated '(counter)clockwise", r"\b([Cc]ounter)?-?[Cc]clockwise\b"),
    SimpleRegexRule("Occurrence of untranslated 'inverse' (case-sensitive)", r"\binverse\b"),
    SimpleRegexRule("Occurrence of untranslated 'blue' (not as color specifier)", r"(?<!\\)\b[Bb]lue\b"),
    SimpleRegexRule("Occurrence of untranslated 'purple' (not as color specifier)", r"(?<!\\)\b[Pp]urple\b"),
    SimpleRegexRule("Occurrence of untranslated 'red' (not as color specifier)", r"(?<!\\)\b[Rr]ed\b"),
    IgnoreByMsgidRegexWrapper(r"", SimpleRegexRule("Occurrence of untranslated 'of'", r"\b[Oo]f\b")),
    IgnoreByMsgidRegexWrapper(r"(Summer|Hour|Art|Lots)\s+of\s+(Code|Scripting|Webpage|Databases|Problem|Fun)",
        SimpleRegexRule("Occurrence of untranslated 'green' (not as color specifier)",
            r"(?<!\\)\b[Gg]reen\b")),
    SimpleRegexRule("Occurrence of untranslated 'pink' (not as color specifier)", r"(?<!\\)\b[Pp]ink\b"),
    SimpleRegexRule("Occurrence of dollar as string", r"(?<!US-)[Dd]ollars?(?!ville)"), #US-Dollars? & Dollarville allowed
    SimpleSubstringRule("Escaped dollar symbol", r"\\+$"),
    SimpleRegexRule("'Sie' instead of 'Du'", r"\bSie\b"),
    SimpleRegexRule("'Ihre' instead of 'Deine'", r"\bIhre[rms]?\b"),
    #Recommended translations
    TranslationConstraintRule("'word problems' not translated to 'Textaufgaben'", r"word\s+problem", r"textaufgabe", flags=re.UNICODE | re.IGNORECASE),
    TranslationConstraintRule("'Coordinate Plane' not translated to 'Koordinatensystem'", r"coordinate\s+plane", r"Koordinatensystem", flags=re.UNICODE | re.IGNORECASE),
    TranslationConstraintRule("'Inequality' not translated to 'Ungleichung'", r"Inequality", r"Ungleichung", flags=re.UNICODE | re.IGNORECASE),
    TranslationConstraintRule("'inverse function' not translated to 'Umkehrfunktion'", r"inverse\s+function", r"Umkehrfunktion", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'shaded' translated to 'schraffiert' instead of 'eingefärbt'", r"shaded", r"schraffiert", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'shaded' translated to 'schattiert' instead of 'eingefärbt'", r"shaded", r"schattiert", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'scientific notation' translated to 'wissenschaftliche Schreibweise' instead of 'Exponentialschreibweise'", r"scientific\s+notation", r"wissenschaftliche\s+schreibweise", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'Coach' translated to 'Trainer' instead of 'Coach'", r"coach", r"trainer", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'Challenge' translated to 'Herausforderung' instead of 'challenge'", r"challenge", r"herausforderung", flags=re.UNICODE | re.IGNORECASE),
    NegativeTranslationConstraintRule("'Post' translated to 'Post' instead of 'Beitrag'", r"\bpost(?!card)", r"\bpost(?!karte)(?!amt)(?!en)", flags=re.UNICODE | re.IGNORECASE),
    # E-Mail must be written exactly "E-Mail". Exceptions: {{email}}, %(error_email), %(email), {{ email }}
    SimpleRegexRule("Wrong syntax of E-Mail", r"(?<!%\()(?<!%\(error_)(?<!\{\{)(?<!\{\{ )(eMail|email|Email|EMail|e-Mail|e-mail)s?"),
    #Bing issues
    SimpleRegexRule("Bing (1)", r"!\[\]\s+\("),
    SimpleRegexRule("Bing (2)", r"!\s+\[\]\("),
    NegativeTranslationConstraintRule("False Bing translation of interactive-graphic", r"☃\s+interactive-graph", r"[Ii]nteraktive\s+Grafik"),
    SimpleRegexRule("False Bing translation of Radio", r"Radio\b"),
    SimpleRegexRule("False Bing translation of input-number", r"[Ee]ingabe-Zahl"),
    SimpleRegexRule("False Bing translation of input-number", r"[Ee]ingabe-Nummer"),
    SimpleRegexRule("False Bing translation of numeric-input", r"[Nn]umerische[-\s]+Eingabe"),
    SimpleRegexRule("False Bing translation of numeric-input", r"[Nn]umerische[-\s]+Eingang"),
    SimpleRegexRule("False Bing translation of image", r"☃\s+Bild"),
    SimpleRegexRule("Missing translation of **How", r"\*\*[Hh]ow"),
    SimpleRegexRule("Missing translation of **What", r"\*\*[Ww]hat"),
    SimpleRegexRule("Missing translation of ones", r"\\text\{\s*ones\}\}"),
    SimpleRegexRule("Missing translation of ten(s)", r"\\text\{\s*tens?\}\}"),
    SimpleRegexRule("Missing translation of hundred(s)", r"\\text\{\s*hundreds?\}\}"),
    # Machine-readable stuff must be identical in the translation
    ExactCopyRule("All image URLs must match in order", r"!\[\]\s*\([^\)]+\)"),
    ExactCopyRule("All GUI elements must match in order", r"\[\[☃\s+[a-z-]+\s*\d*\]\]"),
    # Unsorted rules
]

if __name__ == "__main__":
    #Rule tests. python3 Rules.py to run
    assert(findRule(rules, "Comma in integral coordinate")("$(12,3)$", ""))
    assert(findRule(rules, "Simple number with decimal point instead of comma")("$12.3$", ""))
    assert(findRule(rules, "Value with embedded dollar symbol")("$\\\\$12$", ""))
    assert(findRule(rules, "Value with embedded dollar symbol")("$\\\\$12.5$", ""))
    assert(findRule(rules, "Value with embedded dollar symbol")("$\\\\$12,5$", ""))
    assert(findRule(rules, "Value suffixed by dollar")("$1,234$ dollar", ""))
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function, unicode_literals

import argparse
import copy
import os, os.path
import re
import shlex
import subprocess
import sys

from pprint import pprint, pformat

__version__ = '0.3'

def ask(question, replies, default=None):
    """ Ask question and repeat it, until answer will not be one of 'replies',
        or nothing (then default value will be used).
        Question should not contain possible answers, it's built based on
        'replies'.
        'replies' is iterable with one letter possible answers.
        'default' can be empty.
    """
    replies = { reply.lower() for reply in replies }
    if default:
        default = default.lower()
        replies.add(default)

    choices = ','.join(replies)
    if default:
        choices = choices.replace(default, default.upper())

    question += ' (' + choices + ') '
    while True:
        reply = raw_input(question).lower()
        if reply == '':
            if default:
                return default
        elif reply in replies:
            return reply

def parse_input_args(args):
    """ Parse input 'args' and return parsed.
    """
    p = argparse.ArgumentParser()

    p.add_argument('-0', '--print0', action='store_true', default=False, help='split results by binary zero instead of new line (useful to work with xargs)')
    p.add_argument('-i', '--ignorecase', '--ignore-case', action='store_true', default=False, help='')
    p.add_argument('-s', '--source', action='append', type=str, default=[], help='optional, see: source above')
    p.add_argument('-p', '--pattern', type=str, help='optional, see: pattern above')
    p.add_argument('-g', '--regexp', action='store_true', default=False, help='treat pattern as regular expression (uses Python regexp engine)')
    p.add_argument('-l', '--regex-multiline', action='store_true', default=False, help='')
    p.add_argument('-d', '--regex-dotall', action='store_true', default=False, help='')
    p.add_argument('-B', '--begin', dest='fnmatch_begin', action='store_true', default=False, help='match pattern to begin of item name (ignored in regexp mode)')
    p.add_argument('-E', '--end', dest='fnmatch_end', action='store_true', default=False, help='match pattern to end of item name (ignored in regexp mode)')
    p.add_argument('-v', '--invert-match', action='store_true', default=False, help='')
    p.add_argument('-m', '--mode', choices=('all', 'files', 'dirs'), default='all', help='')
    p.add_argument('-x', '--exec', metavar='COMMAND', dest='execute', type=str, help='execute some command on every found item. In command, placeholders: {path}, {dirname}, {basename} are replaced with correct value')
    p.add_argument('--prefix', action='store_true', default=False, help='add prefix "d: " (directory) or "f: " (file) to every found item')
    p.add_argument('--no-display', dest='display', action='store_false', default=True, help='don\'t display element (useful with --exec argument)')
    p.add_argument('--verbose-exec', action='store_true', default=False, help='show command before execute it')
    p.add_argument('--interactive-exec', action='store_true', default=False, help='ask before execute command on every item')
    p.add_argument('--shell-exec', action='store_true', default=False, help='execute command from --exec argument in shell (with shell expansion etc)')
    p.add_argument('--vcs', action='store_true', default=False, help='do not skip VCS directories (.git, .svn etc)')
    p.add_argument('-c', '--exclude-path', metavar='EXCLUDED_PATH', dest='excluded_paths', action='append', type=str, default=[], help='skip given paths from scanning')
    p.add_argument('anon_pattern', metavar='pattern', type=str, nargs='?', help='pattern to search')
    p.add_argument('anon_sources', metavar='sources', type=str, nargs='*', help='optional source (if missing, use current directory)')

    args = p.parse_args()

    if args.pattern is None:
        args.pattern = args.anon_pattern
    else:
        args.anon_sources.insert(0, args.anon_pattern)

    if args.pattern is None:
        raise p.error('argument -p/--pattern is required')

    args.source += args.anon_sources;
    if not args.source:
        args.source.append('.')

    for i, src in enumerate(args.source):
        if not os.path.isdir(src):
            p.error('Source %s doesn\'t exists or is not a directory' % src)
        args.source[i] = os.path.abspath(src)

    if args.shell_exec:
        args.execute = [args.execute]
    elif args.execute:
        args.execute = shlex.split(args.execute)

    for i, exc in enumerate(args.excluded_paths):
        args.excluded_paths[i] = os.path.abspath(exc).rstrip('/')

    if args.print0:
        args.delim = chr(0)
    else:
        args.delim = "\n"

    return args

def _prepare_execute__vars(m):
    """ Helper method for prepare_execute, used in replacement of regular expression.
        Returns environment variable if found, and quantity of escape characters ('\')
        is even.
    """
    if len(m.group(1)) % 2 == 0:
        return os.environ.get(m.group(2), '')
    else:
        return m.group(0)

def prepare_execute(exe, path, dirname, basename, expand_vars=True):
    """ Replace keywords and env variables in 'exe' with values.
        Recognized keywords:
        {path} - full file path
        {dirname} - parent directory for file
        {basename} - filename without path
    """

    exe = copy.copy(exe)
    rxp_var = re.compile(r'(\\*)\$([_a-zA-Z0-9]+)')
    for i, elem in enumerate(exe):
        exe[i] = exe[i].replace('{path}', path)
        exe[i] = exe[i].replace('{dirname}', dirname)
        exe[i] = exe[i].replace('{basename}', basename)
        if expand_vars:
            exe[i] = rxp_var.sub(_prepare_execute__vars, exe[i])

    return exe

def prepare_pattern(cfg):
    """ Prepare pattern from input args to use.

        If work in regex mode, there pattern is only compiled with flags. In normal mode,
        pattern is converted to regex, and then compiled.

        Returns always compiled regexp, ready to use.
    """
    pattern = cfg.pattern
    flags = 0

    if cfg.regexp:
        if cfg.ignorecase:
            flags = flags | re.IGNORECASE
        if cfg.regex_dotall:
            flags = flags | re.DOTALL
        if cfg.regex_multiline:
            flags = flags | re.MULTILINE
    else:
        import fnmatch

        if cfg.ignorecase:
            flags = flags | re.IGNORECASE

        pattern = fnmatch.translate(pattern)
        if cfg.fnmatch_begin:
            pattern = r'\A' + pattern

        if not cfg.fnmatch_end:
            pattern = re.sub(r'\\Z (?: \( [^)]+ \) )? $', '', pattern, flags=re.VERBOSE)

    pattern = re.compile(pattern, flags)
    return pattern

def process_item(cfg, path):
    """ Test path for matching with pattern, print it if so, and execute command if given.
    """
    m = cfg.pattern.search(os.path.basename(path))
    if (not cfg.invert_match and m) or (cfg.invert_match and not m):
        if cfg.display:
            if not cfg.prefix:
                prefix = ''
            elif os.path.isdir(path):
                prefix = 'd: '
            else:
                prefix = 'f: '
            print(prefix, path, sep='', end=cfg.delim)
        if cfg.execute:
            exe = prepare_execute(cfg.execute, path, os.path.dirname(path), os.path.basename(path), not cfg.shell_exec)
            if cfg.verbose_exec:
                print(' '.join(exe))
            if not cfg.interactive_exec or ask('Execute command on %s?' % path, 'yn', 'n') == 'y':
                subprocess.call(exe, shell=cfg.shell_exec)

def is_path_excluded(excluded_paths, path):
    path = path.rstrip('/')
    for exc in excluded_paths:
        if path == exc or exc + '/' in path:
            return True
    return False

def main():
    try:
        config = parse_input_args(sys.argv[1:])
    except argparse.ArgumentError:
        print(e, file=sys.stderr)
        sys.exit(1)

    config.pattern = prepare_pattern(config)

    rxp_vcs = re.compile('(?:^|/)(?:\.git|\.svn|\.CVS|\.hg|_MTN|CVS|RCS|SCCS|_darcs|_sgbak)(?:$|/)')

    for source in config.source:
        for root, dirs, files in os.walk(source):
            if is_path_excluded(config.excluded_paths, root):
                continue

            if config.mode in ('dirs', 'all'):
                if config.vcs or not rxp_vcs.search(root):
                    process_item(config, root)

            if config.mode in ('files', 'all'):
                for file_ in files:
                    path = os.path.join(root, file_)
                    if is_path_excluded(config.excluded_paths, path):
                        continue
                    if config.vcs or not rxp_vcs.search(path):
                        process_item(config, path)

if __name__ == '__main__':
    main()
<commit_msg>added fuzz mode<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function, unicode_literals

import argparse
import copy
import os, os.path
import re
import shlex
import subprocess
import sys

from pprint import pprint, pformat

__version__ = '0.3'

def ask(question, replies, default=None):
    """ Ask question and repeat it, until answer will not be one of 'replies',
        or nothing (then default value will be used).
        Question should not contain possible answers, it's built based on
        'replies'.
        'replies' is iterable with one letter possible answers.
        'default' can be empty.
    """
    replies = { reply.lower() for reply in replies }
    if default:
        default = default.lower()
        replies.add(default)

    choices = ','.join(replies)
    if default:
        choices = choices.replace(default, default.upper())

    question += ' (' + choices + ') '
    while True:
        reply = raw_input(question).lower()
        if reply == '':
            if default:
                return default
        elif reply in replies:
            return reply

def parse_input_args(args):
    """ Parse input 'args' and return parsed.
    """
    p = argparse.ArgumentParser()

    p.add_argument('-0', '--print0', action='store_true', default=False, help='split results by binary zero instead of new line (useful to work with xargs)')
    p.add_argument('-i', '--ignorecase', '--ignore-case', action='store_true', default=False, help='')
    p.add_argument('-s', '--source', action='append', type=str, default=[], help='optional, see: source above')
    p.add_argument('-p', '--pattern', type=str, help='optional, see: pattern above')
    p.add_argument('-g', '--regexp', action='store_true', default=False, help='treat pattern as regular expression (uses Python regexp engine)')
    p.add_argument('-f', '--fuzz', action='store_true', default=False, help='pattern defines only set and order of characters used in filename')
    p.add_argument('-l', '--regex-multiline', action='store_true', default=False, help='')
    p.add_argument('-d', '--regex-dotall', action='store_true', default=False, help='')
    p.add_argument('-B', '--begin', dest='fnmatch_begin', action='store_true', default=False, help='match pattern to begin of item name (ignored in regexp mode)')
    p.add_argument('-E', '--end', dest='fnmatch_end', action='store_true', default=False, help='match pattern to end of item name (ignored in regexp mode)')
    p.add_argument('-v', '--invert-match', action='store_true', default=False, help='')
    p.add_argument('-m', '--mode', choices=('all', 'files', 'dirs'), default='all', help='')
    p.add_argument('-x', '--exec', metavar='COMMAND', dest='execute', type=str, help='execute some command on every found item. In command, placeholders: {path}, {dirname}, {basename} are replaced with correct value')
    p.add_argument('--prefix', action='store_true', default=False, help='add prefix "d: " (directory) or "f: " (file) to every found item')
    p.add_argument('--no-display', dest='display', action='store_false', default=True, help='don\'t display element (useful with --exec argument)')
    p.add_argument('--verbose-exec', action='store_true', default=False, help='show command before execute it')
    p.add_argument('--interactive-exec', action='store_true', default=False, help='ask before execute command on every item')
    p.add_argument('--shell-exec', action='store_true', default=False, help='execute command from --exec argument in shell (with shell expansion etc)')
    p.add_argument('--vcs', action='store_true', default=False, help='do not skip VCS directories (.git, .svn etc)')
    p.add_argument('-c', '--exclude-path', metavar='EXCLUDED_PATH', dest='excluded_paths', action='append', type=str, default=[], help='skip given paths from scanning')
    p.add_argument('anon_pattern', metavar='pattern', type=str, nargs='?', help='pattern to search')
    p.add_argument('anon_sources', metavar='sources', type=str, nargs='*', help='optional source (if missing, use current directory)')

    args = p.parse_args()

    if args.pattern is None:
        args.pattern = args.anon_pattern
    else:
        args.anon_sources.insert(0, args.anon_pattern)

    if args.pattern is None:
        raise p.error('argument -p/--pattern is required')

    args.source += args.anon_sources;
    if not args.source:
        args.source.append('.')

    for i, src in enumerate(args.source):
        if not os.path.isdir(src):
            p.error('Source %s doesn\'t exists or is not a directory' % src)
        args.source[i] = os.path.abspath(src)

    if args.shell_exec:
        args.execute = [args.execute]
    elif args.execute:
        args.execute = shlex.split(args.execute)

    for i, exc in enumerate(args.excluded_paths):
        args.excluded_paths[i] = os.path.abspath(exc).rstrip('/')

    if args.print0:
        args.delim = chr(0)
    else:
        args.delim = "\n"

    return args

def _prepare_execute__vars(m):
    """ Helper method for prepare_execute, used in replacement of regular expression.
        Returns environment variable if found, and quantity of escape characters ('\')
        is even.
    """
    if len(m.group(1)) % 2 == 0:
        return os.environ.get(m.group(2), '')
    else:
        return m.group(0)

def prepare_execute(exe, path, dirname, basename, expand_vars=True):
    """ Replace keywords and env variables in 'exe' with values.
        Recognized keywords:
        {path} - full file path
        {dirname} - parent directory for file
        {basename} - filename without path
    """

    exe = copy.copy(exe)
    rxp_var = re.compile(r'(\\*)\$([_a-zA-Z0-9]+)')
    for i, elem in enumerate(exe):
        exe[i] = exe[i].replace('{path}', path)
        exe[i] = exe[i].replace('{dirname}', dirname)
        exe[i] = exe[i].replace('{basename}', basename)
        if expand_vars:
            exe[i] = rxp_var.sub(_prepare_execute__vars, exe[i])

    return exe

def prepare_pattern(cfg):
    """ Prepare pattern from input args to use.

        If work in regex mode, there pattern is only compiled with flags. In normal mode,
        pattern is converted to regex, and then compiled. Recognize also fuzz mode.

        Returns always compiled regexp, ready to use.
    """
    pattern = cfg.pattern
    flags = 0

    if cfg.fuzz:
        new_pattern = ''
        if cfg.fnmatch_begin:
            new_pattern += '^'
        for char in pattern:
            new_pattern += '.*' + re.escape(char)
        if cfg.fnmatch_end:
            new_pattern += '$'

        flags = flags | re.DOTALL | re.MULTILINE
        if cfg.ignorecase:
            flags = flags | re.IGNORECASE

        pattern = new_pattern

    elif cfg.regexp:
        if cfg.ignorecase:
            flags = flags | re.IGNORECASE
        if cfg.regex_dotall:
            flags = flags | re.DOTALL
        if cfg.regex_multiline:
            flags = flags | re.MULTILINE
    else:
        import fnmatch

        if cfg.ignorecase:
            flags = flags | re.IGNORECASE

        pattern = fnmatch.translate(pattern)
        if cfg.fnmatch_begin:
            pattern = r'\A' + pattern

        if not cfg.fnmatch_end:
            pattern = re.sub(r'\\Z (?: \( [^)]+ \) )? $', '', pattern, flags=re.VERBOSE)

    pattern = re.compile(pattern, flags)
    return pattern

def process_item(cfg, path):
    """ Test path for matching with pattern, print it if so, and execute command if given.
    """
    m = cfg.pattern.search(os.path.basename(path))
    if (not cfg.invert_match and m) or (cfg.invert_match and not m):
        if cfg.display:
            if not cfg.prefix:
                prefix = ''
            elif os.path.isdir(path):
                prefix = 'd: '
            else:
                prefix = 'f: '
            print(prefix, path, sep='', end=cfg.delim)
        if cfg.execute:
            exe = prepare_execute(cfg.execute, path, os.path.dirname(path), os.path.basename(path), not cfg.shell_exec)
            if cfg.verbose_exec:
                print(' '.join(exe))
            if not cfg.interactive_exec or ask('Execute command on %s?' % path, 'yn', 'n') == 'y':
                subprocess.call(exe, shell=cfg.shell_exec)

def is_path_excluded(excluded_paths, path):
    path = path.rstrip('/')
    for exc in excluded_paths:
        if path == exc or exc + '/' in path:
            return True
    return False

def main():
    try:
        config = parse_input_args(sys.argv[1:])
    except argparse.ArgumentError:
        print(e, file=sys.stderr)
        sys.exit(1)

    config.pattern = prepare_pattern(config)

    rxp_vcs = re.compile('(?:^|/)(?:\.git|\.svn|\.CVS|\.hg|_MTN|CVS|RCS|SCCS|_darcs|_sgbak)(?:$|/)')

    for source in config.source:
        for root, dirs, files in os.walk(source):
            if is_path_excluded(config.excluded_paths, root):
                continue

            if config.mode in ('dirs', 'all'):
                if config.vcs or not rxp_vcs.search(root):
                    process_item(config, root)

            if config.mode in ('files', 'all'):
                for file_ in files:
                    path = os.path.join(root, file_)
                    if is_path_excluded(config.excluded_paths, path):
                        continue
                    if config.vcs or not rxp_vcs.search(path):
                        process_item(config, path)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/python

import ast

class Macros:
    def __init__(self, hp):
        self.hp = hp

    def p(self, php):
        return self.hp._php(php, self)

    def _concat(self, *args):
        return ' . '.join([self.p(a) for a in args])

    def _const(self, constant):
        return constant.id

    def _append(self, target, value):
        return self.p(target) + '[] = ' + self.p(value)

class Actions:
    special_names = {
            'False': 'false',
            'None': 'null',
            'True': 'true'
            }

    ##########################################################################

    def __init__(self, hp, parent):
        self.hp = hp
        self.parent = parent
        self.output = []
        self.statement_context = False

    def p(self, php):
        return self.hp._php(php, self)

    def _get_context(self):
        if self.statement_context:
            return self
        return self.parent._get_context()

    ## Module ################################################################

    def Module(self, a):
        self.statement_context = True
        return self.statements(a.body)

    ## Statements ############################################################

    def statements(self, s):
        self.statement_context = True
        for b in s:
            if type(b) == str:
                self.output.append(b)
            else:
                self.output.append(self.p(b))
        return ';\n'.join(self.output + [''])

    def Assign(self, a):
        if type(a.targets[0]) == ast.Tuple:

            assign = ast.Assign()
            target = ast.Name()
            target.id = '__pyhp_array__'
            assign.targets = [target]
            assign.value = a.value

            assignments = [assign]

            targets = a.targets[0].elts

            n = 0
            for t in targets:
                assign = ast.Assign()
                assign.targets = [t]
                v = ast.Call()
                v.func = ast.Name()
                v.func.id = 'array_shift'
                v.args = [target]
                assign.value = v
                assignments.append(assign)
                n += 1

            return self.statements(assignments)

        else:
            return self.p(a.targets[0]) + ' = ' + self.p(a.value)

    def Expr(self, a):
        return self.p(a.value)

    def For(self, a):
        return 'foreach ( ' + \
                self.p(a.iter) +  ' as ' +  self.p(a.target) + \
                ') {\n' + \
                self.statements(a.body) + \
                '}'

    def If(self, a):
        if a.orelse == []:
            orelse = ''
        elif a.orelse[0].__class__ is ast.If:
            orelse = ' else' + self.p(a.orelse[0])
        else:
            orelse = ' else {\n' + self.p(a.orelse[0]) + '}'

        return 'if ( ' + self.p(a.test) + ' ) {\n' + \
                self.statements(a.body) + '}' + orelse

    ## Expressions ###########################################################

    def Call(self, a):
        m = self.hp.macros(self.hp)
        if a.func.id in dir(m):
            return m.__getattribute__(a.func.id)(*a.args)
        else:
            return a.func.id + '( ' + \
                    ', '.join([self.p(b) for b in a.args]) + ' )'

    def Name(self, a):
        if a.id in self.special_names:
            return self.special_names[a.id]
        return '$'+a.id

    def Subscript(self, a):
        return self.p(a.value) + '[' + self.p(a.slice) + ']'

    def Index(self, a):
        return self.p(a.value)

    def ListComp(self, a):
        g = a.generators[0]

        trees = []

        assign = ast.Assign()
        target = ast.Name()
        target.id = '__pyhp_array__'
        assign.targets = [target]
        assign.value = ast.List()
        assign.value.elts = []
        trees.append(assign)

        for_ = ast.For()
        for_.iter = g.iter
        for_.target = g.target
        for_.body = [ast.Call()]
        for_.body[0].func = ast.Name
        for_.body[0].func.id = '_append'
        for_.body[0].args = [target, a.elt]
        trees.append(for_)

        self._get_context().output.append(self.statements(trees))
        return self.p(target)

    ## Operators #############################################################

    ## UnaryOp

    def UnaryOp(self, a):
        return self.p(a.op) + ' ' + self.p(a.operand)

    def Not(self, a):
        return '!'

    ## BinOp

    def BinOp(self, a):
        if type(a.op) == ast.Pow:
            return 'pow(' + self.p(a.left) + ', ' + self.p(a.right) + ')'
        return self.p(a.left) + ' ' + self.p(a.op) + ' ' + self.p(a.right)

    def Add(self, a):
        return '+'

    def Mult(self, a):
        return '*'

    ## Compare

    def Compare(self, a):
        return self.p(a.left) + \
                ' ' + self.p(a.ops[0]) + ' ' + \
                self.p(a.comparators[0])

    def Eq(self, a):
        return '==='

    def NotEq(self, a):
        return '!=='

    def Lt(self, a):
        return '<'

    def Gt(self, a):
        return '>'

    def LtE(self, a):
        return '<='

    def GtE(self, a):
        return '>='

    ## Builtin types #########################################################

    def Num(self, a):
        return str(a.n)

    def Str(self, a):
        return "'" + a.s.replace('\\','\\\\').replace("'","\\'") + "'"

    def List(self, a):
        return 'array( ' + ', '.join([self.p(e) for e in a.elts]) + ' )'

    def Tuple(self, a):
        return self.List(a)

    def Dict(self, a):
        return 'array(' + \
                ', '.join([self.p(k) + ' => ' + self.p(v)
                    for k,v in zip(a.keys, a.values)]) + \
                            ')'

    def NoneType(self, a):
        return 'null'

class HotPotato:
    def __init__(self, fn, debug=False, macros=Macros):
        self.debug = debug
        self.macros = macros

        self.ast = compile(open(fn).read(),
                fn,
                'exec',
                ast.PyCF_ONLY_AST)

    def php(self):
        return '<?php\n'+self._php(self.ast, None)

    def _php(self, a, parent):
        c = a.__class__
        actions = Actions(self, parent)
        try:
            return actions.__getattribute__(c.__name__)(a)
        except AttributeError:
            if self.debug:
                return repr(a)
            else:
                raise

class Compiler:
    def __init__(self, macros=Macros):
        self.macros = macros

    def compile(self, files):
        for f in files:
            hp = HotPotato(f, macros=self.macros)
            with open(f.replace('.py', '.php'), 'w+') as out:
                out.write(hp.php())

if __name__ == '__main__':
    import sys
    argv = sys.argv[1:]
    debug = False
    if '-d' in argv:
        argv.remove('-d')
        debug = True

    if len(argv) == 1:
        hp = HotPotato(argv[0], debug)
        print(hp.php())

    else:
        print("Usage: python -m hp FILE.py")
        exit(1)
<commit_msg>Clarity comments<commit_after>#!/usr/bin/python

import ast

class Macros:
    def __init__(self, hp):
        self.hp = hp

    def p(self, php):
        return self.hp._php(php, self)

    def _concat(self, *args):
        return ' . '.join([self.p(a) for a in args])

    def _const(self, constant):
        return constant.id

    def _append(self, target, value):
        return self.p(target) + '[] = ' + self.p(value)

class Actions:
    special_names = {
            'False': 'false',
            'None': 'null',
            'True': 'true'
            }

    ##########################################################################

    def __init__(self, hp, parent):
        self.hp = hp
        self.parent = parent
        self.output = []
        self.statement_context = False

    def p(self, php):
        return self.hp._php(php, self)

    def _get_context(self):
        if self.statement_context:
            return self
        return self.parent._get_context()

    ## Module ################################################################

    def Module(self, a):
        self.statement_context = True
        return self.statements(a.body)

    ## Statements ############################################################

    def statements(self, s):
        self.statement_context = True
        for b in s:
            if type(b) == str:
                self.output.append(b)
            else:
                self.output.append(self.p(b))
        return ';\n'.join(self.output + [''])

    def Assign(self, a):
        if type(a.targets[0]) == ast.Tuple:

            # $__pyhp_array__ = $values
            assign = ast.Assign()
            target = ast.Name()
            target.id = '__pyhp_array__'
            assign.targets = [target]
            assign.value = a.value

            assignments = [assign]

            targets = a.targets[0].elts

            for t in targets:
                # $target = $value[$n]
                assign = ast.Assign()
                assign.targets = [t]
                v = ast.Call()
                v.func = ast.Name()
                v.func.id = 'array_shift'
                v.args = [target]
                assign.value = v
                assignments.append(assign)

            return self.statements(assignments)

        else:
            return self.p(a.targets[0]) + ' = ' + self.p(a.value)

    def Expr(self, a):
        return self.p(a.value)

    def For(self, a):
        return 'foreach ( ' + \
                self.p(a.iter) +  ' as ' +  self.p(a.target) + \
                ') {\n' + \
                self.statements(a.body) + \
                '}'

    def If(self, a):
        if a.orelse == []:
            orelse = ''
        elif a.orelse[0].__class__ is ast.If:
            orelse = ' else' + self.p(a.orelse[0])
        else:
            orelse = ' else {\n' + self.p(a.orelse[0]) + '}'

        return 'if ( ' + self.p(a.test) + ' ) {\n' + \
                self.statements(a.body) + '}' + orelse

    ## Expressions ###########################################################

    def Call(self, a):
        m = self.hp.macros(self.hp)
        if a.func.id in dir(m):
            return m.__getattribute__(a.func.id)(*a.args)
        else:
            return a.func.id + '( ' + \
                    ', '.join([self.p(b) for b in a.args]) + ' )'

    def Name(self, a):
        if a.id in self.special_names:
            return self.special_names[a.id]
        return '$'+a.id

    def Subscript(self, a):
        return self.p(a.value) + '[' + self.p(a.slice) + ']'

    def Index(self, a):
        return self.p(a.value)

    def ListComp(self, a):
        g = a.generators[0]

        trees = []

        # $__pyhp_array__ = $values
        assign = ast.Assign()
        target = ast.Name()
        target.id = '__pyhp_array__'
        assign.targets = [target]
        assign.value = ast.List()
        assign.value.elts = []
        trees.append(assign)

        # foreach ( $iter as $target ) { $elt }
        for_ = ast.For()
        for_.iter = g.iter
        for_.target = g.target
        for_.body = [ast.Call()]
        for_.body[0].func = ast.Name
        for_.body[0].func.id = '_append'
        for_.body[0].args = [target, a.elt]
        trees.append(for_)

        self._get_context().output.append(self.statements(trees))
        return self.p(target)

    ## Operators #############################################################

    ## UnaryOp

    def UnaryOp(self, a):
        return self.p(a.op) + ' ' + self.p(a.operand)

    def Not(self, a):
        return '!'

    ## BinOp

    def BinOp(self, a):
        if type(a.op) == ast.Pow:
            return 'pow(' + self.p(a.left) + ', ' + self.p(a.right) + ')'
        return self.p(a.left) + ' ' + self.p(a.op) + ' ' + self.p(a.right)

    def Add(self, a):
        return '+'

    def Mult(self, a):
        return '*'

    ## Compare

    def Compare(self, a):
        return self.p(a.left) + \
                ' ' + self.p(a.ops[0]) + ' ' + \
                self.p(a.comparators[0])

    def Eq(self, a):
        return '==='

    def NotEq(self, a):
        return '!=='

    def Lt(self, a):
        return '<'

    def Gt(self, a):
        return '>'

    def LtE(self, a):
        return '<='

    def GtE(self, a):
        return '>='

    ## Builtin types #########################################################

    def Num(self, a):
        return str(a.n)

    def Str(self, a):
        return "'" + a.s.replace('\\','\\\\').replace("'","\\'") + "'"

    def List(self, a):
        return 'array( ' + ', '.join([self.p(e) for e in a.elts]) + ' )'

    def Tuple(self, a):
        return self.List(a)

    def Dict(self, a):
        return 'array(' + \
                ', '.join([self.p(k) + ' => ' + self.p(v)
                    for k,v in zip(a.keys, a.values)]) + \
                            ')'

    def NoneType(self, a):
        return 'null'

class HotPotato:
    def __init__(self, fn, debug=False, macros=Macros):
        self.debug = debug
        self.macros = macros

        self.ast = compile(open(fn).read(),
                fn,
                'exec',
                ast.PyCF_ONLY_AST)

    def php(self):
        return '<?php\n'+self._php(self.ast, None)

    def _php(self, a, parent):
        c = a.__class__
        actions = Actions(self, parent)
        try:
            return actions.__getattribute__(c.__name__)(a)
        except AttributeError:
            if self.debug:
                return repr(a)
            else:
                raise

class Compiler:
    def __init__(self, macros=Macros):
        self.macros = macros

    def compile(self, files):
        for f in files:
            hp = HotPotato(f, macros=self.macros)
            with open(f.replace('.py', '.php'), 'w+') as out:
                out.write(hp.php())

if __name__ == '__main__':
    import sys
    argv = sys.argv[1:]
    debug = False
    if '-d' in argv:
        argv.remove('-d')
        debug = True

    if len(argv) == 1:
        hp = HotPotato(argv[0], debug)
        print(hp.php())

    else:
        print("Usage: python -m hp FILE.py")
        exit(1)
<|endoftext|>


<commit_before>#!/usr/bin/env python
#-*- coding: utf-8 -*-
#
# Generates project structure from template
# Version 1.0
# Homepage: https://github.com/anodamobi/XCode-Helpers
# Author: Alex Zavrazhniy <alex@anoda.mobi>
# Copyright 2015, ANODA Mobile Development Agency
# License: MIT

import os
import argparse
import re
import logging
import sys

# fixing pwd
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))

parser = argparse.ArgumentParser(description='Generates project structure from template')
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
parser.add_argument('-v', dest='verbose', action='store_true', help='verbose output')
parser.add_argument('-t', '--template', dest='templatePath', type=str, help='path to template folder')
parser.add_argument('-x', '--prefix', dest='prefix', type=str, help='prefix name')
parser.add_argument('-p', '--project', dest='project', type=str, help='project name')
parser.add_argument('-m', '--module', dest='module', type=str, help='module name')

args = parser.parse_args()

if args.verbose:
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
else:
    logging.basicConfig(format='%(levelname)s: %(message)s')

# expand ~ in template path and make ithem absolute
if args.templatePath[0] == '~':
    args.templatePath = os.path.expanduser(args.templatePath)

args.templatePath = os.path.abspath(args.templatePath) + '/'

logging.debug('Using template folder: %s', args.templatePath)

if not os.path.isdir(args.templatePath):
    logging.error('Given templatePath is not a folder: %s', args.templatePath)
    exit(1)

def replace_placeholders(input):
    output = re.sub('<%prefixLower%>', args.prefix.lower(), input)
    output = re.sub('<%prefix%>', args.prefix, output)
    output = re.sub('<%projectLower%>', args.project.lower(), output)
    output = re.sub('<%project%>', args.project, output)
    output = re.sub('<%moduleLower%>', args.module.lower(), output)
    output = re.sub('<%module%>', args.module, output)
    return output

for root, dirs, files in os.walk(args.templatePath):
    logging.debug('Working in %s', root)
    folder = replace_placeholders(root).replace(args.templatePath, '') + '/'
    if len(folder) > 1:
        print folder
        os.makedirs(folder)

    for filename in files:
        newFilename = folder + replace_placeholders(filename)
        print newFilename
        content = replace_placeholders(open('%s/%s' % (root, filename)).read())
        open(newFilename, 'wb').write(content)

# EOF<commit_msg>removed CWD fix<commit_after>#!/usr/bin/env python
#-*- coding: utf-8 -*-
#
# Generates project structure from template
# Version 1.0
# Homepage: https://github.com/anodamobi/XCode-Helpers
# Author: Alex Zavrazhniy <alex@anoda.mobi>
# Copyright 2015, ANODA Mobile Development Agency
# License: MIT

import os
import argparse
import re
import logging
import sys

parser = argparse.ArgumentParser(description='Generates project structure from template')
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
parser.add_argument('-v', dest='verbose', action='store_true', help='verbose output')
parser.add_argument('-t', '--template', dest='templatePath', type=str, help='path to template folder')
parser.add_argument('-x', '--prefix', dest='prefix', type=str, help='prefix name')
parser.add_argument('-p', '--project', dest='project', type=str, help='project name')
parser.add_argument('-m', '--module', dest='module', type=str, help='module name')

args = parser.parse_args()

if args.verbose:
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
else:
    logging.basicConfig(format='%(levelname)s: %(message)s')

# expand ~ in template path and make ithem absolute
if args.templatePath[0] == '~':
    args.templatePath = os.path.expanduser(args.templatePath)

args.templatePath = os.path.abspath(args.templatePath) + '/'

logging.debug('Using template folder: %s', args.templatePath)

if not os.path.isdir(args.templatePath):
    logging.error('Given templatePath is not a folder: %s', args.templatePath)
    exit(1)

def replace_placeholders(input):
    output = re.sub('<%prefixLower%>', args.prefix.lower(), input)
    output = re.sub('<%prefix%>', args.prefix, output)
    output = re.sub('<%projectLower%>', args.project.lower(), output)
    output = re.sub('<%project%>', args.project, output)
    output = re.sub('<%moduleLower%>', args.module.lower(), output)
    output = re.sub('<%module%>', args.module, output)
    return output

for root, dirs, files in os.walk(args.templatePath):
    logging.debug('Working in %s', root)
    folder = replace_placeholders(root).replace(args.templatePath, '') + '/'
    if len(folder) > 1:
        print folder
        os.makedirs(folder)

    for filename in files:
        newFilename = folder + replace_placeholders(filename)
        print newFilename
        content = replace_placeholders(open('%s/%s' % (root, filename)).read())
        open(newFilename, 'wb').write(content)

# EOF<|endoftext|>


<commit_before>#!/usr/bin/env python

import sys
import re
import argparse
from datetime import datetime

from crontab import CronTab

class NotEnoughArguments(Exception):
	pass

class CantUseOptWithOthers(Exception):
	pass

DAYS = {
	"sunday": "0",
	"monday": "1",
	"tuesday": "2",
	"wednesday": "3",
	"thursday": "4",
	"friday": "5",
	"saturday": "6"
}

MONTHS = {
	"january": "1",
	"february": "2",
	"march": "3",
	"april": "4",
	"may": "5",
	"june": "6",
	"july": "7",
	"august": "8",
	"september": "9",
	"october": "10",
	"november": "11",
	"december": "12"
}

SHORT_DAYS = {key[:3]:key for key in DAYS.keys()}

SHORT_MONTHS = {key[:3]:key for key in MONTHS.keys()}

def parse_arguments():
	"""parse the arguments coming from running the script"""
	try:
		parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
			description="A utility that translates intelligent schedule commands to crontab entries.",
			epilog="Examples:\n\tAdd a job:\tsupercron -c \"date +%j >> some_file\" -r \"every 1 hour\" log_dates" +
			"\n\tDelete a job:\tsupercron -d log_dates")
		parser.add_argument("-r", "--repetition", nargs=1,
			help="repetition clause (should be enclosed by quotes if it contains spaces)")
		parser.add_argument("-c", "--command", nargs=1,
			help="command to be executed by the job (should be enclosed by quotes if it contains spaces)")
		parser.add_argument("-d", "--delete", action="store_true", help="for deleteing a job")
		parser.add_argument("--enable", action="store_true", help="for enabling a job")
		parser.add_argument("--disable", action="store_true", help="for disabling a job")
		parser.add_argument("name", help="name of the job")
		args = parser.parse_args()
		if args.delete:
			if not check_other_args("delete", args):
				raise CantUseOptWithOthers
			delete_job(args.name)
			sys.exit(0)
		if args.enable:
			if not check_other_args("enable", args):
				raise CantUseOptWithOthers
			enable_job(args.name, True)
			sys.exit(0)
		if args.disable:
			if not check_other_args("disable", args):
				raise CantUseOptWithOthers
			enable_job(args.name, False)
			sys.exit(0)
		if args.repetition and args.command:
			return args.name, args.command, args.repetition
		else:
			raise NotEnoughArguments
	except CantUseOptWithOthers:
		print("Options '--delete', '--disable' and '--enable' cannot be used with any other options.")
		sys.exit(1)
	except NotEnoughArguments:
		print("Error: not enough (or invalid) arguments. Type 'supercron --help' for help.")
		sys.exit(1)

def check_other_args(wanted, args):
	"""check that mutual exclusive options are alone"""
	for arg in args.__dict__:
		if arg != wanted and arg != "name" and args.__dict__[arg]:
			return False
	return True

def get_time_now():
	"""get current time"""
	hour = datetime.now().hour
	minute = datetime.now().minute
	return hour, minute

def enable_job(name, enable_it):
	"""enable or disable job(s) by their name"""
	cron = CronTab(user=True)
	for job in cron.find_comment(name):
		job.enable(enable_it)
	if enable_it:
		action = "enabled"
	else:
		action = "disabled"
	cron.write_to_user(user=True)
	print("Jobs named '{}' have been {}.".format(name, action))

def add_job(name, command, repeat):
	"""add the job to crontab"""
	if not repeat:
		print("Error: invalid repetition clause.")
		sys.exit(1)
	cron = CronTab(user=True)
	job = cron.new(command=command, comment=name)
	if "min_every" in repeat:
		job.minute.every(repeat['min_every'])
	if "min_on" in repeat:
		job.minute.on(repeat['min_on'])
	if "hour_every" in repeat:
		job.hour.every(repeat['hour_every'])
	if "hour_on" in repeat:
		job.hour.on(repeat['hour_on'])
	if "day_every" in repeat:
		job.day.every(repeat['day_every'])
	if "day_on" in repeat:
		job.day.on(repeat['day_on'])
	if "dow_on" in repeat:
		job.dow.on(*repeat['dow_on'])
	if "dow_during" in repeat:
		job.dow.during(*repeat['dow_during'])
	if "month_every" in repeat:
		job.month.every(repeat['month_every'])
	if "month_during" in repeat:
		job.month.during(*repeat['month_during'])
	if "month_on" in repeat:
		job.month.on(*repeat['month_on'])
	job.enable()
	cron.write_to_user(user=True)
	print("Jobs named '{}' have been successfully added.".format(name))

def delete_job(name):
	"""delete the specified job from user's crontab"""
	cron = CronTab(user=True)
	cron.remove_all(comment=name)
	cron.write_to_user(user=True)
	print("Jobs named '{}' have been deleted.".format(name))

def expand_repetition(repetition):
	"""expand short day/month names to full day/month names"""
	for short_day in SHORT_DAYS.keys():
		repetition = re.sub("\b{}\b".format(short_day), SHORT_DAYS[short_day], repetition)
	for short_month in SHORT_MONTHS.keys():
		repetition = re.sub("\b{}\b".format(short_month), SHORT_MONTHS[short_month], repetition)
	return repetition

def parse_repetition(repetition):
	"""parse and convert different types of repetition clauses"""
	repeat = {}
	repetition = expand_repetition(repetition.lower())
	# check for repetition clauses like: "once every 21 minutes"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+minute(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 60:
			repeat['min_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-59 for minutes.")
			sys.exit(1)
	# check for repetition clauses like: "once every 3 hours"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+hour(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 24:
			repeat['hour_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-23 for hours.")
			sys.exit(1)
	# check for repetition clauses like: "once every 11 days"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+day(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 460:
			repeat['day_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-31 for days.")
			sys.exit(1)
	# check for repetition clauses like: "10:32 am"
	matched = re.search(r"(on\s+)?(\d+):(\d+)(\s+(am|pm))?", repetition)
	if matched:
		hour = int(matched.group(2))
		minute = int(matched.group(3))
		if matched.group(4):
			if matched.group(5) == "pm":
				if hour != 12:
					hour += 12
			else:
				if hour == 12:
					hour = 0
		if hour < 24 and minute < 59:
			repeat['min_on'] = minute
			repeat['hour_on'] = hour
		else:
			print("Error: invalid value for hour and/or minute.")
			sys.exit(1)
	# check for repetition clauses like: "on monday"
	m_repetition = repetition.replace(" and ", " on ").replace(",and ", " on ").replace(",", "on ")
	matched = re.finditer(r"(on\s+)(monday|tuesday|wednesday|thursday|friday|saturday|sunday)s?", m_repetition)
	weekdays = []
	for match in matched:
		weekdays.append(DAYS[match.group(2)])
	if weekdays:
		repeat['dow_on'] = weekdays
	# check for repetition clauses like: "from monday to friday"
	matched = re.search(r"(from\s+)(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\s+to\s+" +
		r"(monday|tuesday|wednesday|thursday|friday|saturday|sunday)", repetition)
	if matched:
		weekdays = []
		weekdays.append(DAYS[matched.group(2)])
		weekdays.append(DAYS[matched.group(3)])
		if weekdays[0] < weekdays[1]:
			repeat['dow_during'] = weekdays
		else:
			dows = []
			i = int(weekdays[0])
			while i != int(weekdays[1]):
				dows.append(str(i))
				i = (i + 1) % 7
			dows.append(str(weekdays[1]))
			repeat['dow_on'] = dows
	# check for repetition clauses like: "on december"
	m_repetition = repetition.replace(" and ", " on ").replace(",and ", " on ").replace(",", "on ")
	matched = re.finditer(r"(on\s+)(january|february|march|april|may|june|july|august|september|october|november|december)",
		m_repetition)
	matched_months = []
	for match in matched:
		matched_months.append(MONTHS[match.group(2)])
	if matched_months:
		repeat['month_on'] = matched_months
	# check for repetition clauses like: "from june to august"
	matched = re.search(r"(from\s+)(january|february|march|april|may|june|july|august|september|october|november|december)" +
		r"\s+to\s+(january|february|march|april|may|june|july|august|september|october|november|december)", repetition)
	if matched:
		matched_months = []
		matched_months.append(MONTHS[matched.group(2)])
		matched_months.append(MONTHS[matched.group(3)])
		if matched_months[0] < matched_months[1]:
			repeat['month_during'] = matched_months
		else:
			consec_months = []
			i = int(matched_months[0])
			while != int(matched_months[1]):
				consec_months.append(str(i))
				i = (i % 12) + 1
			consec_months.append(str(matched_months[1]))
			repeat['month_on'] = consec_months
	# check if minute and hour fields are empty
	hour, minute = get_time_now()
	if not ("min_on" in repeat or "min_every" in repeat):
		repeat['min_on'] = minute
	if not ("hour_on" in repeat or "hour_every" in repeat):
		repeat['hour_on'] = hour
	return repeat

if __name__ == "__main__":
	name, command, repetition = parse_arguments()
	add_job(name, command[0], parse_repetition(repetition[0]))
	sys.exit(0)
<commit_msg>check for repetition clauses like: "once every 3 months"<commit_after>#!/usr/bin/env python

import sys
import re
import argparse
from datetime import datetime

from crontab import CronTab

class NotEnoughArguments(Exception):
	pass

class CantUseOptWithOthers(Exception):
	pass

DAYS = {
	"sunday": "0",
	"monday": "1",
	"tuesday": "2",
	"wednesday": "3",
	"thursday": "4",
	"friday": "5",
	"saturday": "6"
}

MONTHS = {
	"january": "1",
	"february": "2",
	"march": "3",
	"april": "4",
	"may": "5",
	"june": "6",
	"july": "7",
	"august": "8",
	"september": "9",
	"october": "10",
	"november": "11",
	"december": "12"
}

SHORT_DAYS = {key[:3]:key for key in DAYS.keys()}

SHORT_MONTHS = {key[:3]:key for key in MONTHS.keys()}

def parse_arguments():
	"""parse the arguments coming from running the script"""
	try:
		parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
			description="A utility that translates intelligent schedule commands to crontab entries.",
			epilog="Examples:\n\tAdd a job:\tsupercron -c \"date +%j >> some_file\" -r \"every 1 hour\" log_dates" +
			"\n\tDelete a job:\tsupercron -d log_dates")
		parser.add_argument("-r", "--repetition", nargs=1,
			help="repetition clause (should be enclosed by quotes if it contains spaces)")
		parser.add_argument("-c", "--command", nargs=1,
			help="command to be executed by the job (should be enclosed by quotes if it contains spaces)")
		parser.add_argument("-d", "--delete", action="store_true", help="for deleteing a job")
		parser.add_argument("--enable", action="store_true", help="for enabling a job")
		parser.add_argument("--disable", action="store_true", help="for disabling a job")
		parser.add_argument("name", help="name of the job")
		args = parser.parse_args()
		if args.delete:
			if not check_other_args("delete", args):
				raise CantUseOptWithOthers
			delete_job(args.name)
			sys.exit(0)
		if args.enable:
			if not check_other_args("enable", args):
				raise CantUseOptWithOthers
			enable_job(args.name, True)
			sys.exit(0)
		if args.disable:
			if not check_other_args("disable", args):
				raise CantUseOptWithOthers
			enable_job(args.name, False)
			sys.exit(0)
		if args.repetition and args.command:
			return args.name, args.command, args.repetition
		else:
			raise NotEnoughArguments
	except CantUseOptWithOthers:
		print("Options '--delete', '--disable' and '--enable' cannot be used with any other options.")
		sys.exit(1)
	except NotEnoughArguments:
		print("Error: not enough (or invalid) arguments. Type 'supercron --help' for help.")
		sys.exit(1)

def check_other_args(wanted, args):
	"""check that mutual exclusive options are alone"""
	for arg in args.__dict__:
		if arg != wanted and arg != "name" and args.__dict__[arg]:
			return False
	return True

def get_time_now():
	"""get current time"""
	hour = datetime.now().hour
	minute = datetime.now().minute
	return hour, minute

def enable_job(name, enable_it):
	"""enable or disable job(s) by their name"""
	cron = CronTab(user=True)
	for job in cron.find_comment(name):
		job.enable(enable_it)
	if enable_it:
		action = "enabled"
	else:
		action = "disabled"
	cron.write_to_user(user=True)
	print("Jobs named '{}' have been {}.".format(name, action))

def add_job(name, command, repeat):
	"""add the job to crontab"""
	if not repeat:
		print("Error: invalid repetition clause.")
		sys.exit(1)
	cron = CronTab(user=True)
	job = cron.new(command=command, comment=name)
	if "min_every" in repeat:
		job.minute.every(repeat['min_every'])
	if "min_on" in repeat:
		job.minute.on(repeat['min_on'])
	if "hour_every" in repeat:
		job.hour.every(repeat['hour_every'])
	if "hour_on" in repeat:
		job.hour.on(repeat['hour_on'])
	if "day_every" in repeat:
		job.day.every(repeat['day_every'])
	if "day_on" in repeat:
		job.day.on(repeat['day_on'])
	if "dow_on" in repeat:
		job.dow.on(*repeat['dow_on'])
	if "dow_during" in repeat:
		job.dow.during(*repeat['dow_during'])
	if "month_every" in repeat:
		job.month.every(repeat['month_every'])
	if "month_during" in repeat:
		job.month.during(*repeat['month_during'])
	if "month_on" in repeat:
		job.month.on(*repeat['month_on'])
	job.enable()
	cron.write_to_user(user=True)
	print("Jobs named '{}' have been successfully added.".format(name))

def delete_job(name):
	"""delete the specified job from user's crontab"""
	cron = CronTab(user=True)
	cron.remove_all(comment=name)
	cron.write_to_user(user=True)
	print("Jobs named '{}' have been deleted.".format(name))

def expand_repetition(repetition):
	"""expand short day/month names to full day/month names"""
	for short_day in SHORT_DAYS.keys():
		repetition = re.sub("\b{}\b".format(short_day), SHORT_DAYS[short_day], repetition)
	for short_month in SHORT_MONTHS.keys():
		repetition = re.sub("\b{}\b".format(short_month), SHORT_MONTHS[short_month], repetition)
	return repetition

def parse_repetition(repetition):
	"""parse and convert different types of repetition clauses"""
	repeat = {}
	repetition = expand_repetition(repetition.lower())
	# check for repetition clauses like: "once every 21 minutes"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+minute(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 60:
			repeat['min_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-59 for minutes.")
			sys.exit(1)
	# check for repetition clauses like: "once every 3 hours"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+hour(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 24:
			repeat['hour_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-23 for hours.")
			sys.exit(1)
	# check for repetition clauses like: "once every 11 days"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+day(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 460:
			repeat['day_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-31 for days.")
			sys.exit(1)
	# check for repetition clauses like: "once every 3 months"
	matched = re.search(r"(once\s+)?every\s+(\d+)\s+month(s)?(\.)?", repetition)
	if matched:
		if int(matched.group(2)) > 0 and int(matched.group(2)) < 13:
			repeat['month_every'] = int(matched.group(2))
		else:
			print("Error: invalid value '{}'. Expected 1-12 for months.")
			sys.exit(1)
	# check for repetition clauses like: "10:32 am"
	matched = re.search(r"(on\s+)?(\d+):(\d+)(\s+(am|pm))?", repetition)
	if matched:
		hour = int(matched.group(2))
		minute = int(matched.group(3))
		if matched.group(4):
			if matched.group(5) == "pm":
				if hour != 12:
					hour += 12
			else:
				if hour == 12:
					hour = 0
		if hour < 24 and minute < 59:
			repeat['min_on'] = minute
			repeat['hour_on'] = hour
		else:
			print("Error: invalid value for hour and/or minute.")
			sys.exit(1)
	# check for repetition clauses like: "on monday"
	m_repetition = repetition.replace(" and ", " on ").replace(",and ", " on ").replace(",", "on ")
	matched = re.finditer(r"(on\s+)(monday|tuesday|wednesday|thursday|friday|saturday|sunday)s?", m_repetition)
	weekdays = []
	for match in matched:
		weekdays.append(DAYS[match.group(2)])
	if weekdays:
		repeat['dow_on'] = weekdays
	# check for repetition clauses like: "from monday to friday"
	matched = re.search(r"(from\s+)(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\s+to\s+" +
		r"(monday|tuesday|wednesday|thursday|friday|saturday|sunday)", repetition)
	if matched:
		weekdays = []
		weekdays.append(DAYS[matched.group(2)])
		weekdays.append(DAYS[matched.group(3)])
		if weekdays[0] < weekdays[1]:
			repeat['dow_during'] = weekdays
		else:
			dows = []
			i = int(weekdays[0])
			while i != int(weekdays[1]):
				dows.append(str(i))
				i = (i + 1) % 7
			dows.append(str(weekdays[1]))
			repeat['dow_on'] = dows
	# check for repetition clauses like: "on december"
	m_repetition = repetition.replace(" and ", " on ").replace(",and ", " on ").replace(",", "on ")
	matched = re.finditer(r"(on\s+)(january|february|march|april|may|june|july|august|september|october|november|december)",
		m_repetition)
	matched_months = []
	for match in matched:
		matched_months.append(MONTHS[match.group(2)])
	if matched_months:
		repeat['month_on'] = matched_months
	# check for repetition clauses like: "from june to august"
	matched = re.search(r"(from\s+)(january|february|march|april|may|june|july|august|september|october|november|december)" +
		r"\s+to\s+(january|february|march|april|may|june|july|august|september|october|november|december)", repetition)
	if matched:
		matched_months = []
		matched_months.append(MONTHS[matched.group(2)])
		matched_months.append(MONTHS[matched.group(3)])
		if matched_months[0] < matched_months[1]:
			repeat['month_during'] = matched_months
		else:
			consec_months = []
			i = int(matched_months[0])
			while != int(matched_months[1]):
				consec_months.append(str(i))
				i = (i % 12) + 1
			consec_months.append(str(matched_months[1]))
			repeat['month_on'] = consec_months
	# check if minute and hour fields are empty
	hour, minute = get_time_now()
	if not ("min_on" in repeat or "min_every" in repeat):
		repeat['min_on'] = minute
	if not ("hour_on" in repeat or "hour_every" in repeat):
		repeat['hour_on'] = hour
	return repeat

if __name__ == "__main__":
	name, command, repetition = parse_arguments()
	add_job(name, command[0], parse_repetition(repetition[0]))
	sys.exit(0)
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""
A programmer's search tool
"""
import re
from multiprocessing import Pool, Manager, Process
import sys
from os import sep as directory_separator, getcwd, path, walk

class NullDevice():
    def write(self, s):
        pass

def build_parser():
    import argparse
    parser = argparse.ArgumentParser(description="A programmer's search tool")
    parser.add_argument('pattern', metavar='PATTERN', action='store',
                        help='a python re regular expression')
    parser.add_argument('--ignore-case', '-i',  dest='ignorecase', action='store_true',
                        default=False, help='ignore case when searching')
    parser.add_argument('--no-unicode', '-u', dest='unicode', action='store_false',
                        default=True, help='unicode-unfriendly searching')
    parser.add_argument('--no-multiline', '-l', dest='multiline',
                        action='store_false', default=True,
                        help='don\'t search over multiple lines')
    parser.add_argument('--dot-all', '-a', dest='dot_all',
                        action='store_true', default=False,
                        help='dot in pattern matches newline')
    parser.add_argument('--follow-links', '-s', dest='follow_links',
                        action='store_true', default=False,
                        help='follow symlinks')
    parser.add_argument('--hidden', '-n', dest='search_hidden',
                        action='store_true', default=False,
                        help='search hidden files and directories')
    parser.add_argument('--num-processes', '-p', dest='number_processes',
                        action='store', default=10, type=int,
                        help='number of child processes to concurrently search with')
    parser.add_argument('--exclude', '-x', dest='exclude',
                        action='store', default=None, type=str,
                        help='exclude')
    parser.add_argument('--debug', '-d', dest='debug',
                        action='store_true', default=False,
                        help='print debug output')
    parser.add_argument('directory', metavar='DIRECTORY', nargs='?',
                        default=getcwd(), help='a directory to search in (default cwd)')
    return parser

def get_text_file_contents(path):
    # if this isn't a text file, we should raise an IOError
    f = open(path, 'r')
    file_contents = f.read()
    f.close()
    if file_contents.find('\000') >= 0:
        raise IOError('Not a text file')
    return file_contents

class SearchManager(object):
    def __init__(self, regex, number_processes=10, chunk_size=10,
                 search_hidden=False, follow_links=False):
        self.regex = regex
        self.search_hidden = search_hidden
        self.follow_links = follow_links
        self.pool = Pool(processes=number_processes)
        self.chunk_size = chunk_size
        self.manager = Manager()

    def search(self, directory):
        all_results = {}

        if self.search_hidden:
            def filtered(names):
                return names
        else:
            def filtered(names):
                return [name for name in names if not name.startswith('.')]

        def search_walk():
            for packed in walk(directory, followlinks=self.follow_links):
                directory_path, directory_names, file_names = packed
                directory_names[:] = filtered(directory_names)
                file_names[:] = filtered(file_names)
                yield directory_path, directory_names, file_names

        for directory_path, directory_names, file_names in search_walk():
            args = (self.regex, directory_path, file_names)
            self.pool.apply_async(search_path, args, callback=print_result)

class ColorWriter(object):
    GREEN = '\033[92m'
    BLUE = '\033[94m'
    END_COLOR = '\033[0m'

    def __init__(self, output=None):
        if output == None:
            output = sys.stdout
        self.output = output

    def write(self, text):
        self.output.write(text)

    def write_green(self, text):
        self.output.write(self.GREEN + text + self.END_COLOR)

    def write_blue(self, text):
        self.output.write(self.BLUE + text + self.END_COLOR)

def search_path(regex, directory_path, names):
    result = DirectoryResult(directory_path)
    def find_matches(name):
        full_path = path.join(directory_path, name)
        file_contents = get_text_file_contents(full_path)
        start = 0
        match = regex.search(file_contents, start)
        while match:
            result.put(name, file_contents, match)
            start = match.end()
            match = regex.search(file_contents, start)
    for name in names:
        try:
            find_matches(name)
        except IOError:
            pass
    return result

class DirectoryResult(object):
    def __init__(self, directory_path):
        self.directory_path = directory_path
        self._line_results = {}

    def put(self, file_name, file_contents, regex_match):
        group = regex_match.group()
        match_start = regex_match.start()
        line_number = file_contents.count('\n', 0, match_start) + 1
        left_offset = match_start - file_contents.rfind('\n', 0, match_start)
        right_offset = file_contents.find('\n', match_start)
        left_of_group = file_contents[1+match_start-left_offset:match_start]
        right_of_group = file_contents[match_start+len(group):right_offset]
        line_result = LineResult(line_number, left_offset,
                                 left_of_group, group, right_of_group)

        if not file_name in self._line_results:
            self._line_results[file_name] = []
        self._line_results[file_name].append(line_result)

    def get_line_results(self):
        return self._line_results.items()

class LineResult(object):
    def __init__(self, line_number, left_offset, left_of_group,
                 group, right_of_group):
        self.line_number = line_number
        self.left_offset = left_offset
        self.left_of_group = left_of_group # left of group
        self.group = group
        self.right_of_group = right_of_group # right of group

def print_result(directory_result):
    writer = ColorWriter(sys.stdout)
    for file_name, line_results in directory_result.get_line_results():
        full_path = path.join(directory_result.directory_path, file_name)
        writer.write_green(full_path+':')
        writer.write('\n')
        for line_result in line_results:
            writer.write('%s: ' % (line_result.line_number))
            writer.write(line_result.left_of_group)
            writer.write_blue(line_result.group)
            writer.write(line_result.right_of_group+'\n')

def main():
    # parse arguments
    parser = build_parser()

    """
    options:
    install - make a symlink at /usr/local/bin/lk
    replace
    ignore files matching glob or pattern
    config (set config options, like git)
    open files with app (including mac friendly syntax using the "open -a" cmd)
    default: ignore .git, .svn, .hg, etc
    """

    args = parser.parse_args()
    flags = re.LOCALE

    if args.dot_all:
        flags |= re.DOTALL

    if args.ignorecase:
        flags |= re.IGNORECASE

    if args.unicode:
        flags |= re.UNICODE

    if args.multiline:
        flags |= re.MULTILINE

    regex = re.compile(args.pattern, flags)
    directory = args.directory

#    TODO: fix this
#    if not args.debug:
#        sys.stderr = NullDevice()

    search_manager = SearchManager(regex, number_processes=args.number_processes,
                                   search_hidden=args.search_hidden,
                                   follow_links=args.follow_links)

    search_manager.search(directory)

if __name__ == '__main__':
    main()
<commit_msg>Added some docstrings<commit_after>#!/usr/bin/env python
"""
A programmer's search tool
"""
import re
from multiprocessing import Pool, Manager, Process
import sys
from os import sep as directory_separator, getcwd, path, walk

def build_parser():
    """
    Returns an argparse.ArgumentParser instance to parse the command line
    arguments for lk
    """
    import argparse
    parser = argparse.ArgumentParser(description="A programmer's search tool")
    parser.add_argument('pattern', metavar='PATTERN', action='store',
                        help='a python re regular expression')
    parser.add_argument('--ignore-case', '-i',  dest='ignorecase', action='store_true',
                        default=False, help='ignore case when searching')
    parser.add_argument('--no-unicode', '-u', dest='unicode', action='store_false',
                        default=True, help='unicode-unfriendly searching')
    parser.add_argument('--no-multiline', '-l', dest='multiline',
                        action='store_false', default=True,
                        help='don\'t search over multiple lines')
    parser.add_argument('--dot-all', '-a', dest='dot_all',
                        action='store_true', default=False,
                        help='dot in pattern matches newline')
    parser.add_argument('--follow-links', '-s', dest='follow_links',
                        action='store_true', default=False,
                        help='follow symlinks')
    parser.add_argument('--hidden', '-n', dest='search_hidden',
                        action='store_true', default=False,
                        help='search hidden files and directories')
    parser.add_argument('--num-processes', '-p', dest='number_processes',
                        action='store', default=10, type=int,
                        help='number of child processes to concurrently search with')
#    parser.add_argument('--exclude', '-x', metavar='GLOB', dest='exclude',
#                        action='store', default=None, type=str,
#                        help='exclude')
#    parser.add_argument('--debug', '-d', dest='debug',
#                        action='store_true', default=False,
#                        help='print debug output')
    parser.add_argument('directory', metavar='DIRECTORY', nargs='?',
                        default=getcwd(), help='a directory to search in (default cwd)')
    return parser

def get_file_contents(path, binary=False):
    """
    Return the contents of the text file at path.
    If it is a binary file,raise an IOError
    """
    # if this isn't a text file, we should raise an IOError
    f = open(path, 'r')
    file_contents = f.read()
    f.close()
    if not binary and file_contents.find('\000') >= 0:
        raise IOError('Expected text file, got binary file')
    return file_contents

class SearchManager(object):
    """
    An object for handling parallel searches of a regex
    """
    def __init__(self, regex, number_processes=10, chunk_size=10,
                 search_hidden=False, follow_links=False):
        self.regex = regex
        self.search_hidden = search_hidden
        self.follow_links = follow_links
        self.pool = Pool(processes=number_processes)
        self.chunk_size = chunk_size
        self.manager = Manager()

    def search(self, directory):
        """
        start a new pool of parallel search processes for self.regex in
        directory
        """
        all_results = {}

        if self.search_hidden:
            def filtered(names):
                return names
        else:
            def filtered(names):
                return [name for name in names if not name.startswith('.')]

        def search_walk():
            for packed in walk(directory, followlinks=self.follow_links):
                directory_path, directory_names, file_names = packed
                directory_names[:] = filtered(directory_names)
                file_names[:] = filtered(file_names)
                yield directory_path, directory_names, file_names

        for directory_path, directory_names, file_names in search_walk():
            args = (self.regex, directory_path, file_names)
            self.pool.apply_async(search_path, args, callback=print_result)

class ColorWriter(object):
    """'
    an object that wraps a file handler and can output ANSI color codes
    """
    GREEN = '\033[92m'
    BLUE = '\033[94m'
    END_COLOR = '\033[0m'

    def __init__(self, output=None):
        if output == None:
            output = sys.stdout
        self.output = output

    def write(self, text):
        self.output.write(text)

    def write_green(self, text):
        self.output.write(self.GREEN + text + self.END_COLOR)

    def write_blue(self, text):
        self.output.write(self.BLUE + text + self.END_COLOR)

def search_path(regex, directory_path, names):
    """
    build a DirectoryResult for the given regex, directory path, and file names
    """
    result = DirectoryResult(directory_path)
    def find_matches(name):
        full_path = path.join(directory_path, name)
        file_contents = get_file_contents(full_path)
        start = 0
        match = regex.search(file_contents, start)
        while match:
            result.put(name, file_contents, match)
            start = match.end()
            match = regex.search(file_contents, start)
    for name in names:
        try:
            find_matches(name)
        except IOError:
            pass
    return result

class DirectoryResult(object):
    """
    A container object for storing LineResult instances for text files within
    the directory at directory_path
    """
    def __init__(self, directory_path):
        self.directory_path = directory_path
        self._line_results = {}

    def put(self, file_name, file_contents, regex_match):
        group = regex_match.group()
        match_start = regex_match.start()
        line_number = file_contents.count('\n', 0, match_start) + 1
        left_offset = match_start - file_contents.rfind('\n', 0, match_start)
        right_offset = file_contents.find('\n', match_start)
        left_of_group = file_contents[1+match_start-left_offset:match_start]
        right_of_group = file_contents[match_start+len(group):right_offset]
        line_result = LineResult(line_number, left_offset,
                                 left_of_group, group, right_of_group)

        if not file_name in self._line_results:
            self._line_results[file_name] = []
        self._line_results[file_name].append(line_result)

    def get_line_results(self):
        return self._line_results.items()

class LineResult(object):
    """
    An object for storing metadata about search matches on one line of a text
    file
    """
    def __init__(self, line_number, left_offset, left_of_group,
                 group, right_of_group):
        self.line_number = line_number
        self.left_offset = left_offset
        self.left_of_group = left_of_group # left of group
        self.group = group
        self.right_of_group = right_of_group # right of group

writer = ColorWriter(sys.stdout)
def print_result(directory_result):
    """
    Print out the contents of the directory result, using ANSI color codes if
    supported
    """
    for file_name, line_results in directory_result.get_line_results():
        full_path = path.join(directory_result.directory_path, file_name)
        writer.write_green(full_path+':')
        writer.write('\n')
        for line_result in line_results:
            writer.write('%s: ' % (line_result.line_number))
            writer.write(line_result.left_of_group)
            writer.write_blue(line_result.group)
            writer.write(line_result.right_of_group+'\n')

def main():
    """
    if lk.py is run as a script, this function will run
    """
    parser = build_parser()

    args = parser.parse_args()
    flags = re.LOCALE

    if args.dot_all:
        flags |= re.DOTALL

    if args.ignorecase:
        flags |= re.IGNORECASE

    if args.unicode:
        flags |= re.UNICODE

    if args.multiline:
        flags |= re.MULTILINE

#    if not args.debug:
#        sys.stderr = NullDevice()

    regex = re.compile(args.pattern, flags)
    directory = args.directory

    search_manager = SearchManager(regex, number_processes=args.number_processes,
                                   search_hidden=args.search_hidden,
                                   follow_links=args.follow_links)
    search_manager.search(directory)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>import wpilib
from wpilib import SmartDashboard
from wpilib.buttons import JoystickButton

#import subsystems
from subsystems.drivetrain import DriveTrain
from subsystems.lift import LiftMech
from subsystems.pneumatics_comp import Pneumatics

#import commands
from commands.speed_toggle import SpeedToggle
from commands.pull_intake import PullIntake
from commands.lift_arm import MoveArm

class OI:

	def __init__(self, robot):

		#Xbox controller
		self.joy = wpilib.Joystick(0)

		#Actual Joystick
		self.joy_lift = wpilib.Joystick(1)

		#Buttons for Xbox controller
		self.r_trig = JoystickButton(self.joy, 6) #I think

		#buttons for actual joystick
		self.btn3 = JoystickButton(self.joy_lift, 3)
		#self.btn4 = JoystickButton(self.joy_lift, 4)

		#bind buttons on Xbox controller to commands
		self.r_trig.toggleWhenPressed(SpeedToggle(robot.pneumatics_comp))

		#bind buttons on Joystick to commands
		self.btn3.whileHeld(PullIntake(robot.intake))
		#self.btn3.whenPressed(PullIntake(Intake))

	def getJoystick(self):
		return self.joy

	def getLiftstick(self):
		return self.joy_lift<commit_msg>Reworked oi.py<commit_after>import wpilib
from wpilib import SmartDashboard
from wpilib.buttons import JoystickButton

#import subsystems
from subsystems.drivetrain import DriveTrain
from subsystems.lift import LiftMech
from subsystems.pneumatics_comp import Pneumatics

#import commands
from commands.speed_toggle import SpeedToggle
from commands.pull_intake import PullIntake
from commands.lift_arm import MoveArm

class OI:

	def __init__(self, robot):

		#Xbox controller
		self.joy = wpilib.Joystick(0)

		#Actual Joystick
		self.joy_lift = wpilib.Joystick(1)

		#Buttons for Xbox controller
		self.r_trig = JoystickButton(self.joy, 6) #I am confident

		#buttons for actual joystick

		self.btn3 = JoystickButton(self.joy_lift, 3)
		self.btn4 = JoystickButton(self.joy_lift, 4)

		self.btn7 = JoystickButton(self.joy_lift, 7)
		self.btn8 = JoystickButton(self.joy_lift, 8)

		#bind buttons on Xbox controller to commands
		self.r_trig.toggleWhenPressed(SpeedToggle(robot.pneumatics_comp))

		#bind buttons on Joystick to commands
		self.btn7.whenPressed()
		#self.btn3.whenPressed(PullIntake(Intake))

	def getJoystick(self):
		return self.joy

	def getLiftstick(self):
		return self.joy_lift<|endoftext|>