Skip to content

Commit

Permalink
Merge pull request #180 from GatorEducator/feature/add-wildcards
Browse files Browse the repository at this point in the history
Support Wildcards in File Fragment Checking
  • Loading branch information
gkapfham authored Jul 20, 2019
2 parents dffd7c4 + 1ec76e5 commit 29413af
Show file tree
Hide file tree
Showing 13 changed files with 603 additions and 106 deletions.
5 changes: 5 additions & 0 deletions .codacy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
exclude_paths:
- 'tests/**'
- '.github/**'
- '.github/**/*'
10 changes: 6 additions & 4 deletions .gitmessage
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
#
# - The message is a single line of about 72 characters that
# contains a succinct description of the change. It contains an
# optional type, an optional scope, and a required subject
# encouraged type, an optional scope, and a required subject.
# + <type> describes the kind of change that this commit is
# providing. Allowed types are:
# * feat (add feature)
# * fix (fix defect)
# * docs (add documentation)
# * style (improve/fix formatting)
# * docs (add or modify user documentation)
# * coms (add or modify code documentation)
# * style (improve code or doc formatting)
# * refactor (improve the code, no new features)
# * test (add test(s))
# * perf (improve performance, no new features)
# * test (add or modify test(s))
# * chore (maintenance or infrastructure)
# + <scope> can be anything specifying commit change place
# + <subject> is a very short description of the change, in
Expand Down
18 changes: 9 additions & 9 deletions gator/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import re

from gator import constants

# References for the regular expressions:
# https://stackoverflow.com/questions/15423658/regular-expression-for-single-line-comments
Expand All @@ -15,35 +16,34 @@
/(?:[^#"\\]|\\.)|/\"(?:[^\"\\]|\\.)*\"|\\.)*#(.*)$"""
MULTILINECOMMENT_RE_PYTHON = r'^[ \t]*"""(.*?)"""[ \t]*$'

# Note that all of these functions also return an empty dictionary
# (i.e., {}) so that it matches the return signature of the
# other analagous function that counts the words and returns
# a dictionary of the {number of a paragraph, word count in paragraph}


def count_singleline_java_comment(contents):
"""Count the number of singleline Java comments in the code."""
pattern = re.compile(SINGLELINECOMMENT_RE_JAVA, re.MULTILINE | re.VERBOSE)
matches = pattern.findall(contents)
return len(matches), {}
matches_count = len(matches)
return matches_count, {constants.markers.First: matches_count}


def count_singleline_python_comment(contents):
"""Count the number of singleline Python comments in the code."""
pattern = re.compile(SINGLELINECOMMENT_RE_PYTHON, re.MULTILINE)
matches = pattern.findall(contents)
return len(matches), {}
matches_count = len(matches)
return matches_count, {constants.markers.First: matches_count}


def count_multiline_java_comment(contents):
"""Count the number of multiline Java comments in the code."""
pattern = re.compile(MULTILINECOMMENT_RE_JAVA, re.MULTILINE)
matches = pattern.findall(contents)
return len(matches), {}
matches_count = len(matches)
return matches_count, {constants.markers.First: matches_count}


def count_multiline_python_comment(contents):
"""Count the number of multiline Python comments in the code."""
pattern = re.compile(MULTILINECOMMENT_RE_PYTHON, re.MULTILINE | re.DOTALL)
matches = pattern.findall(contents)
return len(matches), {}
matches_count = len(matches)
return matches_count, {constants.markers.First: matches_count}
7 changes: 7 additions & 0 deletions gator/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def create_constants(name, *args, **kwargs):
# define the environment variables for the program
environmentvariables = create_constants("environmentvariables", Home="GATORGRADER_HOME")

# define reference function names in the program
functions = create_constants("functions", Count_Total_Words="count_total_words")

# define the programming languages for comment checks
languages = create_constants("languages", "Java", "Python")

Expand All @@ -45,6 +48,10 @@ def create_constants(name, *args, **kwargs):
Nothing="",
Space=" ",
Tab=" ",
File="file",
Of_File="of file",
In_A_File="in a file",
First=1,
Invalid=-1,
)

Expand Down
56 changes: 45 additions & 11 deletions gator/entities.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Count entities with provided functions."""

from gator import util
from gator import constants
from gator import files
from gator import util


# pylint: disable=bad-continuation
Expand All @@ -21,23 +22,56 @@ def entity_greater_than_count(


def count_entities(given_file, containing_directory, checking_function):
"""Count the number of entities for the file in the directory."""
"""Count the number of entities for the file(s) in the directory."""
# create an empty dictionary of filenames and an internal dictionary
file_counts_dictionary = {}
# if the file is not found in the directory, the count is zero
file_contents_count = 0
# create a path for the given file and its containing directory
# note that this call does not specify any *args and thus there
# are no directories between the home directory and the file
file_for_checking = files.create_path(file=given_file, home=containing_directory)
# start the count of the number of entities at zero, assuming none found yet
file_contents_count = 0
# create an empty dictionary of the counts
file_contents_count_dictionary = {}
# a valid file exists and thus it is acceptable to perform the checking
if file_for_checking.is_file():
for file_for_checking in files.create_paths(
file=given_file, home=containing_directory
):
# start the count of the number of entities at zero, assuming none found yet
file_contents_count = 0
# create an empty dictionary of the counts
file_contents_count_dictionary = {}
# a valid file exists and thus it is acceptable to perform the checking
# extract the text from the file_for_checking
file_contents = file_for_checking.read_text()
# use the provided checking_function to check the contents of the file
# note this works since Python supports passing a function to a function
# print(checking_function)
file_contents_count, file_contents_count_dictionary = checking_function(
file_contents
)
return file_contents_count, file_contents_count_dictionary
# the checking_function returned a dictionary of form {entity: count}
# so we should store this dictionary insider the containing dictionary
# this case would occur for checks like number of words in paragraphs
if file_contents_count_dictionary:
# associate these file counts with the filename in a dictionary
file_counts_dictionary[
file_for_checking.name
] = file_contents_count_dictionary
# the checking_function did not return a dictionary because that was
# not sensible for the type of check (e.g., counting paragraphs)
# so we should make a "dummy" dictionary containing the entity count
else:
file_contents_count_dictionary = {1: file_contents_count}
file_counts_dictionary[
file_for_checking.name
] = file_contents_count_dictionary
# find the minimum count for all paragraphs across all of the files
# assume that nothing was found and the count is zero and prove otherwise
file_contents_count_overall = file_contents_count
# there is a dictionary of counts for files, so deeply find the minimum
# as long as the count is not of the total words in a file
if (
file_counts_dictionary
and checking_function.__name__ != constants.functions.Count_Total_Words
):
file_contents_count_overall = util.get_first_minimum_value_deep(
file_counts_dictionary
)[1][1]
# return the overall requested count and the nested file count dictionary
return file_contents_count_overall, file_counts_dictionary
14 changes: 14 additions & 0 deletions gator/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from gator import constants

from glob import glob
from pathlib import Path


Expand All @@ -10,6 +11,19 @@ def create_cwd_path():
return Path.cwd()


def create_paths(*args, file="", home):
"""Create a generator of Path objects for a glob with varying sub-path count."""
# attempt to create the path that could contain:
# --> a glob (e.g., *.py) or
# --> a single file (e.g., hello.py)
# Pathlib does not support globs of absolute directories, so use glob
# to create a list of all files matched by the glob
file_or_glob_path = create_path(*args, file=file, home=home)
home_directory_globbed = [Path(p) for p in glob(str(file_or_glob_path))]
# return this list of Path objects resulting from glob application
return home_directory_globbed


def create_path(*args, file="", home):
"""Create a Path object for a file with varying sub-path count."""
# create the Path for the home
Expand Down
30 changes: 18 additions & 12 deletions gator/fragments.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def specified_entity_greater_than_count(
)
# also return an empty dictionary since this function does not
# need to count details about multiple entities
return condition_truth, value, {}
return condition_truth, value, file_entity_count_dictionary


# pylint: disable=bad-continuation
Expand All @@ -184,24 +184,30 @@ def count_entities(
contents=constants.markers.Nothing,
):
"""Count fragments for the file in the directory (or contents) and a fragment."""
# create a Path object to the chosen file in the containing directory
file_for_checking = files.create_path(file=given_file, home=containing_directory)
file_contents_count = 0
file_contents_count_dictionary = {}
# file is not available and the contents are provided
# the context for this condition is when the function checks
# the output from the execution of a specified command
if not file_for_checking.is_file() and contents is not constants.markers.Nothing:
if (
contents is not constants.markers.Nothing
and given_file is constants.markers.Nothing
):
file_contents_count = checking_function(contents, chosen_fragment)
# file is available and the contents are not provided
# the context for this condition is when the function checks
# the contents of a specified file
elif file_for_checking.is_file() and contents is constants.markers.Nothing:
# read the text from the file and the check for the chosen fragment
return file_contents_count, file_contents_count_dictionary
for file_for_checking in files.create_paths(
file=given_file, home=containing_directory
):
# an actual file is available and command contents are not provided
# the context for this condition is when the function checks file contents
# read the text from the file and then check for the chosen fragment
file_contents = file_for_checking.read_text()
file_contents_count = checking_function(file_contents, chosen_fragment)
# also return an empty dictionary since this function does not
# need to count details about multiple entities
return file_contents_count, {}
file_contents_count_dictionary[file_for_checking.name] = file_contents_count
# return the minimum value and the entire dictionary of counts
minimum_pair = util.get_first_minimum_value(file_contents_count_dictionary)
file_contents_count = minimum_pair[1]
return file_contents_count, file_contents_count_dictionary


# pylint: disable=bad-continuation
Expand Down
Loading

0 comments on commit 29413af

Please sign in to comment.