aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatěj Cepl <mcepl@redhat.com>2013-09-17 20:21:46 +0200
committerMatěj Cepl <mcepl@redhat.com>2013-09-17 22:54:21 +0200
commitd11bbfb94d93b8c8f711e3a65c9b6b50a3592ce7 (patch)
tree124dae4c4a2d4934d93842b38df5bc5d4be415af
parent585c9d9a1fd29add74c8695e12e7fc8a2044557d (diff)
downloadcucutags-d11bbfb94d93b8c8f711e3a65c9b6b50a3592ce7.tar.gz
Script generates something resembling tags file.
Testing is required. Add MIT license to the head of script
-rwxr-xr-xcucutags.py206
1 files changed, 129 insertions, 77 deletions
diff --git a/cucutags.py b/cucutags.py
index 4ad62f7..14c8ce0 100755
--- a/cucutags.py
+++ b/cucutags.py
@@ -1,38 +1,46 @@
#!/usr/bin/python
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
import os
import io
import re
+import sys
import logging
logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s',
level=logging.INFO)
-
-
-# Press "{sequence}" [@./common_steps/app.py:17]
-# Start {app:w} via {type:w} [@./common_steps/app.py:42]
-# Make sure that {app:w} is running [@./common_steps/app.py:59]
-# {app:w} should start [@./common_steps/app.py:66]
-# folder select dialog with name "{name}" is displayed
-# [@./common_steps/dialogs.py:8]
-# folder select dialog is displayed [@./common_steps/dialogs.py:13]
-# in folder select dialog I choose "{name}" [@./common_steps/dialogs.py:19]
-# file select dialog with name "{name}" is displayed
-# [@./common_steps/dialogs.py:24]
-# file select dialog is displayed [@./common_steps/dialogs.py:29]
-# in file select dialog I select "{name}" [@./common_steps/dialogs.py:35]
-# in file save dialog I save file to "{path}" clicking "{button}"
-# [@./common_steps/dialogs.py:54]
-# I open GApplication menu [@./common_steps/gmenu.py:12]
-# I close GApplication menu [@./common_steps/gmenu.py:27]
-# I click menu "{name}" in GApplication menu [@./common_steps/gmenu.py:34]
-# I get submenus from GApplication [@./common_steps/gmenu.py:45]
+import parse
+import argparse
class Target(object):
+ """
+ Represents one line from the Python modules.
+ """
pattern = re.compile(r"^\s*@(step|when|then)\(u'(.*)'\)")
result = 'targets'
def __init__(self, text, filename, lineno):
self.text = text
+ self.parser = parse.compile(self.text)
self.filename = filename
self.lineno = int(lineno)
@@ -43,13 +51,20 @@ class Target(object):
return self.__unicode__().encode("utf-8")
def ismatch(self, feature):
- return True
+ """
+ Checks whether the target line can be expanded to match
+ the Feature.
+ """
+ out = self.parser.parse(feature)
+ if out:
+ logging.debug("out = %s", out)
+ return out
-# sed -n -e \
-# 's/^\s\+\(\*\|[Ww]hen\|[Ss]tep\|[Tt]hen\|[Gg]iven\)\s\+\(.*\)\s*$/\2/p' \
-# "$1" |sort -u
class Feature(object):
+ """
+ Represents one line from the feature BDD files.
+ """
pattern = \
re.compile(r'^\s+(\*|[Ww]hen|[Ss]tep|[Tt]hen|[Gg]iven)\s+(.*)\s*$')
result = 'features'
@@ -63,43 +78,63 @@ class Feature(object):
def __str__(self):
return self.__unicode__().encode("utf-8")
-
-def ishidden(filename):
- """Is file hidden on the given OS.
-
- Later we can add some magic for non-Unix filesystems.
- """
- return filename[0] == "."
-
-
-def process_file(cdir, filename):
- PATTERNS = {'.py': Target, '.feature': Feature}
- out = {
- 'targets': [],
- 'features': []
- }
- file_ext = os.path.splitext(filename)[1]
- if file_ext in PATTERNS.keys():
- ftype = PATTERNS[file_ext]
-
- logging.debug("cdir = %s, file = %s", cdir, filename)
- ffname = os.path.join(cdir, filename)
- with io.open(ffname) as f:
- lineno = 0
- for line in f.readlines():
- lineno += 1
- matches = ftype.pattern.search(line)
- if matches:
- logging.debug("key = %s", ftype.result)
- logging.debug("value = %s", matches.group(2))
- obj = ftype(matches.group(2), ffname, lineno)
- out[ftype.result].append(obj)
-
- len_results = len(out['targets']) + len(out['features'])
- if len_results:
- logging.debug("len out = %d", len_results)
-
- return out
+ def match(self, targlist):
+ """
+ Returns the first Target matching the current Feature line.
+ """
+ for trg in targlist:
+ if trg.ismatch(self.text):
+ return trg
+
+ return None
+
+
+class SourceFile(object):
+ def __init__(self, filename):
+ self.name = filename
+
+ def ishidden(self):
+ """Is file hidden on the given OS.
+
+ Later we can add some magic for non-Unix filesystems.
+ """
+ return self.name[0] == "."
+
+
+class CodeFile(io.TextIOWrapper):
+ def __init__(self, filename):
+ filename = os.path.abspath(filename)
+ io.TextIOWrapper.__init__(self,
+ io.BufferedReader(
+ io.FileIO(filename, "r")))
+
+ def process_file(self, cdir):
+ PATTERNS = {'.py': Target, '.feature': Feature}
+ out = {
+ 'targets': [],
+ 'features': []
+ }
+ file_ext = os.path.splitext(self.name)[1]
+ if file_ext in PATTERNS.keys():
+ ftype = PATTERNS[file_ext]
+
+ logging.debug("cdir = %s, file = %s", cdir, self.name)
+ with io.open(self.name) as f:
+ lineno = 0
+ for line in f.readlines():
+ lineno += 1
+ matches = ftype.pattern.search(line)
+ if matches:
+ logging.debug("key = %s", ftype.result)
+ logging.debug("value = %s", matches.group(2))
+ obj = ftype(matches.group(2), self.name, lineno)
+ out[ftype.result].append(obj)
+
+ len_results = len(out['targets']) + len(out['features'])
+ if len_results:
+ logging.debug("len out = %d", len_results)
+
+ return out
def walker(startdir):
@@ -108,37 +143,54 @@ def walker(startdir):
for root, dirs, files in os.walk(startdir):
for directory in dirs:
- if ishidden(directory):
- dirs.remove(directory)
+ d = SourceFile(directory)
+ if d.ishidden():
+ dirs.remove(d.name)
for f in files:
- new_out = process_file(root, f)
+ in_f = CodeFile(os.path.join(root, f))
+
+ new_out = in_f.process_file(root)
feature_list.extend(new_out['features'])
target_list.extend(new_out['targets'])
return feature_list, target_list
-def match(feat, targlist):
- for trg in targlist:
- if trg.ismatch(feat):
- return trg
-
- return None
-
-
-def matcher(features, targets):
+def matcher(features, targets, out_dir):
out = []
for feat in features:
- trg = match(feat, targets)
+ trg = feat.match(targets)
if trg:
- out.append((feat, trg.filename, trg.lineno,))
+ rel_filename = os.path.relpath(trg.filename, out_dir)
+ logging.debug("feat = %s", feat)
+ logging.debug("trg.filename = %s", rel_filename)
+ logging.debug("trg.lineno = %s", trg.lineno)
+ out.append((feat, rel_filename, trg.lineno,))
return out
if __name__ == "__main__":
+ desc = """
+ Generate tags from Behave feature files and steps.
+ """
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument("-o", "--output", metavar="OUTPUT",
+ action="store", dest="output", default=None,
+ help="Name of the output file")
+ options = parser.parse_args()
+
+ logging.debug("options.output = %s", options.output)
+ if options.output:
+ outf = io.open(options.output, "w")
+ outdir = os.path.dirname(outf.name)
+ else:
+ outf = sys.stdout
+ outdir = os.curdir
+ logging.debug("outf = %s", outf)
+
raw = walker(os.curdir)
- res = matcher(raw[0], raw[1])
+ res = matcher(raw[0], raw[1], outdir)
for r in res:
- print("%s\t%s\t%s" % r)
+ outf.write(unicode("%s\t%s\t%s\n" % r))