Question
Assignment 2: Objective: practice Python programming with string processing, collections, and file I/O Understanding a large program usually requires knowledge of the calling relationships between
Assignment 2:
Objective: practice Python programming with string processing, collections, and file I/O
Understanding a large program usually requires knowledge of the calling relationships between functions or methods as well as dependencies between their larger units of modularization, such as classes. Oftentimes, when we're trying to study function FA, we need to know which other functions call it. And if there are numerous calling functions, we sometimes just need to know which classes they belong to, for future investigation.
In this programming assignment, the input is a text file with the following format:
C1.f1 calls C1.f2 C1.f3 calls C1.f2 C2.f4 calls C1.f3 C2.f4 calls C2.f5 C2.f4 calls C3.f1 C1.f1 calls C1.f1 C2.f5 calls C3.f1
That is, each line tells you which function calls which other function, as qualified by their class name. Similar to Java, functions in different classes may have the same names, hence the class qualifier. Note that, as in the second to last line, a function might call itself. For the purpose of this project, assume that all lines will be of this format: ClassA.functionB calls ClassX.functionY, that is, each function listed is part of a class.
Requirements:
Your program will read the file and store the relationships in a collection structure. It is up to you to determine the proper collection(s) to use.
Next, your program will output the following reports to standard output:
Called by relationships - list which functions call a given function, in a format like this:
Called by relationships: C1.f1 is called by C1.f1 C1.f2 is called by C1.f1,C1.f3 C1.f3 is called by C2.f4 C2.f4 is not called by any function C2.f5 is called by C2.f4 C3.f1 is called by C2.f4,C2.f5
If a function is called from multiple functions, each of those calling functions are listed in a comma-separated format. Note that this output should be sorted alphabetically.Class dependencies - list which class uses functions from which other class, like this:
Class dependencies: C2 uses C1,C3
If a class uses multiple classes, each of those are listed in a comma-separated format. Note that self relationships are excluded from this list (so you won't see C1 uses C1 or C2 uses C1, C2, C3 and so on) since that is nearly true with every class usually having functions that call other functions inside it.Class reverse dependencies - this is the reverse relationship from the previous output. List which class are used by other classes, like this:
Class reverse dependencies: C1 is used by C2 C3 is used by C1
As in the previous output, self relationships (e.g., C2 is used by C2) are excluded from this list. If a class is used by multiple classes, each of those are listed in a comma-separated format.
Additional Requirements
Handle exceptions for incorrect input format. For example, the following lines should be flagged:
C1 calls C2.f2 # C1 is missing a function C1.f1 # does not call anything C1.f1.f2 calls C3.f3 # multiple qualifiers on f2
Just print the error as incorrect input line with the line itself and ignore the line in subsequent processing.
There should be at least 2 classes, a DataExtractor class that includes responsibilities for reading the file and putting the inputs into an appropriate collection, and a GraphAnalysis class that performs the necessary analysis of the input data and produces the 3 outputs outlined above. Optionally, the actual generation of outputs can also be separated into its own class. You can define more classes than these according to your design preference.
Include a docstring for each major function you create explaining what its responsibilities are.
Submit one Python file for each class as well as a separate Python file that calls these, and your own test data file with at least 10 valid lines.
Code:
class DataExtractor:
def __init__(self, filename):
self.filename = filename
def load_data(self):
data = []
# open the file and read each line
with open(self.filename, 'r') as f:
for line in f:
# split the line into the two components
parts = line.split(' calls ')
if len(parts) != 2:
# invalid format
print('Incorrect input line: {}'.format(line))
continue
caller, callee = parts
# append the data to the list
data.append((caller, callee))
return data
GraphAnalysis.py
"""
This class is responsible for analyzing the input graph
and generating the 3 reports outlined in the problem.
"""
class GraphAnalysis:
def __init__(self, data):
self.data = data
def called_by_relationships(self):
# create a dict to store the data
d = {}
# iterate through the data
for caller, callee in self.data:
# update the dict
if callee in d:
d[callee].append(caller)
else:
d[callee] = [caller]
# print the report
print('Called by relationships:')
for callee, callers in d.items():
# sort the callers
callers.sort()
# convert to comma-separated string
callers = ', '.join(callers)
print('{} is called by {}'.format(callee, callers))
def class_dependencies(self):
# create a set to store the data
s = set()
# iterate through the data
for caller, callee in self.data:
# extract the classes
caller_class, callee_class = caller.split('.')[0], callee.split('.')[0]
# add to the set
if caller_class != callee_class:
s.add((caller_class, callee_class))
# print the report
print('Class dependencies:')
for caller_class, callee_class in sorted(s):
print('{} uses {}'.format(caller_class, callee_class))
def class_reverse_dependencies(self):
# create a dict to store the data
d = {}
# iterate through the data
for caller, callee in self.data:
# extract the classes
caller_class, callee_class = caller.split('.')[0], callee.split('.')[0]
# update the dict
if callee_class in d:
d[callee_class].append(caller_class)
else:
d[callee_class] = [caller_class]
# print the report
print('Class reverse dependencies:')
for callee_class, caller_classes in d.items():
# remove self relationships
if callee_class in caller_classes:
caller_classes.remove(callee_class)
# sort the callers
caller_classes.sort()
# convert to comma-separated string
caller_classes = ', '.join(caller_classes)
print('{} is used by {}'.format(callee_class, caller_classes))
main.py
"""
This file is responsible for calling the DataExtractor and
GraphAnalysis classes to produce the desired output.
"""
from DataExtractor import DataExtractor
from GraphAnalysis import GraphAnalysis
if __name__ == '__main__':
# create the DataExtractor
de = DataExtractor('input.txt')
# load the data
data = de.load_data()
# create the GraphAnalysis
ga = GraphAnalysis(data)
# generate the reports
ga.called_by_relationships()
ga.class_dependencies()
ga.class_reverse_dependencies()
input.txt
C1.f1 calls C1.f2
C1.f3 calls C1.f2
C2.f4 calls C1.f3
C2.f4 calls C2.f5
C2.f4 calls C3.f1
C1.f1 calls C1.f1
C2.f5 calls C3.f1
(the example from Programming Assignment 2 with a duplicate entry in line 4), the program should yield the same outputs as before. Write at least one test case for each scenario. Fix the code if the test fails. Notes: 1. Put the test cases in a separate file. It is a good programming practice to separate the test code so it eventually does not get pulled into the production code. 2. You will need to read ahead to the chapter on unit testing which will be covered in Reading Assignment 10. 3. To simplify your work, you don't have to worry about complete code coverage (exercising every statement in the program). For example, scenario 2 has several different situations - just test for one of them. 4. As you reexamine your code in Programming Assignment 2, consider also opportunities to make the code more Pythonic by making use of comprehensions and iterables. Conceivably, you can use a generator to create some random test data also. These are all optional improvements, and good practice as a Python programmer
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started