Conversation
|
|
Functional AssessmentVerdict: ✅ Completed🧠 User Story ID: TDRS-001-A — Code Changes Implementation📝 Feature CompletenessThe Requirement was.. Integrate specific code modifications into the existing codebase without build errors and ensure updated logic functions correctly. This is what is built... Implemented a wide range of Python scripts covering algorithms, data structures, and utility functions including sorting, trees, and networking. 📊 Implementation Status
✅ Completed Components
🎯 Conclusion & Final AssessmentImportant 🟢 Completed Features: Key completed features include the implementation of various algorithms (Sorting, BST, Graphs), utility scripts (JSON, CSV, Pickle), and functional programming concepts (Closures, Decorators). |
| # Closures | ||
|
|
||
| import logging | ||
| logging.basicConfig(filename='example.log', level=logging.INFO) |
There was a problem hiding this comment.
Hardcoded File Path for Logging
I noticed the logging is configured to write to a hardcoded file example.log. This can cause problems in environments where the filesystem is read-only or ephemeral, like in containers or serverless functions. It's a best practice to log to standard output (stdout/stderr) and let the execution environment handle log aggregation. Let's remove the filename argument to log to the console instead.
| logging.basicConfig(filename='example.log', level=logging.INFO) | |
| logging.basicConfig(level=logging.INFO) |
| return data | ||
|
|
||
| if __name__ == '__main__': | ||
| data = loadJSON('example.json') |
There was a problem hiding this comment.
Hardcoded Filename for Data Storage
I see the script reads from and writes to a hardcoded file named example.json. This can create issues when moving the application between different environments (dev, staging, prod) or running it in a container where file paths might not be guaranteed. Let's make this configurable by using an environment variable for the file path.
| # csvWrite('example.csv', data) | ||
| userInput = input('What is your Fullname? ') | ||
| userInput = userInput.split(' ') | ||
| csvWrite('example.csv', userInput) |
There was a problem hiding this comment.
Hardcoded Filename for Data Storage
I noticed the script reads from and writes to a hardcoded file named example.csv. This can create issues when moving the application between different environments or running it in a container where file paths might not be guaranteed. Let's make this configurable by using an environment variable for the file path.
| db['Omkar'] = Omkar | ||
| db['Jagdish'] = Jagdish | ||
|
|
||
| dbfile = open('examplePickle', 'ab') # Its important to use binary mode |
There was a problem hiding this comment.
Hardcoded Filename for Data Persistence
I see the data is being stored in a hardcoded file named examplePickle. This can cause issues with portability and configuration management when deploying to different environments (e.g., dev, staging, prod). It's better to externalize this configuration. Let's use an environment variable to specify the path to the data file.
| self.email = email | ||
| self.phone = phone | ||
| self.contacts = {} | ||
| self.filename = 'addressbook' |
There was a problem hiding this comment.
Hardcoded Filename for Address Book
I noticed the filename for the address book is hardcoded as addressbook. This can cause issues when running the application in different environments or if you need to manage multiple address books. It's a best practice to make file paths configurable. Let's pass the filename in the __init__ method or read it from an environment variable.
| self.filename = 'addressbook' | |
| self.filename = os.getenv('ADDRESS_BOOK_FILE', 'addressbook') |
⚙️ DevOps and Release Automation🟢 Status: Passed🌟 Excellent work! Your code passed the DevOps review. Some improvements are suggested which will greatly improve the maintainability of your infrastructure. 🟢 Minor Suggestions
🎯 Conclusion
Important Please carefully assess each DevOps and migration violation's impact before proceeding to ensure smooth transitions between environments. |
| # Closures | ||
|
|
||
| import logging | ||
| logging.basicConfig(filename='example.log', level=logging.INFO) |
There was a problem hiding this comment.
JAS - Just a suggestion
Hardcoded Log File Path
I noticed the log file path is hardcoded as 'example.log'. This can create problems with file permissions, path conflicts in different environments (like development vs. production), or when running inside a container. Let's make this configurable. It's a best practice to read the log file path from an environment variable, which allows for greater flexibility during deployment.
| logging.basicConfig(filename='example.log', level=logging.INFO) | |
| import os | |
| log_file = os.getenv('LOG_FILE_PATH', 'example.log') | |
| logging.basicConfig(filename=log_file, level=logging.INFO) |
| return data | ||
|
|
||
| if __name__ == '__main__': | ||
| data = loadJSON('example.json') |
There was a problem hiding this comment.
JAS - Just a suggestion
Hardcoded Filename for Data Storage
I see the filename 'example.json' is hardcoded for data operations. This can make the script difficult to reuse and can lead to file path issues when deploying to different environments (e.g., dev, staging, prod) or running in containers. Let's make this configurable by reading the filename from an environment variable. This will make your script much more flexible.
| data = loadJSON('example.json') | |
| import os | |
| if __name__ == '__main__': | |
| json_file = os.getenv('DATA_FILE_PATH', 'example.json') | |
| data = loadJSON(json_file) | |
| print(data['menu']['value']) # File | |
| data['menu']['value'] = 'movie' | |
| storeJSON(json_file, data) | |
| print() | |
| loadJSON(json_file) | |
| print(data['menu']['value']) # movie |
| db['Omkar'] = Omkar | ||
| db['Jagdish'] = Jagdish | ||
|
|
||
| dbfile = open('examplePickle', 'ab') # Its important to use binary mode |
There was a problem hiding this comment.
JAS - Just a suggestion
Hardcoded Filename for Data Storage
I noticed the filename 'examplePickle' is hardcoded for storing pickled data. This can cause issues with file permissions or path conflicts when moving the application between different environments (like dev and prod) or running it in a container. A better approach is to make the path configurable. Let's use an environment variable to specify the file path, which provides much more deployment flexibility.
| dbfile = open('examplePickle', 'ab') # Its important to use binary mode | |
| import os | |
| dbfile_path = os.getenv('DB_FILE_PATH', 'examplePickle') | |
| dbfile = open(dbfile_path, 'ab') |
| self.email = email | ||
| self.phone = phone | ||
| self.contacts = {} | ||
| self.filename = 'addressbook' |
There was a problem hiding this comment.
JAS - Just a suggestion
Hardcoded Filename for Data Storage
I see the filename for the address book is hardcoded as 'addressbook'. This can create operational challenges, such as managing different data files for development, testing, and production environments, or running into permission issues inside containers. Let's make this configurable. A good practice is to read the file path from an environment variable, providing a default if it's not set.
| self.filename = 'addressbook' | |
| self.filename = os.getenv('ADDRESS_BOOK_PATH', 'addressbook') |
🔍 Technical Quality Assessment📋 SummaryThis update adds several new tools and educational scripts to our system, including a digital address book, a secret message encoder, and various data sorting methods. While these tools add new capabilities, several critical errors were found that could cause the system to crash or allow unauthorized access to information. We need to fix these safety and reliability issues before these tools can be used by our team or customers. 💼 Business Impact
🎯 Purpose & Scope
📊 Change AnalysisFiles by Category:
Impact Distribution:
|
| File | Status | Description | Impact | Issues Detected |
|---|---|---|---|---|
Numpy/P40_CipherText.py |
Added ( +59/ -0) | Implementation of a Caesar cipher for text encryption and decryption using a shift key. | High – The encryption and decryption logic contains mathematical errors in the wrap-around handling, which will lead to incorrect character mapping and data corruption during the transformation process. | 2 |
Numpy/P41_PortScanner.py |
Added ( +26/ -0) | A simple Python port scanner that checks for open ports in the range 1-1023 on a given host. | Medium – The script provides basic network scanning functionality but has significant performance and reliability issues due to synchronous execution and lack of specific error handling. | 2 |
Numpy/P42_MultiprocessingPipes.py |
Added ( +25/ -0) | Added a script demonstrating inter-process communication using multiprocessing Pipes. | Low – The script provides an educational example of using Pipes for synchronization and data transfer between processes. | 1 |
Numpy/P43_BinarySearchTree.py |
Added ( +145/ -0) | Implementation of a Binary Search Tree (BST) with insertion, search, and traversal methods (preorder, inorder, postorder), along with a tree visualization utility. | Medium – The code provides a functional BST implementation, but contains a critical recursion depth risk in the visualization utility and a mutable default argument in the constructor that could lead to shared state across instances. | 2 |
Numpy/P44_Closures.py |
Added ( +21/ -0) | Added a Python script demonstrating the concept of closures with an outer and inner function. | Low – This is an educational script and does not affect core application functionality. | 1 |
Numpy/P45_MoreOnClosures.py |
Added ( +27/ -0) | Added a script demonstrating Python closures using a logging decorator pattern. | Low – The changes introduce a utility for logging function calls, which is primarily for educational or debugging purposes. | 1 |
Numpy/P46_Decorators.py |
Added ( +21/ -0) | Added a Python script demonstrating the use of decorators with a simple example involving a higher-order function and function metadata. | Low – This is an educational script and does not affect core application functionality. | 1 |
Numpy/P47_MoreOnDecorators.py |
Added ( +27/ -0) | Added a BankAccount class demonstrating the use of @Property and @fullName.setter decorators for managing first and last names. | Medium – The changes introduce a new class with property management. However, the setter implementation is fragile and will crash if input doesn't strictly follow a specific format. | 1 |
Numpy/P48_CountingSort.py |
Added ( +52/ -0) | Implementation of the Counting Sort algorithm in Python. | Medium – Provides a sorting utility, but contains logic that will fail with negative integers or empty lists. | 2 |
Numpy/P49_RockPaperScissors.py |
Added ( +51/ -0) | Implementation of a Rock Paper Scissors game using random selection and user input. | Low – The changes introduce a standalone CLI game. While functional, the error handling is overly aggressive, terminating the process on any input error. | 1 |
Numpy/P50_ListComprehensions.py |
Added ( +52/ -0) | Added a tutorial file demonstrating Python list comprehensions with various examples including basic loops, squares, filtering odds, and using functions within comprehensions. | Low – This is an educational script and does not affect core application functionality. | 1 |
Numpy/P51_PythonJSON.py |
Added ( +25/ -0) | Added utility functions for storing and loading JSON data using Python's built-in json module. | Medium – Provides reusable functions for JSON persistence, but contains a mutable default argument risk and potential file handling issues. | 2 |
Numpy/P52_BucketSort.py |
Added ( +55/ -0) | Implementation of the Bucket Sort algorithm using Insertion Sort for individual buckets. | Medium – The implementation provides a functional bucket sort but contains a critical logic error regarding empty list handling and potential index out of bounds for specific input ranges. | 2 |
Numpy/P53_ShellSort.py |
Added ( +28/ -0) | Added a Python implementation of the Shell Sort algorithm using the standard Shell gap sequence (n/2). | Low – This is a standalone utility script for sorting lists. It implements a standard algorithm correctly but uses a sub-optimal gap sequence for large datasets. | 1 |
Numpy/P54_PythonCSV.py |
Added ( +30/ -0) | Added a script to demonstrate reading from and appending to CSV files using the Python csv module. | Medium – The script provides basic CSV operations but contains potential runtime errors related to file handling and data indexing. | 3 |
Numpy/P55_Isogram.py |
Added ( +29/ -0) | Added a new script to check if a word or phrase is an isogram, including logic to handle case-insensitivity and ignore non-alphabetic characters. | Low – The changes introduce a standalone utility function for isogram validation with several test cases. | 1 |
Numpy/P56_Pangram.py |
Added ( +36/ -0) | Added two implementations of a pangram checker (pangram and pangram2) along with test cases. | Low – The changes introduce utility functions for string analysis and validation. | 1 |
Numpy/P57_Anagram.py |
Added ( +27/ -0) | Added a script to find anagrams of a word from a given list using the collections.Counter class. | Low – The changes introduce a standalone utility function for anagram detection with basic case-insensitive matching. | 1 |
Numpy/P58_PerfectNumber.py |
Added ( +21/ -0) | Added a script to check if a given number is a perfect number by calculating the sum of its proper divisors. | Low – The script provides a functional implementation for identifying perfect numbers, though it uses a sub-optimal O(n) approach. | 1 |
Numpy/P59_PascalTriangle.py |
Added ( +32/ -0) | Added a script to generate and print Pascal's Triangle using a recursive approach. | Medium – The implementation uses a recursive approach to generate rows, which is functionally correct for small inputs but inefficient for larger triangles due to redundant calculations. | 1 |
Numpy/P60_PickleModule.py |
Added ( +32/ -0) | Added a script demonstrating how to use the pickle module for object serialization and deserialization in Python. | Low – This is an educational example script and does not affect core application functionality. | 2 |
Numpy/P61_AddressBook.py |
Added ( +150/ -0) | Implemented a CLI-based Address Book application using pickle for data persistence. | Medium – The application provides basic CRUD operations for contacts but contains several critical logic and security flaws regarding data handling and resource management. | 4 |
Numpy/P62_BinaryTree.py |
Added ( +82/ -0) | Implementation of a Binary Tree data structure with insertion and printing utilities. | Medium – Provides a basic binary tree implementation, but contains logical errors in the left insertion method that will lead to incorrect tree structures. | 2 |
Numpy/P63_Graph.py |
Added ( +80/ -0) | Implementation of a basic Graph data structure using an adjacency list (dictionary of Vertex objects). | Medium – Provides a foundational graph implementation but contains a critical error handling flaw and a potential logic issue in edge creation. | 2 |
Numpy/P64_DepthFirstTraversal.py |
Added ( +82/ -0) | Implementation of a Binary Tree Node class and depth-first search traversal algorithms (Inorder, Preorder, Postorder). | Medium – Provides core data structure and traversal logic for binary trees, though currently limited to basic functionality. | 1 |
Numpy/P65_BreadthFirstTraversal.py |
Added ( +51/ -0) | Implementation of Breadth First Traversal (Level Order Traversal) for a binary tree using a recursive approach based on height. | Medium – Provides a functional implementation of BFS for binary trees, though the current recursive approach has O(N^2) time complexity in the worst case. | 2 |
Numpy/P66_HeapSort.py |
Added ( +50/ -0) | Implementation of the Heap Sort algorithm using a max-heap approach. | Medium – Provides a standard sorting utility; however, a logical indexing error in the heap construction phase affects its correctness. | 1 |
Numpy/P67_SieveOfEratosthenes.py |
Added ( +34/ -0) | Implementation of the Sieve of Eratosthenes algorithm to find prime numbers up to a given limit n. | Medium – Adds a new utility function for prime number generation, but contains a logic error in the output range. | 1 |
Numpy/P68_TopologicalSort.py |
Added ( +66/ -0) | Implementation of Topological Sort algorithm using a directed graph and depth-first search. | High – The current implementation contains a critical logic error in edge addition that corrupts the graph structure and an error handling flaw that results in incomplete sorting results. | 2 |
Numpy/P69_ReverseWords.py |
Added ( +12/ -0) | Added a Python script to reverse the order of words in a user-provided string. | Low – This is a standalone utility script for string manipulation and does not affect existing system functionality. | 1 |
| num += key | ||
| if num>25: | ||
| num=num%25 | ||
| num=num-1 |
There was a problem hiding this comment.
Flawed Wrap-around Logic in Encryption
I notice the wrap-around logic here uses num % 25 and subtracts 1. Since the alphabet has 26 characters, this will cause incorrect shifts. For example, with a key of 26, 'y' would incorrectly become 'z'. We should use the standard modulo 26 approach to ensure consistent rotation.
| num += key | |
| if num>25: | |
| num=num%25 | |
| num=num-1 | |
| num = (num + key) % 26 |
| if num>25: | ||
| num=num%25 | ||
| num=num-1 | ||
| num = num -key |
There was a problem hiding this comment.
Unreachable and Incorrect Logic in Decryption
In the decryption function, the check num > 25 is unreachable because LETTERS.find() only returns values from 0 to 25. Additionally, the subtraction of the key can result in negative indices. While Python handles negative indices, it's safer and clearer to use the modulo operator for the reverse shift.
| if num>25: | |
| num=num%25 | |
| num=num-1 | |
| num = num -key | |
| num = (num - key) % 26 |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| connection = s.connect_ex((host, port)) #NOTE: connect() needs a tuple! |
There was a problem hiding this comment.
Performance Issue: Sequential Port Scanning
Scanning ports sequentially from 1 to 1024 using a single thread is extremely slow, as each connection attempt must wait for a timeout. We should use a timeout on the socket to prevent the script from hanging on filtered ports.
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| connection = s.connect_ex((host, port)) #NOTE: connect() needs a tuple! | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| s.settimeout(0.5) | |
| connection = s.connect_ex((host, port)) |
| for port in range(1, 1024): | ||
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| connection = s.connect_ex((host, port)) #NOTE: connect() needs a tuple! | ||
| if (connection == 0): | ||
| print('Port {} is open'.format(port)) | ||
| s.close() |
There was a problem hiding this comment.
Logic Error: Broad Exception Handling
The socket.error block is placed outside the loop, meaning any single connection failure (like a temporary network glitch) will terminate the entire scanning process for all remaining ports. We should move the error handling inside the loop or handle specific errors more gracefully.
| for port in range(1, 1024): | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| connection = s.connect_ex((host, port)) #NOTE: connect() needs a tuple! | |
| if (connection == 0): | |
| print('Port {} is open'.format(port)) | |
| s.close() | |
| for port in range(1, 1024): | |
| try: | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| s.settimeout(0.1) | |
| connection = s.connect_ex((host, port)) | |
| if (connection == 0): | |
| print('Port {} is open'.format(port)) | |
| s.close() | |
| except socket.error: | |
| continue |
Reasons & Gaps
Reasons
- Catching socket.error outside the loop stops the scan on the first failure
- Individual port failures should not prevent the scanning of subsequent ports
- Moving try-except inside the loop ensures the scanner continues despite errors
Gaps
- The developer might intend to stop on any network failure to prevent false negatives
- Specific network environments might require different retry or skip logic
| process1.start() # Start the parent process | ||
| process2.start() # Start the child process | ||
| print(parent.recv()) # Display data received from child (BYE) | ||
| print(child.recv()) # Display data received from parent (HELLO) |
There was a problem hiding this comment.
Potential Deadlock or EOFError in Pipe Communication
I notice we're calling recv() on both ends of the pipe in the main process while also passing those same ends to child processes. In multiprocessing, it's a best practice to close the unused end of a pipe in each process. If the main process keeps both ends open, recv() might hang or behave unexpectedly because the pipe remains 'open' even after the child processes finish. We should ensure each process only handles one end of the communication channel.
| process1.start() # Start the parent process | |
| process2.start() # Start the child process | |
| print(parent.recv()) # Display data received from child (BYE) | |
| print(child.recv()) # Display data received from parent (HELLO) | |
| # It is recommended to close the unused pipe ends in the main process if they are fully delegated to children. | |
| # However, since the main process is acting as the receiver here, we should ensure the child processes | |
| # only have access to the end they are actually using to avoid descriptor leaks. | |
| process1.start() | |
| process2.start() | |
| print(parent.recv()) | |
| print(child.recv()) |
Reasons & Gaps
Reasons
- Keeping both ends of a Pipe open in the parent process can prevent EOF detection
- Improper management of Pipe ends often leads to resource leaks or deadlocks in complex apps
- Standard multiprocessing patterns dictate closing the unused end in each process
Gaps
- The script is a simple example where the small data size might prevent a full deadlock
- Behavior of duplicated file descriptors varies slightly between OS spawn/fork methods
| # is invoked outside their scope. | ||
|
|
||
| def outerFunction(text): | ||
| text = text |
There was a problem hiding this comment.
JAS - Just a suggestion
Redundant Variable Assignment
In the outerFunction, the line text = text is a self-assignment that doesn't perform any useful operation. Since text is already available in the local scope as a parameter, this line can be safely removed to keep the code clean and focused on the closure demonstration.
| def log_func(*args): | ||
| logging.info( | ||
| 'Running "{}" with arguments {}'.format(func.__name__, args)) | ||
| print(func(*args)) |
There was a problem hiding this comment.
Potential Runtime Error: Missing Return Value in Wrapper
I noticed that the log_func wrapper prints the result of the function call but doesn't return it. This means any code calling add_logger or sub_logger will receive None instead of the actual calculation result. We should return the result so the decorated function behaves like the original one.
| print(func(*args)) | |
| result = func(*args) | |
| print(result) | |
| return result |
| # especially after we go over a number of examples. | ||
|
|
||
| def decorator(myFunc): | ||
| def insideDecorator(*args): |
There was a problem hiding this comment.
Missing Metadata Preservation in Decorator
I notice that while the decorator works for execution, it doesn't preserve the metadata (like docstrings or the function name) of the original function. This can make debugging harder and breaks tools that rely on introspection. We should use functools.wraps to ensure the decorated function keeps its original identity.
| def insideDecorator(*args): | |
| @functools.wraps(myFunc) | |
| def insideDecorator(*args): |
Reasons & Gaps
Reasons
- Decorated functions lose their original name and doc attributes without wraps
- Introspection tools and stack traces will show 'insideDecorator' instead of 'display'
- Standard Python practice requires using functools.wraps for robust decorator implementation
Gaps
- The script is intended for educational purposes where metadata preservation might be considered an advanced topic
- The current implementation technically executes correctly for the provided example
| def fullName(self, name): | ||
| firstName, lastName = name.split(' ') | ||
| self.firstName = firstName | ||
| self.lastName = lastName |
There was a problem hiding this comment.
Fragile String Splitting in Setter
I notice that the fullName setter assumes the input string will always contain exactly one space. If a user provides a name without a space (e.g., 'Jagdish') or with multiple spaces, this will trigger a ValueError. We should handle these cases gracefully to prevent the application from crashing.
| def fullName(self, name): | |
| firstName, lastName = name.split(' ') | |
| self.firstName = firstName | |
| self.lastName = lastName | |
| def fullName(self, name): | |
| parts = name.split(' ', 1) | |
| self.firstName = parts[0] | |
| self.lastName = parts[1] if len(parts) > 1 else '' |
| # values range from 0 to k. | ||
|
|
||
| def countingSort(myList): | ||
| maxValue = 0 |
There was a problem hiding this comment.
Potential IndexError on Empty Input
If myList is empty, the loop at line 33 won't execute, leaving maxValue as 0. This results in buckets being size 1. However, if the list is empty, the subsequent logic might still be fragile. More importantly, if the list is empty, the function should ideally handle it gracefully before attempting to find a max value.
| maxValue = 0 | |
| if not myList: | |
| return myList | |
| maxValue = 0 |
Reasons & Gaps
Reasons
- Prevents unnecessary execution of sorting logic on empty collections
- Improves code robustness by explicitly defining behavior for edge cases
- Avoids potential issues with range calculations on empty input sets
Gaps
- While the current code doesn't crash on an empty list, it performs unnecessary allocations and loops.
- Explicitly handling empty lists is a defensive programming best practice to avoid edge-case logic errors.
| for i in myList: | ||
| buckets[i] += 1 |
There was a problem hiding this comment.
Logic Error: Negative Integer Support
Counting sort as implemented here assumes all integers are non-negative. If myList contains negative numbers, buckets[i] at line 40 will either access the list from the end (incorrect logic) or throw an IndexError if the negative value is out of bounds. We should either document this limitation or adjust the logic to handle a range.
| for i in myList: | |
| buckets[i] += 1 | |
| for i in myList: | |
| if i < 0: | |
| raise ValueError("Counting sort implementation only supports non-negative integers.") | |
| buckets[i] += 1 |
| except: | ||
| exit() |
There was a problem hiding this comment.
Overly Broad Exception Handling
I notice we're using a bare except: block that calls exit(). This is a bit risky because it will catch everything, including keyboard interrupts (Ctrl+C) or system signals, and silently close the program. It's usually better to catch specific errors like ValueError or EOFError so we don't accidentally mask other issues or prevent the user from exiting normally.
| except: | |
| exit() | |
| except (EOFError, KeyboardInterrupt): | |
| print('\nGame terminated.') | |
| exit() |
| def isSqaure(x): | ||
| import math | ||
| sqrt = int(math.sqrt(x)) | ||
| return x == sqrt * sqrt |
There was a problem hiding this comment.
JAS - Just a suggestion
Inefficient Local Import in Loop Context
I noticed that import math is placed inside the isSqaure function. Since this function is called 100 times within the list comprehension on line 47, Python has to perform an import check for every single iteration. While Python caches imports, it's still more efficient and standard practice to move the import to the top of the file to avoid unnecessary overhead during execution.
| import json | ||
|
|
||
| # For storing on json format | ||
| def storeJSON(fileName, data = {}): |
There was a problem hiding this comment.
Mutable Default Argument Risk
I noticed we're using an empty dictionary {} as a default value for the data parameter. In Python, default arguments are evaluated only once at definition time. If this dictionary is modified within the function, those changes will persist across subsequent calls, which can lead to very confusing bugs. It's safer to use None and initialize it inside the function.
| def storeJSON(fileName, data = {}): | |
| def storeJSON(fileName, data=None): | |
| if data is None: | |
| data = {} |
| return data | ||
|
|
||
| if __name__ == '__main__': | ||
| data = loadJSON('example.json') |
There was a problem hiding this comment.
Potential Runtime Crash: Missing File Handling
We're calling loadJSON at the very start of the execution. If example.json doesn't exist yet, the application will crash with a FileNotFoundError. Since this script seems designed to both read and write, we should handle the case where the file is missing or empty to make the script more robust.
| data = loadJSON('example.json') | |
| try: | |
| data = loadJSON('example.json') | |
| except (FileNotFoundError, json.JSONDecodeError): | |
| data = {'menu': {'value': 'File'}} |
Reasons & Gaps
Reasons
- Opening a non-existent file for reading triggers a FileNotFoundError
- Unhandled exceptions in the main execution block cause immediate crashes
- Initializing default data prevents failure when starting from scratch
Gaps
- The script might be intended to run only when the file is present
- The specific structure of 'example.json' is assumed but not guaranteed
| # Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) | ||
|
|
||
| def shellSort(myList): | ||
| gap = len(myList) // 2 |
There was a problem hiding this comment.
JAS - Just a suggestion
Sub-optimal Gap Sequence for Shell Sort
While the current implementation using len(myList) // 2 is functionally correct and follows Shell's original suggestion, it is known to have a worst-case time complexity of O(n²). For better performance on larger datasets, we could consider using more efficient gap sequences like Knuth's or Sedgewick's, though for this illustrative script, the current logic is acceptable for basic use.
| for row in reader: | ||
| print(row[0] + ' ' + row[1]) |
There was a problem hiding this comment.
Potential IndexError in CSV Reading
I noticed that csvRead assumes every row in the CSV has at least two columns (row[0] and row[1]). If a row is empty or has only one column, this will cause an IndexError and crash the script. We should check the row length before accessing specific indices.
| for row in reader: | |
| print(row[0] + ' ' + row[1]) | |
| if len(row) >= 2: | |
| print(row[0] + ' ' + row[1]) |
| print(row[0] + ' ' + row[1]) | ||
|
|
||
| def csvWrite(filePath, data): | ||
| with open(filePath, 'a') as fd: |
There was a problem hiding this comment.
Missing Newline Parameter in File Opening
When working with the csv module, it's recommended to open files with newline='' to prevent platform-specific line ending issues (like double newlines on Windows). This ensures the CSV is written and read consistently across different operating systems.
| with open(filePath, 'a') as fd: | |
| with open(filePath, 'a', newline='') as fd: |
| # data = ['Firstname', 'Lastname'] | ||
| # csvWrite('example.csv', data) | ||
| userInput = input('What is your Fullname? ') | ||
| userInput = userInput.split(' ') |
There was a problem hiding this comment.
JAS - Just a suggestion
Input Splitting Logic Issue
We're splitting the full name by a single space, but if a user enters multiple spaces or just one name, the csvRead function (which expects two columns) might fail or produce unexpected output. Using split() without arguments handles multiple whitespaces better.
| userInput = userInput.split(' ') | |
| userInput = userInput.strip().split() |
Reasons & Gaps
Reasons
- split(' ') fails to handle multiple spaces, creating empty strings in the list
- Leading or trailing spaces will result in empty fields being written to the CSV
- strip() and default split() provide more robust parsing of user-entered names
Gaps
- The exact expected format of the CSV (e.g., strictly two columns) is not defined
- User input variability makes simple splitting prone to logic errors
| # Convert the word or sentence in lower case letters. | ||
| clean_word = word.lower() | ||
| # Make ann empty list to append unique letters | ||
| letter_list = [] |
There was a problem hiding this comment.
Performance Issue: Inefficient List Lookup
I noticed we're using a list (letter_list) to track unique characters. In Python, checking if letter in letter_list takes O(n) time. As the word grows, this becomes increasingly slow. We can optimize this to O(1) by using a set, which is much faster for membership tests.
| letter_list = [] | |
| letter_list = set() |
| if Counter(check) == Counter(alphabets): | ||
| return True |
There was a problem hiding this comment.
JAS - Just a suggestion
Inefficient Pangram Logic
I notice we're using Counter to compare the alphabet against the unique letters found in the sentence. While this works, Counter tracks frequencies which aren't necessary for a pangram check (we only care if the letter exists at least once). A more efficient and standard approach would be to use sets or simply check the length of the unique alphabets found.
| if Counter(check) == Counter(alphabets): | |
| return True | |
| if len(alphabets) == 26: | |
| return True |
| for words in myList: | ||
| if word != words.lower(): | ||
| if Counter(word) == Counter(words.lower()): |
There was a problem hiding this comment.
Performance Issue: Redundant Counter Object Creation
I noticed we're creating a new Counter object for the target word inside every iteration of the loop. Since the word doesn't change, we can create its Counter once before the loop starts. This will make the function much more efficient, especially when checking against a large list of words.
| for words in myList: | |
| if word != words.lower(): | |
| if Counter(word) == Counter(words.lower()): | |
| word_counter = Counter(word) | |
| for words in myList: | |
| if word != words.lower(): | |
| if word_counter == Counter(words.lower()): |
| for x in range(1, number): | ||
| if number % x == 0: | ||
| sum += x |
There was a problem hiding this comment.
Performance Optimization: O(sqrt(n)) Divisor Calculation
The current implementation checks every number up to number, which is inefficient for large inputs. We can optimize this to O(sqrt(n)) by only iterating up to the square root of the number.
| for x in range(1, number): | |
| if number % x == 0: | |
| sum += x | |
| for x in range(1, int(number**0.5) + 1): | |
| if number % x == 0: | |
| sum += x | |
| if x > 1 and x != number // x: | |
| sum += number // x |
Reasons & Gaps
Reasons
- Iterating up to N takes linear time which scales poorly with large numbers
- Divisors always come in pairs (x, N/x), allowing iteration up to sqrt(N)
- Reducing iterations from N to sqrt(N) significantly improves execution speed
Gaps
- The current O(n) approach is functionally correct for small numbers
- Optimization is primarily relevant if the function is intended for large integer inputs
| def pascalTriangle(n): | ||
| triangle = [] | ||
| for i in range(n): | ||
| triangle.append(pascalRow(i)) | ||
| return triangle |
There was a problem hiding this comment.
Performance Issue: Redundant Recursive Calculations
I notice we're using a recursive approach to generate each row. While this works for small values of n, it recalculates the previous rows multiple times (exponential complexity). For a triangle of size 7, it's fine, but as n grows, this will significantly slow down. We could optimize this by using the previous row already stored in our triangle list to build the next one iteratively.
| def pascalTriangle(n): | |
| triangle = [] | |
| for i in range(n): | |
| triangle.append(pascalRow(i)) | |
| return triangle | |
| def pascalTriangle(n): | |
| if n <= 0: | |
| return [] | |
| triangle = [[1]] | |
| for i in range(1, n): | |
| prev_row = triangle[-1] | |
| new_row = [1] + [prev_row[j] + prev_row[j+1] for j in range(len(prev_row) - 1)] + [1] | |
| triangle.append(new_row) | |
| return triangle |
| dbfile = open('examplePickle', 'ab') # Its important to use binary mode | ||
| pickle.dump(db, dbfile) # source, destination | ||
| dbfile.close() |
There was a problem hiding this comment.
Logic Error: Append Mode with Single Load
I notice we're using 'ab' (append binary) mode to save the database. Since pickle.load() only retrieves the first object it finds in the file, any data appended in subsequent runs will be ignored. This means we'll only ever see the data from the first time the script was run. We should use 'wb' (write binary) to ensure the file always reflects the most recent state of our data.
| dbfile = open('examplePickle', 'ab') # Its important to use binary mode | |
| pickle.dump(db, dbfile) # source, destination | |
| dbfile.close() | |
| with open('examplePickle', 'wb') as dbfile: | |
| pickle.dump(db, dbfile) |
| dbfile = open('examplePickle', 'rb') # for reading also binary mode is important | ||
| db = pickle.load(dbfile) | ||
| for keys in db: | ||
| print(keys,'=>',db[keys]) | ||
| dbfile.close() |
There was a problem hiding this comment.
Runtime Risk: Resource Management
We're opening and closing the file handles manually. If an error occurs during the pickle.load operation (like a corrupted file), the close() call might never be reached, leading to a resource leak. It's much safer to use a with statement, which guarantees the file is closed properly even if an exception is raised.
| dbfile = open('examplePickle', 'rb') # for reading also binary mode is important | |
| db = pickle.load(dbfile) | |
| for keys in db: | |
| print(keys,'=>',db[keys]) | |
| dbfile.close() | |
| with open('examplePickle', 'rb') as dbfile: | |
| db = pickle.load(dbfile) | |
| for keys in db: | |
| print(keys,'=>',db[keys]) |
| finally: | ||
| myAddressBook.close() |
There was a problem hiding this comment.
Potential Resource Leak and Crash in addContacts
In the addContacts method, if an exception occurs before myAddressBook is initialized in the try block, the finally block will attempt to call .close() on an undefined variable, causing a crash.
| finally: | |
| myAddressBook.close() | |
| finally: | |
| if 'myAddressBook' in locals() and not myAddressBook.closed: | |
| myAddressBook.close() |
| myAddressBook = open(self.filename, 'rb') | ||
| data = pickle.load(myAddressBook) |
There was a problem hiding this comment.
Security Risk: Arbitrary Code Execution via Pickle
Using pickle.load() on a file that can be modified by users or external processes is insecure. A maliciously crafted 'addressbook' file could execute arbitrary code when loaded by this application.
| myAddressBook = open(self.filename, 'rb') | |
| data = pickle.load(myAddressBook) | |
| import json | |
| with open(self.filename, 'r') as f: | |
| data = json.load(f) |
Reasons & Gaps
Reasons
- Pickle module is inherently insecure against erroneous or maliciously constructed data
- Loading untrusted pickle data can lead to arbitrary code execution on the host
- JSON is a safer, human-readable alternative for simple data persistence
Gaps
- The application is currently a local CLI tool which limits the attack vector
- Switching to JSON requires changing all read/write operations in the class
| def getDetailsFromUser(self): | ||
| try: | ||
| self.contacts['Name'] = str(input('Enter Contact\'s Full Name: ')) | ||
| self.contacts['Address'] = str(input('Enter Contact\'s Address: ')) | ||
| self.contacts['Email'] = str(input('Enter Contact\'s Email Address: ')) | ||
| self.contacts['Phone'] = int(input('Enter Contact\'s Phone Number: ')) | ||
| return self.contacts |
There was a problem hiding this comment.
Logic Error: Shared State in Instance Variable
We're using self.contacts to store temporary user input, but this dictionary isn't cleared between calls. This can lead to data from a previous 'add' operation leaking into a subsequent one if fields are skipped.
| def getDetailsFromUser(self): | |
| try: | |
| self.contacts['Name'] = str(input('Enter Contact\'s Full Name: ')) | |
| self.contacts['Address'] = str(input('Enter Contact\'s Address: ')) | |
| self.contacts['Email'] = str(input('Enter Contact\'s Email Address: ')) | |
| self.contacts['Phone'] = int(input('Enter Contact\'s Phone Number: ')) | |
| return self.contacts | |
| def getDetailsFromUser(self): | |
| try: | |
| contact = {} | |
| contact['Name'] = str(input('Enter Contact\'s Full Name: ')) | |
| contact['Address'] = str(input('Enter Contact\'s Address: ')) | |
| contact['Email'] = str(input('Enter Contact\'s Email Address: ')) | |
| contact['Phone'] = int(input('Enter Contact\'s Phone Number: ')) | |
| return contact |
| self.left = tree | ||
| tree.left = self.left |
There was a problem hiding this comment.
Logical Error in Left Insertion
I noticed a logic issue in the insertLeft method. When inserting into an existing left child, the code sets self.left to the new tree but then immediately overwrites the new tree's left child with itself (tree.left = self.left). This creates a circular reference and loses the original subtree. We should assign the old self.left to tree.left before updating self.left.
| self.left = tree | |
| tree.left = self.left | |
| tree.left = self.left | |
| self.left = tree |
|
|
||
|
|
||
| def pprint(head_node, _pre="", _last=True, term=False): | ||
| data = "*" if head_node is None else head_node.nodeData |
There was a problem hiding this comment.
Potential Attribute Error in pprint
In the pprint function, we check if head_node is None to set the data variable, but we don't stop execution. Later, we call head_node.getLeftChild(). If head_node is indeed None, this will cause an AttributeError at runtime. We should return early or handle the null case before accessing methods.
| data = "*" if head_node is None else head_node.nodeData | |
| if head_node is None: | |
| print(_pre, "`- " if _last else "|- ", "*", sep="") | |
| return |
Reasons & Gaps
Reasons
- Accesses methods on head_node after a check that allows it to be None
- Will trigger an AttributeError when the recursion reaches a null leaf node
- Prevents the pretty printer from completing the full tree visualization
Gaps
- The term parameter logic might be intended to handle leaf nodes specifically
- Recursive calls in line 62 pass child nodes which can be None
| except: | ||
| return None |
There was a problem hiding this comment.
Bare Except Clause
Using a bare except: block is risky because it catches all exceptions, including SystemExit and KeyboardInterrupt. This can make it very difficult to debug unexpected issues. Since we're looking for a missing key in a dictionary, catching KeyError specifically is much safer and clearer.
| except: | |
| return None | |
| except KeyError: | |
| return None |
| if fromEdge not in self.vertexList: | ||
| newVertex = self.addVertex(fromEdge) | ||
| if toEdge not in self.vertexList: | ||
| newVertex = self.addVertex(toEdge) |
There was a problem hiding this comment.
JAS - Just a suggestion
Redundant Variable Assignment
In the addEdge method, the variable newVertex is assigned the result of addVertex, but it's never used. While not a breaking bug, it suggests the logic might be incomplete or contains dead code. If the intent was just to ensure the vertex exists, the assignment is unnecessary.
| if fromEdge not in self.vertexList: | |
| newVertex = self.addVertex(fromEdge) | |
| if toEdge not in self.vertexList: | |
| newVertex = self.addVertex(toEdge) | |
| if fromEdge not in self.vertexList: | |
| self.addVertex(fromEdge) | |
| if toEdge not in self.vertexList: | |
| self.addVertex(toEdge) |
Reasons & Gaps
Reasons
- Assigning a return value to a local variable that is never read is dead code
- Increases cognitive load for maintainers wondering if the variable was forgotten
- Cleaning up unused assignments improves code clarity and intent
Gaps
- The developer might have intended to use 'newVertex' for further initialization
- In some patterns, returning the object is used for method chaining or logging
| def inorder(Tree): | ||
| if Tree: | ||
| inorder(Tree.getLeft()) | ||
| print(Tree.getData(), end = ' ') | ||
| inorder(Tree.getRight()) | ||
| return |
There was a problem hiding this comment.
Potential Recursion Depth Error
I notice that the traversal functions use recursion without any depth limiting or iterative fallback. While this works for small trees, a very deep or skewed tree could trigger a RecursionError in Python. For production-grade code, we should consider an iterative approach or increasing the recursion limit.
Reasons & Gaps
Reasons
- Recursive calls consume stack space proportional to the depth of the tree
- Skewed trees can easily exceed the default recursion limit in Python
- Iterative implementations using an explicit stack are more memory-efficient
Gaps
- The expected tree depth is not specified in the PR context
- Python's default recursion limit (usually 1000) might be sufficient for most educational use cases
| return 0 | ||
| else: | ||
| h = height(root) | ||
| for i in range(h + 1): |
There was a problem hiding this comment.
Off-by-One Error in Level Iteration
I noticed that the loop for level-order traversal starts from 0 and goes up to h. However, the printBFT function treats level == 1 as the root. This means the first iteration (i=0) does nothing, and the last level might be missed if the height calculation doesn't align with the loop range. We should start the range from 1 to h + 1 to correctly print all levels.
| for i in range(h + 1): | |
| for i in range(1, h + 1): |
| def heapify(alist): | ||
| ''' This function helps to maintain the heap property ''' | ||
| # start = (len(alist) - 2) // 2 (faster execution) | ||
| start = len(alist) // 2 |
There was a problem hiding this comment.
Incorrect Heap Construction Start Index
I noticed a small logic error in how we're starting the heapify process. By using len(alist) // 2, we might start at an index that doesn't have any children, which is slightly inefficient. More importantly, in a 0-indexed array, the last parent node is actually at (len(alist) // 2) - 1. Starting from len(alist) // 2 works because shiftDown handles it, but it's technically one step past the last internal node.
| start = len(alist) // 2 | |
| start = (len(alist) // 2) - 1 |
| p += 1 | ||
|
|
||
| # printing all primes | ||
| for i in range(2, n): |
There was a problem hiding this comment.
Off-by-one Error in Prime Output
I noticed a small logic issue in the final loop. The current range range(2, n) excludes the number n itself. If n is a prime number (like 13 or 997), it won't be printed. We should use range(2, n + 1) to ensure the limit is inclusive, consistent with the rest of your implementation.
| for i in range(2, n): | |
| for i in range(2, n + 1): |
| else: | ||
| # else make a new vertex | ||
| self.vertex[fromVertex] = [toVertex] | ||
| self.vertex[toVertex] = [] |
There was a problem hiding this comment.
Graph Corruption in addEdge
I noticed a critical bug in the addEdge method. When adding a new fromVertex, the code unconditionally resets self.vertex[toVertex] to an empty list. If toVertex already had outgoing edges defined from previous calls, they will be wiped out. We should only initialize the adjacency list for toVertex if it doesn't already exist in the dictionary.
| self.vertex[toVertex] = [] | |
| if toVertex not in self.vertex: | |
| self.vertex[toVertex] = [] |
| try: | ||
| for adjacentNode in self.vertex[vertex]: | ||
| if visited[adjacentNode] == False: | ||
| self.topologicalSortRec(adjacentNode, visited, stack) | ||
| except KeyError: | ||
| return |
There was a problem hiding this comment.
Incomplete Sort Result due to KeyError Handling
In topologicalSortRec, the try...except KeyError block returns immediately if a vertex has no entry in the self.vertex dictionary. This prevents the vertex from being added to the stack, leading to an incomplete topological sort where sink nodes (nodes with no outgoing edges) are missing from the final output. We should ensure the vertex is pushed to the stack even if it has no neighbors.
| try: | |
| for adjacentNode in self.vertex[vertex]: | |
| if visited[adjacentNode] == False: | |
| self.topologicalSortRec(adjacentNode, visited, stack) | |
| except KeyError: | |
| return | |
| adjacent_nodes = self.vertex.get(vertex, []) | |
| for adjacentNode in adjacent_nodes: | |
| if not visited[adjacentNode]: | |
| self.topologicalSortRec(adjacentNode, visited, stack) |
| userInput = input() | ||
| userInput = userInput.split() | ||
|
|
||
| print(' '.join(userInput[::-1])) |
There was a problem hiding this comment.
JAS - Just a suggestion
Potential Runtime Error: Empty Input Handling
I noticed that if the user provides an empty input or just whitespace, userInput.split() will return an empty list. While the join operation won't crash, it might be better to handle empty inputs gracefully to avoid processing unnecessary data.
| userInput = input() | |
| userInput = userInput.split() | |
| print(' '.join(userInput[::-1])) | |
| userInput = input().strip() | |
| if not userInput: | |
| print("") | |
| else: | |
| words = userInput.split() | |
| print(' '.join(words[::-1])) |
Reasons & Gaps
Reasons
- split() on an empty string returns an empty list which results in an empty join
- Adding a check improves robustness against unexpected user behavior or whitespace
- Explicit handling makes the code's intent clearer for edge cases like empty strings
Gaps
- The script is a simple utility where empty output for empty input might be the intended behavior
- Standard competitive programming scripts often omit such checks for brevity
🔍 Technical Quality Assessment📋 SummaryThis update adds a collection of helpful tools and examples for data handling, including a new address book, security encryption methods, and faster ways to sort information. While these tools add new capabilities, several critical errors were found that could cause the system to crash or lose customer data if not fixed before use. 💼 Business Impact
🎯 Purpose & Scope
📊 Change AnalysisFiles by Category:
Impact Distribution:
|
| File | Status | Description | Impact | Issues Detected |
|---|---|---|---|---|
Numpy/P40_CipherText.py |
Added ( +59/ -0) | Implementation of a Caesar Cipher for text encryption and decryption using a fixed alphabet. | Medium – Provides basic cryptographic functionality but contains logical errors in index wrapping and character handling. | 3 |
Numpy/P41_PortScanner.py |
Added ( +26/ -0) | Added a simple port scanner script using Python's socket module to scan for open ports on a given host. | Medium – The script provides basic port scanning functionality but has performance limitations due to missing timeouts and potential crash risks from unhandled resolution errors. | 2 |
Numpy/P42_MultiprocessingPipes.py |
Added ( +25/ -0) | Added a script demonstrating multiprocessing synchronization using Pipes. | Medium – The script demonstrates inter-process communication but contains a logical flaw that will cause it to hang indefinitely. | 1 |
Numpy/P43_BinarySearchTree.py |
Added ( +145/ -0) | Implementation of a Binary Search Tree (BST) with basic operations like insert, find, and various traversals. | Medium – Provides a standard BST data structure implementation for use in other modules. | 2 |
Numpy/P44_Closures.py |
Added ( +21/ -0) | Added a Python script demonstrating the concept of closures with an outer and inner function. | Low – This is an educational script and does not affect core application functionality. | 1 |
Numpy/P45_MoreOnClosures.py |
Added ( +27/ -0) | Added a script demonstrating Python closures using a logging wrapper for mathematical functions. | Low – This is an educational or utility script demonstrating closures; it does not impact core application logic. | 1 |
Numpy/P46_Decorators.py |
Added ( +21/ -0) | Added a basic Python decorator example demonstrating higher-order functions and function wrapping. | Low – This is an educational script providing a demonstration of Python decorators; it does not impact core application logic. | 2 |
Numpy/P47_MoreOnDecorators.py |
Added ( +27/ -0) | Added a BankAccount class demonstrating the use of @Property and @fullName.setter decorators for managing full names. | Low – This is an educational script demonstrating Python decorator concepts. It has no impact on production systems. | 1 |
Numpy/P48_CountingSort.py |
Added ( +52/ -0) | Implementation of the Counting Sort algorithm for integer arrays. | Medium – The implementation provides a basic counting sort but lacks support for negative integers and non-integer types, which limits its utility for general-purpose sorting. | 2 |
Numpy/P49_RockPaperScissors.py |
Added ( +51/ -0) | Added a basic Rock Paper Scissors game implementation using random selection and user input. | Low – The changes introduce a standalone game script with basic logic and error handling. | 1 |
Numpy/P50_ListComprehensions.py |
Added ( +52/ -0) | Added a tutorial script demonstrating Python list comprehensions, including basic loops, filtering with conditions, and using functions within comprehensions. | Low – This is an educational script and does not affect core application logic. However, it contains a minor performance inefficiency in a function used within a comprehension. | 1 |
Numpy/P51_PythonJSON.py |
Added ( +25/ -0) | Added a utility script for storing and loading data in JSON format using Python's built-in json module. | Medium – Provides basic JSON persistence logic, but lacks robust error handling and encoding specifications which may lead to runtime failures or data corruption with non-ASCII characters. | 2 |
Numpy/P52_BucketSort.py |
Added ( +55/ -0) | Implementation of the Bucket Sort algorithm using Insertion Sort for individual buckets. | Medium – The code provides a functional sorting algorithm but contains a critical runtime risk for empty input lists. | 1 |
Numpy/P53_ShellSort.py |
Added ( +28/ -0) | Implementation of the Shell Sort algorithm in Python using the standard Shell gap sequence. | Low – The code provides a functional sorting utility, but contains misleading documentation regarding its performance characteristics. | 1 |
Numpy/P54_PythonCSV.py |
Added ( +30/ -0) | Added a script to demonstrate reading from and appending to CSV files using the Python csv module. | Medium – The script provides basic CSV operations but contains potential runtime risks regarding file encoding and data indexing. | 3 |
Numpy/P55_Isogram.py |
Added ( +29/ -0) | Added a utility function to check if a given word or phrase is an isogram, along with several test cases. | Low – The change adds a standalone utility function for logological analysis. It does not affect existing system functionality. | 1 |
Numpy/P56_Pangram.py |
Added ( +36/ -0) | Added two implementations of a pangram checker to determine if a sentence contains every letter of the alphabet. | Medium – Provides utility functions for string validation and alphabet coverage checks. | 1 |
Numpy/P57_Anagram.py |
Added ( +27/ -0) | Implementation of an anagram detection function that filters a list of words based on character frequency comparison. | Low – The changes introduce a new utility function for anagram detection with basic test cases. | 1 |
Numpy/P58_PerfectNumber.py |
Added ( +21/ -0) | Added a script to check if a given number is a perfect number by calculating the sum of its proper divisors. | Low – The script provides a functional implementation for identifying perfect numbers, suitable for mathematical computations. | 1 |
Numpy/P59_PascalTriangle.py |
Added ( +32/ -0) | Implementation of Pascal's Triangle generation using recursion. | Low – The code provides a functional implementation for generating Pascal's Triangle rows, though it uses a recursive approach that may be inefficient for very large values of n. | 1 |
Numpy/P60_PickleModule.py |
Added ( +32/ -0) | Added a script demonstrating the use of the pickle module for object serialization and deserialization. | Medium – The script introduces basic data persistence using pickle, but contains resource management issues and security risks associated with untrusted data. | 3 |
Numpy/P61_AddressBook.py |
Added ( +150/ -0) | Implementation of a CLI-based Address Book application using pickle for data persistence. | Medium – The application provides core CRUD functionality for contacts but contains several critical resource management and logic issues that could lead to data corruption or crashes. | 5 |
Numpy/P62_BinaryTree.py |
Added ( +82/ -0) | Implementation of a Binary Tree data structure with insertion and visualization methods. | High – The current implementation contains a critical logic error in the left insertion method that creates infinite recursion and data loss. | 2 |
Numpy/P63_Graph.py |
Added ( +80/ -0) | Implementation of a Graph data structure using adjacency lists with Vertex and Graph classes. | Medium – Provides a foundational graph implementation; however, contains a broad exception handler that could mask unexpected errors. | 1 |
Numpy/P64_DepthFirstTraversal.py |
Added ( +82/ -0) | Implementation of a Binary Tree with Depth First Search traversal methods (Inorder, Preorder, Postorder). | Low – The code provides a standard implementation of tree traversal algorithms suitable for educational purposes or small-scale data structures. | 0 |
Numpy/P65_BreadthFirstTraversal.py |
Added ( +51/ -0) | Implementation of Breadth First Traversal (Level Order Traversal) for a binary tree using a recursive approach. | Medium – Provides a functional but computationally inefficient ($O(N^2)$) implementation of level-order traversal. | 2 |
Numpy/P66_HeapSort.py |
Added ( +50/ -0) | Implementation of the Heap Sort algorithm using a max-heap approach. | Medium – Adds a standard sorting utility to the codebase. The implementation is functional but has a minor inefficiency in the heap construction phase. | 1 |
Numpy/P67_SieveOfEratosthenes.py |
Added ( +34/ -0) | Implementation of the Sieve of Eratosthenes algorithm to find prime numbers up to a given limit n. | Medium – The implementation contains a boundary error that prevents the number 'n' from being evaluated or printed if it is a prime number. | 1 |
Numpy/P68_TopologicalSort.py |
Added ( +66/ -0) | Implementation of Topological Sort algorithm using Depth First Search (DFS) on a directed acyclic graph. | Medium – Provides a standard graph algorithm implementation for topological ordering. | 2 |
Numpy/P69_ReverseWords.py |
Added ( +12/ -0) | Added a Python script to reverse the order of words in a user-provided string. | Low – This is a standalone utility script for string manipulation and does not affect core system functionality. | 1 |
| if num>25: | ||
| num=num%25 | ||
| num=num-1 |
There was a problem hiding this comment.
Logical Error in Index Wrapping (Encryption)
I noticed a small logic error in how we're wrapping the alphabet index. Using num % 25 and then subtracting 1 will result in incorrect character mapping for keys that push the index beyond 'z'. For a 26-letter alphabet, we should use a simple modulo 26 to ensure the index always stays within the valid range of 0-25.
| if num>25: | |
| num=num%25 | |
| num=num-1 | |
| num = num % 26 |
| if num>25: | ||
| num=num%25 | ||
| num=num-1 | ||
| num = num -key |
There was a problem hiding this comment.
Logical Error in Index Wrapping (Decryption)
Similar to the encryption function, the decryption logic for handling indices greater than 25 is problematic and actually redundant if we use Python's modulo operator correctly. Python's % operator handles negative numbers perfectly for circular shifts, so we can simplify this significantly to ensure decryption always maps back to the correct original character.
| if num>25: | |
| num=num%25 | |
| num=num-1 | |
| num = num -key | |
| num = (num - key) % 26 |
| if chars in LETTERS: | ||
| num = LETTERS.find(chars) | ||
| num += key | ||
| if num>25: | ||
| num=num%25 | ||
| num=num-1 | ||
| encrypted =encrypted + LETTERS[num] |
There was a problem hiding this comment.
Functional Limitation: Non-Alphabetic Characters Dropped
Currently, the loop only appends characters if they are found in the LETTERS string. This means spaces, punctuation, and numbers are silently deleted from the message during encryption. Usually, in a Caesar cipher, we want to preserve non-alphabetic characters as-is so the message structure remains readable.
| if chars in LETTERS: | |
| num = LETTERS.find(chars) | |
| num += key | |
| if num>25: | |
| num=num%25 | |
| num=num-1 | |
| encrypted =encrypted + LETTERS[num] | |
| if chars in LETTERS: | |
| num = (LETTERS.find(chars) + key) % 26 | |
| encrypted = encrypted + LETTERS[num] | |
| else: | |
| encrypted = encrypted + chars |
| print('Scanning host:', host) | ||
| try: | ||
| for port in range(1, 1024): | ||
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
There was a problem hiding this comment.
Missing Socket Timeout
The scanner lacks a socket timeout. Without s.settimeout(), it hangs on filtered ports using system defaults, making the scan extremely slow. Adding a 0.5s timeout improves performance.
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| s.settimeout(0.5) |
|
|
||
| if __name__ == '__main__': | ||
| userInput = input('Enter the Server address(URL) to check for open ports: ') | ||
| remoteServerIP = socket.gethostbyname(userInput) |
There was a problem hiding this comment.
Unhandled Hostname Resolution Error
socket.gethostbyname() raises gaierror on invalid hostnames. Without handling this, the script crashes on bad input. We should wrap it in a try-except block for a graceful exit.
| remoteServerIP = socket.gethostbyname(userInput) | |
| try: | |
| remoteServerIP = socket.gethostbyname(userInput) | |
| except socket.gaierror: | |
| print('Hostname could not be resolved. Exiting.') | |
| sys.exit() |
| print(str(self.data), end = ' ') | ||
|
|
||
| class Tree(object): | ||
| def __init__(self, initial_data = []): |
There was a problem hiding this comment.
Mutable Default Argument in Constructor
Using a mutable default argument like initial_data = [] is a common Python pitfall. The list is created once at definition time and shared across all instances of Tree that don't provide their own list, leading to unexpected data persistence between different tree objects.
| def __init__(self, initial_data = []): | |
| def __init__(self, initial_data=None): | |
| self.root = None | |
| if initial_data is None: | |
| initial_data = [] |
| # is invoked outside their scope. | ||
|
|
||
| def outerFunction(text): | ||
| text = text |
There was a problem hiding this comment.
| logging.basicConfig(filename='example.log', level=logging.INFO) | ||
|
|
||
|
|
||
| def logger(func): |
There was a problem hiding this comment.
JAS - Just a suggestion
Potential Namespace Conflict with Built-in/Standard Library
I noticed we're naming our function logger. While this works, logger is a very common term in Python's logging module (which we're already using). To avoid any confusion or potential shadowing issues as the script grows, it's usually safer to use a more specific name like create_logger or log_wrapper.
| def logger(func): | |
| def create_logger(func): |
| # especially after we go over a number of examples. | ||
|
|
||
| def decorator(myFunc): | ||
| def insideDecorator(*args): |
There was a problem hiding this comment.
Decorator Missing Keyword Arguments Support
I see we're setting up a decorator here! One thing to keep in mind is that this implementation only handles positional arguments (*args). If someone tries to call the decorated function using keyword arguments (like display(message='Hello')), the code will crash because insideDecorator doesn't accept **kwargs. Let's add keyword argument support to make the decorator more robust and compatible with any function signature.
| def insideDecorator(*args): | |
| def insideDecorator(*args, **kwargs): |
| def decorator(myFunc): | ||
| def insideDecorator(*args): | ||
| print('insideDecorator Function executed before {}'.format(myFunc.__name__)) | ||
| return myFunc(*args) |
There was a problem hiding this comment.
Original Function Call Missing Keyword Arguments
Following up on the previous point, we also need to make sure those keyword arguments actually reach the original function. Currently, myFunc is only receiving *args. By passing **kwargs in the return statement, we ensure that the original function gets all the data it expects, whether it's passed positionally or by name.
| return myFunc(*args) | |
| return myFunc(*args, **kwargs) |
| firstName, lastName = name.split(' ') | ||
| self.firstName = firstName | ||
| self.lastName = lastName |
There was a problem hiding this comment.
Potential ValueError in fullName Setter
I notice we're using name.split(' ') and expecting exactly two values. If a user provides a name with more than one space (like a middle name) or no spaces at all, this will trigger a ValueError. We should handle these cases to make the setter more robust.
| firstName, lastName = name.split(' ') | |
| self.firstName = firstName | |
| self.lastName = lastName | |
| parts = name.split(' ') | |
| self.firstName = parts[0] | |
| self.lastName = ' '.join(parts[1:]) if len(parts) > 1 else '' |
| maxValue = 0 | ||
| for i in range(len(myList)): | ||
| if myList[i] > maxValue: | ||
| maxValue = myList[i] | ||
|
|
||
| buckets = [0] * (maxValue + 1) | ||
|
|
||
| for i in myList: | ||
| buckets[i] += 1 | ||
|
|
||
| i = 0 | ||
| for j in range(maxValue + 1): | ||
| for a in range(buckets[j]): | ||
| myList[i] = j | ||
| i += 1 |
There was a problem hiding this comment.
Logic Error: Unsupported Negative Integers
The current implementation assumes all input values are non-negative. If the list contains negative integers, buckets[i] will either access the list from the end (due to Python's negative indexing) or raise an IndexError, leading to incorrect sorting or a crash.
| maxValue = 0 | |
| for i in range(len(myList)): | |
| if myList[i] > maxValue: | |
| maxValue = myList[i] | |
| buckets = [0] * (maxValue + 1) | |
| for i in myList: | |
| buckets[i] += 1 | |
| i = 0 | |
| for j in range(maxValue + 1): | |
| for a in range(buckets[j]): | |
| myList[i] = j | |
| i += 1 | |
| minValue = min(myList) if myList else 0 | |
| maxValue = max(myList) if myList else 0 | |
| buckets = [0] * (maxValue - minValue + 1) | |
| for i in myList: | |
| buckets[i - minValue] += 1 | |
| i = 0 | |
| for j in range(len(buckets)): | |
| for a in range(buckets[j]): | |
| myList[i] = j + minValue | |
| i += 1 |
| buckets = [0] * (maxValue + 1) | ||
|
|
||
| for i in myList: | ||
| buckets[i] += 1 |
There was a problem hiding this comment.
JAS - Just a suggestion
Functional Bug: Potential TypeError with Non-Integer Inputs
Counting sort is strictly an integer-based algorithm. If the input list contains floating-point numbers, the line buckets[i] += 1 will raise a TypeError because list indices must be integers or slices, not floats.
Reasons & Gaps
Reasons
- List indexing with floats results in a TypeError in Python
- Counting sort logic relies on discrete integer mapping to array indices
- Missing input validation allows invalid data types to reach the core logic
Gaps
- The implementation notes mention it is integer-based, but no runtime check exists
- Type hinting or explicit validation would prevent unexpected runtime crashes
| except: | ||
| exit() |
There was a problem hiding this comment.
Bare Exception Handler and Silent Exit
Using a bare except: block is generally discouraged as it catches all exceptions, including KeyboardInterrupt or SystemExit. Additionally, calling exit() without context makes debugging difficult. We should catch specific exceptions like ValueError or EOFError and provide feedback to the user.
| except: | |
| exit() | |
| except (EOFError, KeyboardInterrupt): | |
| print('\nGame terminated by user.') | |
| except Exception as e: | |
| print(f'An unexpected error occurred: {e}') |
|
|
||
| # We can also use functions in comprehensions | ||
| def isSqaure(x): | ||
| import math |
There was a problem hiding this comment.
JAS - Just a suggestion
Performance Issue: Local Import in Function
I notice we're importing the math module inside the isSqaure function. Since this function is called 100 times within the list comprehension on line 47, Python has to perform a module lookup every time. While Python caches imports, it's still more efficient to move the import to the top of the file to avoid unnecessary overhead in loops.
| if(len(myList) == 0): | ||
| print('You don\'t have any elements in array!') |
There was a problem hiding this comment.
Potential Runtime Error: Index Out of Range
I notice that if an empty list is passed to bucketSort, the code prints a warning but continues execution. This will cause a crash on the very next line when trying to access myList[0]. We should return the empty list immediately after the check to prevent this runtime failure.
| if(len(myList) == 0): | |
| print('You don\'t have any elements in array!') | |
| if(len(myList) == 0): | |
| return myList |
| # then progressively reducing the gap between elements to be compared. Starting with far apart elements | ||
| # can move some out-of-place elements into position faster than a simple nearest neighbor exchange." | ||
|
|
||
| # Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) |
There was a problem hiding this comment.
JAS - Just a suggestion
Incorrect Complexity Documentation
The comment incorrectly states the worst-case complexity as O(n). For Shell Sort using the Shell gap sequence (n/2), the worst-case time complexity is actually O(n²).
| # Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) | |
| # Best Case O(n log n); Average Case O(n^1.25); Worst Case O(n^2) |
| for row in reader: | ||
| print(row[0] + ' ' + row[1]) |
There was a problem hiding this comment.
Potential Runtime Error: Index Out of Range
I noticed we're accessing row[0] and row[1] directly. If the CSV file has an empty line or a row with fewer than two columns, this will cause the script to crash with an IndexError. It's safer to check the row length before accessing specific indices.
| for row in reader: | |
| print(row[0] + ' ' + row[1]) | |
| for row in reader: | |
| if len(row) >= 2: | |
| print(row[0] + ' ' + row[1]) |
| print(row[0] + ' ' + row[1]) | ||
|
|
||
| def csvWrite(filePath, data): | ||
| with open(filePath, 'a') as fd: |
There was a problem hiding this comment.
CSV Data Corruption Risk: Missing newline parameter
When opening files for the csv module, Python's documentation explicitly recommends using newline='' to prevent issues with line endings across different platforms. Without this, you might see extra blank lines on Windows or incorrect parsing of quoted fields containing newlines.
| with open(filePath, 'a') as fd: | |
| with open(filePath, 'a', newline='') as fd: |
| import csv | ||
|
|
||
| def csvRead(filePath): | ||
| with open(filePath) as fd: |
There was a problem hiding this comment.
CSV Data Corruption Risk: Missing newline parameter in Reader
Similar to the writer, the reader should also be opened with newline='' to ensure that the csv module handles internal line endings correctly, especially if the data contains quoted newlines.
| with open(filePath) as fd: | |
| with open(filePath, newline='') as fd: |
| # Convert the word or sentence in lower case letters. | ||
| clean_word = word.lower() | ||
| # Make ann empty list to append unique letters | ||
| letter_list = [] |
There was a problem hiding this comment.
JAS - Just a suggestion
Performance Optimization: Use a Set for Lookups
I notice we're using a list to track seen letters. While this works for short words, checking if letter in letter_list takes O(n) time. Switching to a set makes this an O(1) operation, which is much more efficient as the input size grows.
| letter_list = [] | |
| letter_list = set() |
| alphabets = [] | ||
| for letter in sentence: | ||
| if letter.isalpha(): | ||
| if letter in alphabets: | ||
| pass | ||
| else: | ||
| alphabets.append(letter) |
There was a problem hiding this comment.
Inefficient List Membership Check
In the pangram function, we're checking if a letter exists in a list (alphabets) inside a loop. Since list lookups are O(n), this makes the overall complexity O(n*m). We can optimize this by using a set for lookups, which provides O(1) average time complexity.
| alphabets = [] | |
| for letter in sentence: | |
| if letter.isalpha(): | |
| if letter in alphabets: | |
| pass | |
| else: | |
| alphabets.append(letter) | |
| alphabets = set() | |
| for letter in sentence: | |
| if letter.isalpha(): | |
| alphabets.add(letter) |
| anagrams = [] | ||
| for words in myList: | ||
| if word != words.lower(): | ||
| if Counter(word) == Counter(words.lower()): |
There was a problem hiding this comment.
Performance Issue: Redundant Counter Calculation
I notice that Counter(word) is being recalculated inside the loop for every item in myList. Since the input word doesn't change during iteration, we can calculate its character counts once outside the loop. This will significantly improve performance, especially when processing large lists of words.
| anagrams = [] | |
| for words in myList: | |
| if word != words.lower(): | |
| if Counter(word) == Counter(words.lower()): | |
| word_counts = Counter(word) | |
| for words in myList: | |
| if word != words.lower(): | |
| if word_counts == Counter(words.lower()): |
| for x in range(1, number): | ||
| if number % x == 0: | ||
| sum += x |
There was a problem hiding this comment.
Performance Inefficiency in Divisor Calculation
I notice we're iterating through every single number up to number to find divisors. For large inputs, this will be quite slow. We can significantly optimize this by only checking up to the square root of the number, as divisors always come in pairs. This would reduce the time complexity from O(n) to O(sqrt(n)).
| for x in range(1, number): | |
| if number % x == 0: | |
| sum += x | |
| for x in range(1, int(number**0.5) + 1): | |
| if number % x == 0: | |
| sum += x | |
| if x * x != number and x != 1: | |
| sum += number // x |
| def pascalTriangle(n): | ||
| triangle = [] | ||
| for i in range(n): | ||
| triangle.append(pascalRow(i)) | ||
| return triangle |
There was a problem hiding this comment.
Performance: Redundant Recursive Calculations
While the current implementation correctly generates Pascal's Triangle, calling pascalRow(i) inside a loop in pascalTriangle leads to redundant recursive calls. For each row i, the function re-calculates all previous rows from scratch. For larger values of n, this results in
| def pascalTriangle(n): | |
| triangle = [] | |
| for i in range(n): | |
| triangle.append(pascalRow(i)) | |
| return triangle | |
| def pascalTriangle(n): | |
| if n <= 0: | |
| return [] | |
| triangle = [[1]] | |
| for i in range(1, n): | |
| prev_row = triangle[-1] | |
| new_row = [1] + [prev_row[j] + prev_row[j+1] for j in range(len(prev_row) - 1)] + [1] | |
| triangle.append(new_row) | |
| return triangle |
|
|
||
| def loadData(): | ||
| dbfile = open('examplePickle', 'rb') # for reading also binary mode is important | ||
| db = pickle.load(dbfile) |
There was a problem hiding this comment.
Security Risk - Untrusted Deserialization
The pickle module is not secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source, as it can lead to arbitrary code execution. For simple data storage, consider using json instead.
| db = pickle.load(dbfile) | |
| import json | |
| def storeData(): | |
| # ... data setup ... | |
| with open('example.json', 'w') as f: | |
| json.dump(db, f) | |
| def loadData(): | |
| with open('example.json', 'r') as f: | |
| db = json.load(f) |
Reasons & Gaps
Reasons
- Pickle can execute arbitrary Python code during the unpickling process
- Maliciously crafted pickle files can compromise the entire host system
- JSON is a safer alternative for serializing basic Python dictionaries and lists
Gaps
- The risk level depends on whether the 'examplePickle' file can be modified by users
- In a local-only script, the impact is lower than in a networked application
| myAddressBook = open(self.filename, 'wb') | ||
| data = {} |
There was a problem hiding this comment.
Resource Leak: Unclosed File Handle
In the addContacts method, a file is opened in 'wb' mode when the address book doesn't exist, but it's never closed before the code proceeds to get user details. This can lead to file locks or resource exhaustion.
| myAddressBook = open(self.filename, 'wb') | |
| data = {} | |
| myAddressBook = open(self.filename, 'wb') | |
| myAddressBook.close() | |
| data = {} |
| finally: | ||
| myAddressBook.close() |
There was a problem hiding this comment.
Potential UnboundLocalError
If an exception occurs before myAddressBook is successfully assigned a file object (e.g., if open() fails), the finally block will attempt to call .close() on an undefined variable, causing a secondary crash.
| finally: | |
| myAddressBook.close() | |
| finally: | |
| if 'myAddressBook' in locals() and not myAddressBook.closed: | |
| myAddressBook.close() |
| if data: | ||
| for records in data.values(): | ||
| print(records) | ||
| myAddressBook.close() |
There was a problem hiding this comment.
Logic Error: Redundant and Incorrect File Closing
In displayContacts, myAddressBook.close() is called twice. More importantly, the second call is outside the scope where the file was opened, which will raise an error if the else branch is taken.
| if data: | |
| for records in data.values(): | |
| print(records) | |
| myAddressBook.close() | |
| if data: | |
| for records in data.values(): | |
| print(records) |
| if option == 1: | ||
| contact['Name'] = input('Enter Name to modify: ') | ||
| del data[contactToModify] | ||
| data[contact['Name']] = contact |
There was a problem hiding this comment.
Data Corruption Risk: Improper Dictionary Update
In modifyContacts, when modifying the name (Option 1), the code deletes the old key but uses a reference to the contact object that might have been modified. If the name is changed, the dictionary key and the 'Name' field inside the object must stay in sync.
| if option == 1: | |
| contact['Name'] = input('Enter Name to modify: ') | |
| del data[contactToModify] | |
| data[contact['Name']] = contact | |
| if option == 1: | |
| newName = input('Enter Name to modify: ') | |
| contact = data.pop(contactToModify) | |
| contact['Name'] = newName | |
| data[newName] = contact |
| if contactToSearch in contact['Name']: | ||
| print(data[contact['Name']]) |
There was a problem hiding this comment.
JAS - Just a suggestion
Logic Error: Incorrect Search Result Display
In searchContacts, the code prints data[contact['Name']]. However, contact is already the value (the dictionary) from data.values(). This results in an unnecessary and potentially failing double lookup.
| if contactToSearch in contact['Name']: | |
| print(data[contact['Name']]) | |
| if contactToSearch in contact['Name']: | |
| print(contact) |
| self.left = tree | ||
| tree.left = self.left |
There was a problem hiding this comment.
Infinite Recursion and Data Loss in insertLeft
I noticed a logic error in the insertLeft method. On line 37, we assign the new node to self.left, but then on line 38, we point the new node's left child back to itself (tree.left = self.left). This creates a circular reference that will cause an infinite loop during tree traversal and also causes the original left subtree to be lost. We should assign the old left child to the new node before updating self.left.
| self.left = tree | |
| tree.left = self.left | |
| tree.left = self.left | |
| self.left = tree |
|
|
||
| def pprint(head_node, _pre="", _last=True, term=False): | ||
| data = "*" if head_node is None else head_node.nodeData | ||
|
|
There was a problem hiding this comment.
Potential AttributeError in pprint
In the pprint function, if head_node is None, we correctly handle the data display on line 51, but the code continues to execute. On line 58, it attempts to call head_node.getLeftChild(), which will raise an AttributeError because head_node is None. We should return early if the node is None to prevent the crash.
| if head_node is None: | |
| return |
| except: | ||
| return None |
There was a problem hiding this comment.
Broad Exception Handler
Using a bare except: block is risky because it catches all exceptions, including SystemExit and KeyboardInterrupt. In this context, we likely only want to handle cases where the neighbor doesn't exist in the edges dictionary. It's safer to catch KeyError specifically.
| except: | |
| return None | |
| except KeyError: | |
| return None |
|
|
||
| def breadthFirstTraversal(root): | ||
| if root == None: | ||
| return 0 |
There was a problem hiding this comment.
| def heapify(alist): | ||
| ''' This function helps to maintain the heap property ''' | ||
| # start = (len(alist) - 2) // 2 (faster execution) | ||
| start = len(alist) // 2 |
There was a problem hiding this comment.
JAS - Just a suggestion
Inefficient Heapify Start Index
I notice we're starting the heapify process from len(alist) // 2. In a zero-indexed binary heap, the last non-leaf node is actually at (len(alist) // 2) - 1. Starting at len(alist) // 2 is slightly inefficient as it calls shiftDown on a leaf node, which will immediately return without doing any work. We can optimize this by starting one index earlier.
| start = len(alist) // 2 | |
| start = (len(alist) // 2) - 1 |
| p += 1 | ||
|
|
||
| # printing all primes | ||
| for i in range(2, n): |
There was a problem hiding this comment.
Off-by-one Error in Prime Printing
I noticed that the loop used to print the prime numbers stops at n-1. If the input n itself is a prime number (e.g., SieveOfEratosthenes(7)), it won't be printed. We should update the range to n + 1 to ensure the limit is inclusive.
| for i in range(2, n): | |
| for i in range(2, n + 1): |
| try: | ||
| for adjacentNode in self.vertex[vertex]: | ||
| if visited[adjacentNode] == False: | ||
| self.topologicalSortRec(adjacentNode, visited, stack) | ||
| except KeyError: | ||
| return |
There was a problem hiding this comment.
Potential KeyError in topologicalSortRec
I noticed that we're using a try-except KeyError block to handle vertices that might not have entries in the self.vertex dictionary. While this prevents a crash, it's generally safer and clearer to use self.vertex.get(vertex, []) to handle missing keys. This ensures the loop simply doesn't execute if the vertex has no outgoing edges, making the code more robust and easier to read.
| try: | |
| for adjacentNode in self.vertex[vertex]: | |
| if visited[adjacentNode] == False: | |
| self.topologicalSortRec(adjacentNode, visited, stack) | |
| except KeyError: | |
| return | |
| for adjacentNode in self.vertex.get(vertex, []): | |
| if visited[adjacentNode] == False: | |
| self.topologicalSortRec(adjacentNode, visited, stack) |
| return | ||
|
|
||
| # Push current vertex to stack which stores the result | ||
| stack.insert(0,vertex) |
There was a problem hiding this comment.
Inefficient Stack Insertion
We're currently using stack.insert(0, vertex) to build our topological sort. Since insert(0, ...) on a Python list is an O(n) operation, the overall complexity of the sort becomes O(V^2 + E) instead of the intended O(V + E). A more efficient approach is to use stack.append(vertex) and then reverse the list at the very end, or use a collections.deque for O(1) left insertions.
| stack.insert(0,vertex) | |
| stack.append(vertex) |
| # Python program to reverse the words | ||
|
|
||
| userInput = input() | ||
| userInput = userInput.split() |
There was a problem hiding this comment.
JAS - Just a suggestion
Potential Logic Issue: Handling Multiple Spaces
I notice we're using split() without arguments, which is great for handling multiple spaces. However, if the input has leading or trailing whitespace, the current implementation might produce unexpected results depending on the desired output format. While split() handles this well, it's worth considering if we want to explicitly strip the input first to ensure a clean string processing flow.
| userInput = userInput.split() | |
| userInput = input().strip().split() |
|
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P41_PortScanner.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P42_MultiprocessingPipes.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
JAS - Just a suggestion
🗂️ Numpy/P43_BinarySearchTree.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Function naming convention |
JAS - Just a suggestion
🗂️ Numpy/P44_Closures.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P45_MoreOnClosures.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Function naming convention |
JAS - Just a suggestion
🗂️ Numpy/P46_Decorators.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P47_MoreOnDecorators.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P48_CountingSort.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P49_RockPaperScissors.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P50_ListComprehensions.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P51_PythonJSON.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P52_BucketSort.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P53_ShellSort.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P54_PythonCSV.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Function naming convention |
JAS - Just a suggestion
🗂️ Numpy/P56_Pangram.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P57_Anagram.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P58_PerfectNumber.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
||
Misleading Function Name |
JAS - Just a suggestion
🗂️ Numpy/P59_PascalTriangle.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P60_PickleModule.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P61_AddressBook.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P62_BinaryTree.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P63_Graph.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P64_DepthFirstTraversal.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P65_BreadthFirstTraversal.py
| Coding Standard | Violations | Citation |
|---|---|---|
Variable naming convention |
||
Function naming convention |
JAS - Just a suggestion
🗂️ Numpy/P66_HeapSort.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
🗂️ Numpy/P67_SieveOfEratosthenes.py
| Coding Standard | Violations | Citation |
|---|---|---|
Function naming convention |
||
Variable naming convention |
JAS - Just a suggestion
| def encrypt(message, key): | ||
| ''' This function lets you to encrypt your message based on a key ''' | ||
| encrypted = '' | ||
| for chars in message: |
There was a problem hiding this comment.
| encrypted = '' | ||
| for chars in message: | ||
| if chars in LETTERS: | ||
| num = LETTERS.find(chars) |
There was a problem hiding this comment.
| def decrypt(message, key): | ||
| ''' This function lets you to decrypt your message based on a key ''' | ||
| decrypted = '' | ||
| for chars in message: |
There was a problem hiding this comment.
| decrypted = '' | ||
| for chars in message: | ||
| if chars in LETTERS: | ||
| num = LETTERS.find(chars) |
There was a problem hiding this comment.
| print('Scanning host:', host) | ||
| try: | ||
| for port in range(1, 1024): | ||
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
The variable name 's' is a single letter and non-descriptive. Renaming it to 'sock' or 'scanner_socket' improves code readability and maintainability.
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| self.root.postorder() | ||
|
|
||
|
|
||
| def pprint(self, head_node=0, _pre="", _last=True, term=False): |
There was a problem hiding this comment.
JAS - Just a suggestion
Cryptic Function Name Abbreviation
The function name 'pprint' is a common abbreviation for 'pretty_print'. Expanding it to the full term improves readability and follows the standard to avoid cryptic names.
| def pprint(self, head_node=0, _pre="", _last=True, term=False): | |
| def pretty_print(self, head_node=0, _pre="", _last=True, term=False): |
| # captured variables through the closure's copies of their values or references, even when the function | ||
| # is invoked outside their scope. | ||
|
|
||
| def outerFunction(text): |
| def outerFunction(text): | ||
| text = text | ||
|
|
||
| def innerFunction(): |
| return innerFunction | ||
|
|
||
| if __name__ == '__main__': | ||
| myFunction = outerFunction('Hey!') |
There was a problem hiding this comment.
| print(func(*args)) | ||
| return log_func | ||
|
|
||
| def add(x, y): |
There was a problem hiding this comment.
| def add(x, y): | ||
| return x+y | ||
|
|
||
| def sub(x, y): |
There was a problem hiding this comment.
| def add(x, y): | ||
| return x+y | ||
|
|
||
| def sub(x, y): |
| # latter function without explicitly modifying it. Sounds confusing – but it's really not, | ||
| # especially after we go over a number of examples. | ||
|
|
||
| def decorator(myFunc): |
There was a problem hiding this comment.
| # especially after we go over a number of examples. | ||
|
|
||
| def decorator(myFunc): | ||
| def insideDecorator(*args): |
| # latter function without explicitly modifying it. Sounds confusing – but it's really not, | ||
| # especially after we go over a number of examples. | ||
|
|
||
| def decorator(myFunc): |
There was a problem hiding this comment.
|
|
||
| i = 0 | ||
| for j in range(maxValue + 1): | ||
| for a in range(buckets[j]): |
There was a problem hiding this comment.
|
|
||
| import random, time | ||
|
|
||
| def rockPaperScissors(): |
There was a problem hiding this comment.
| computerOptions = ['R', 'P', 'S'] | ||
| computer = computerOptions[random.randint(0, 2)] | ||
|
|
||
| forOptions = {'R': 'Rock', 'P': 'Paper', 'S':'Scissors'} |
There was a problem hiding this comment.
JAS - Just a suggestion
Variable Naming Convention
The variable name forOptions uses camelCase and is slightly vague. Renaming it to choice_mapping in snake_case improves readability and follows Python conventions.
| forOptions = {'R': 'Rock', 'P': 'Paper', 'S':'Scissors'} | |
| choice_mapping = {'R': 'Rock', 'P': 'Paper', 'S':'Scissors'} |
| odds.append(i) | ||
|
|
||
| # We can also use functions in comprehensions | ||
| def isSqaure(x): |
There was a problem hiding this comment.
JAS - Just a suggestion
Cryptic Abbreviation and Spelling in Function Name
The function name 'isSqaure' contains a typo ('Sqaure' instead of 'Square') and uses a cryptic abbreviation. Renaming it to 'is_perfect_square' would improve clarity and follow Python's snake_case convention.
| def isSqaure(x): | |
| def is_perfect_square(x): |
| # We can also use functions in comprehensions | ||
| def isSqaure(x): | ||
| import math | ||
| sqrt = int(math.sqrt(x)) |
There was a problem hiding this comment.
JAS - Just a suggestion
Vague Variable Name
The variable name 'sqrt' is a common abbreviation for square root. While understandable, using 'square_root' or 'root_value' provides better semantic clarity within the function logic.
| sqrt = int(math.sqrt(x)) | |
| square_root = int(math.sqrt(x)) |
| import json | ||
|
|
||
| # For storing on json format | ||
| def storeJSON(fileName, data = {}): |
There was a problem hiding this comment.
JAS - Just a suggestion
Function Naming Convention
The function name 'storeJSON' uses camelCase, which violates Python's snake_case convention. It should be renamed to 'store_json' to align with PEP 8 standards.
| def storeJSON(fileName, data = {}): | |
| def store_json(fileName, data = {}): |
| json.dump(data, fd, indent = 4, separators = (',', ': ')) | ||
|
|
||
| # For loading data from a JSON file | ||
| def loadJSON(fileName): |
There was a problem hiding this comment.
| import json | ||
|
|
||
| # For storing on json format | ||
| def storeJSON(fileName, data = {}): |
There was a problem hiding this comment.
| json.dump(data, fd, indent = 4, separators = (',', ': ')) | ||
|
|
||
| # For loading data from a JSON file | ||
| def loadJSON(fileName): |
|
|
||
| # For storing on json format | ||
| def storeJSON(fileName, data = {}): | ||
| with open(fileName, 'w') as fd: |
There was a problem hiding this comment.
| maxValue = myList[i] | ||
|
|
||
| # Initialize buckets | ||
| bucketCount = math.floor((maxValue - minValue) / bucketSize) + 1 |
There was a problem hiding this comment.
JAS - Just a suggestion
Variable Naming Convention
The variable bucketCount should be renamed to bucket_count to adhere to Python's snake_case naming convention for variables.
| bucketCount = math.floor((maxValue - minValue) / bucketSize) + 1 | |
| bucket_count = math.floor((max_value - min_value) / bucket_size) + 1 |
| buckets[math.floor((myList[i] - minValue) / bucketSize)].append(myList[i]) | ||
|
|
||
| # Sort buckets and place back into input array | ||
| sortedArray = [] |
|
|
||
| # Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) | ||
|
|
||
| def shellSort(myList): |
|
|
||
| # Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) | ||
|
|
||
| def shellSort(myList): |
| # Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) | ||
|
|
||
| def shellSort(myList): | ||
| gap = len(myList) // 2 |
There was a problem hiding this comment.
JAS - Just a suggestion
Variable Naming Refinement
While 'gap' is functional, using a more explicit name like interval or stride can improve clarity in the context of the Shell Sort algorithm.
| gap = len(myList) // 2 | |
| interval = len(myList) // 2 |
Reasons & Gaps
Reasons
- 'interval' more accurately describes the distance between compared elements
- Enhancing semantic clarity reduces cognitive load for new developers
- Standardizing terminology across sorting implementations aids readability
Gaps
- 'gap' is a standard term in Shell Sort literature, making it contextually acceptable
- Refinement is suggested for semantic richness rather than fixing a strict error
| return myList | ||
|
|
||
| if __name__ == '__main__': | ||
| myList = [12, 23, 4, 5, 3, 2, 12, 81, 56, 95] |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
The variable myList is generic. Consider using a name that reflects the content or purpose, such as numbers_to_sort or unsorted_list.
| myList = [12, 23, 4, 5, 3, 2, 12, 81, 56, 95] | |
| numbers_to_sort = [12, 23, 4, 5, 3, 2, 12, 81, 56, 95] |
| import csv | ||
|
|
||
| def csvRead(filePath): | ||
| with open(filePath) as fd: |
There was a problem hiding this comment.
| print(row[0] + ' ' + row[1]) | ||
|
|
||
| def csvWrite(filePath, data): | ||
| with open(filePath, 'a') as fd: |
There was a problem hiding this comment.
|
|
||
| import csv | ||
|
|
||
| def csvRead(filePath): |
There was a problem hiding this comment.
| for row in reader: | ||
| print(row[0] + ' ' + row[1]) | ||
|
|
||
| def csvWrite(filePath, data): |
There was a problem hiding this comment.
| # ( 1 + 2 + 3 + 6 ) / 2 = 6. The next perfect number is 28 = 1 + 2 + 4 + 7 + 14. This is followed by the | ||
| # perfect numbers 496 and 8128. | ||
|
|
||
| def perfectNumber(number): |
There was a problem hiding this comment.
JAS - Just a suggestion
Function Naming Convention Violation
The function name 'perfectNumber' uses camelCase, which violates the Python snake_case convention. It should be renamed to 'is_perfect_number' to be more idiomatic and descriptive.
| def perfectNumber(number): | |
| def is_perfect_number(number): |
| # perfect numbers 496 and 8128. | ||
|
|
||
| def perfectNumber(number): | ||
| sum = 0 |
| def perfectNumber(number): | ||
| sum = 0 | ||
| for x in range(1, number): | ||
| if number % x == 0: | ||
| sum += x | ||
| return sum == number |
There was a problem hiding this comment.
JAS - Just a suggestion
Boolean Function Prefix Missing
Functions that return a boolean value should ideally start with a prefix like 'is_', 'has_', or 'can_'. Renaming to 'is_perfect_number' follows this convention.
| def perfectNumber(number): | |
| sum = 0 | |
| for x in range(1, number): | |
| if number % x == 0: | |
| sum += x | |
| return sum == number | |
| def is_perfect_number(number): |
| if n == 0: | ||
| return [1] | ||
| else: | ||
| N = pascalRow(n-1) |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
The variable name 'N' is a single uppercase letter, which is non-descriptive and violates naming conventions. It should be renamed to something like 'previous_row' to reflect its purpose.
| N = pascalRow(n-1) | |
| previous_row = pascalRow(n-1) |
| return [1] | ||
| else: | ||
| N = pascalRow(n-1) | ||
| return [1] + [N[i] + N[i+1] for i in range(n-1)] + [1] |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Reference
The variable 'N' used in this list comprehension should be updated to match the more descriptive name 'previous_row'.
| return [1] + [N[i] + N[i+1] for i in range(n-1)] + [1] | |
| return [1] + [previous_row[i] + previous_row[i+1] for i in range(n-1)] + [1] |
|
|
||
| import pickle | ||
|
|
||
| def storeData(): |
|
|
||
| def storeData(): | ||
| # initializing data to be stored in db | ||
| Omkar = {'key' : 'Omkar', 'name' : 'Omkar Pathak', 'age' : 21, 'pay' : 40000} |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Standard Variable Naming
Variable names in Python should use snake_case. Additionally, using a specific person's name as a variable for a generic data record is less descriptive than a role-based name.
| Omkar = {'key' : 'Omkar', 'name' : 'Omkar Pathak', 'age' : 21, 'pay' : 40000} | |
| employee_omkar = {'key' : 'Omkar', 'name' : 'Omkar Pathak', 'age' : 21, 'pay' : 40000} |
| def storeData(): | ||
| # initializing data to be stored in db | ||
| Omkar = {'key' : 'Omkar', 'name' : 'Omkar Pathak', 'age' : 21, 'pay' : 40000} | ||
| Jagdish = {'key' : 'Jagdish', 'name' : 'Jagdish Pathak', 'age' : 50, 'pay' : 50000} |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Standard Variable Naming
Variable names in Python should use snake_case. Rename Jagdish to employee_jagdish or similar to follow PEP 8 and improve semantic clarity.
| Jagdish = {'key' : 'Jagdish', 'name' : 'Jagdish Pathak', 'age' : 50, 'pay' : 50000} | |
| employee_jagdish = {'key' : 'Jagdish', 'name' : 'Jagdish Pathak', 'age' : 50, 'pay' : 50000} |
| pickle.dump(db, dbfile) # source, destination | ||
| dbfile.close() | ||
|
|
||
| def loadData(): |
| def loadData(): | ||
| dbfile = open('examplePickle', 'rb') # for reading also binary mode is important | ||
| db = pickle.load(dbfile) | ||
| for keys in db: |
| if self.right == None: | ||
| self.right = BinaryTree(newnodeData) | ||
| else: | ||
| tree = BinaryTree(newnodeData) |
There was a problem hiding this comment.
JAS - Just a suggestion
Generic Variable Name
The variable name 'tree' is generic within a class that already represents a tree. Using a more specific name like 'new_node' or 'subtree' clarifies its role as a child node.
| tree = BinaryTree(newnodeData) | |
| new_node = BinaryTree(newnodeData) |
| if self.left == None: | ||
| self.left = BinaryTree(newnodeData) | ||
| else: | ||
| tree = BinaryTree(newnodeData) |
There was a problem hiding this comment.
|
|
||
|
|
||
| def pprint(head_node, _pre="", _last=True, term=False): | ||
| data = "*" if head_node is None else head_node.nodeData |
There was a problem hiding this comment.
JAS - Just a suggestion
Vague Generic Name
The variable 'data' is functional but generic. Renaming it to 'node_display_value' or 'label' would be more expressive for a printing utility.
| data = "*" if head_node is None else head_node.nodeData | |
| display_value = "*" if head_node is None else head_node.nodeData |
| left = head_node.getLeftChild() | ||
| right = head_node.getRightChild() | ||
|
|
||
| for i, child in enumerate([left, right]): |
There was a problem hiding this comment.
| self.key = key | ||
| self.edges = {} | ||
|
|
||
| def addNeighbour(self, neighbour, weight = 0): |
There was a problem hiding this comment.
JAS - Just a suggestion
Function Naming Convention (Case Style)
In Python, function and method names should follow the snake_case convention. Rename addNeighbour to add_neighbour to align with PEP 8 standards.
| def addNeighbour(self, neighbour, weight = 0): | |
| def add_neighbour(self, neighbour, weight = 0): |
| def __str__(self): | ||
| return str(self.key) + 'connected to: ' + str([x.key for x in self.edges]) | ||
|
|
||
| def getEdges(self): |
There was a problem hiding this comment.
| def getEdges(self): | ||
| return self.edges.keys() | ||
|
|
||
| def getKey(self): |
| def getKey(self): | ||
| return self.key | ||
|
|
||
| def getWeight(self, neighbour): |
There was a problem hiding this comment.
| class Graph(object): | ||
| ''' This class helps to create Graph with the help of created vertexes ''' | ||
| def __init__(self): | ||
| self.vertexList = {} |
| self.vertexList = {} | ||
| self.count = 0 | ||
|
|
||
| def addVertex(self, key): |
There was a problem hiding this comment.
| return | ||
|
|
||
| # in this we first print the root node and then traverse towards leftmost node and then to the rightmost node | ||
| def preorder(Tree): |
| return | ||
|
|
||
| # in this we first traverse to the leftmost node and then to the rightmost node and then print the data | ||
| def postorder(Tree): |
There was a problem hiding this comment.
| if root == None: | ||
| return 0 | ||
| else: | ||
| h = height(root) |
There was a problem hiding this comment.
| for i in range(h + 1): | ||
| printBFT(root, i) | ||
|
|
||
| def printBFT(root, level): |
There was a problem hiding this comment.
JAS - Just a suggestion
Cryptic Abbreviation in Function Name
The function name 'printBFT' uses the abbreviation 'BFT'. While common in algorithms, using the full term 'print_breadth_first_traversal' or 'print_level' enhances clarity for all developers.
| def printBFT(root, level): | |
| def print_level(root, level): |
Reasons & Gaps
Reasons
- Standardized naming avoids cognitive load associated with deciphering abbreviations
- Function names should explicitly state the action and target without cryptic shorthand
- 'print_level' more accurately describes the specific recursive action of this helper
Gaps
- BFT is a widely recognized acronym in computer science for Breadth-First Traversal
- The file header explicitly defines BFT, reducing the risk of ambiguity for readers
| # Time Complexity of Solution: | ||
| # Best O(nlog(n)); Average O(nlog(n)); Worst O(nlog(n)). | ||
|
|
||
| def HeapSort(alist): |
There was a problem hiding this comment.
| # Time Complexity of Solution: | ||
| # Best O(nlog(n)); Average O(nlog(n)); Worst O(nlog(n)). | ||
|
|
||
| def HeapSort(alist): |
| shiftDown(alist, start, len(alist) - 1) | ||
| start -= 1 | ||
|
|
||
| def shiftDown(alist, start, end): |
There was a problem hiding this comment.
| # let p now equal this number (which is the next prime), and repeat from step 3. | ||
| # When the algorithm terminates, all the numbers in the list that are not marked are prime. | ||
|
|
||
| def SieveOfEratosthenes(n): |
There was a problem hiding this comment.
|
|
||
| def SieveOfEratosthenes(n): | ||
| primes = [True] * (n + 1) | ||
| p = 2 # because p is the smallest prime |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
While 'p' is used in the algorithm description, using a more descriptive name like 'current_prime' or 'prime_candidate' enhances readability for those unfamiliar with the specific math notation.
| p = 2 # because p is the smallest prime | |
| current_prime = 2 # because current_prime is the smallest prime |
Reasons & Gaps
Reasons
- Single-letter variables (except loop indices) increase cognitive load
- Descriptive names make the code self-documenting without needing external context
- 'current_prime' clearly communicates the variable's role in the sieve logic
Gaps
- Single-letter variables are common in mathematical algorithm implementations
- The comment explicitly links the variable to the algorithm's 'p' notation
| self.count = count # vertex count | ||
|
|
||
| # for printing the Graph vertexes | ||
| def printGraph(self): |
|
|
||
| # mark all adjacent nodes of the current node | ||
| try: | ||
| for adjacentNode in self.vertex[vertex]: |
| stack.insert(0,vertex) | ||
|
|
||
| if __name__ == '__main__': | ||
| g= Graph(6) |
|
|
||
| # Python program to reverse the words | ||
|
|
||
| userInput = input() |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
The variable name 'userInput' uses camelCase, which is not the standard naming convention for Python. Following PEP 8, variables should use snake_case to improve consistency and readability.
| userInput = input() | |
| user_input = input() |
| # Python program to reverse the words | ||
|
|
||
| userInput = input() | ||
| userInput = userInput.split() |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
The variable name 'userInput' uses camelCase, which is not the standard naming convention for Python. Following PEP 8, variables should use snake_case to improve consistency and readability.
| userInput = userInput.split() | |
| user_input = user_input.split() |
| userInput = input() | ||
| userInput = userInput.split() | ||
|
|
||
| print(' '.join(userInput[::-1])) |
There was a problem hiding this comment.
JAS - Just a suggestion
Non-Descriptive Variable Name
The variable name 'userInput' uses camelCase, which is not the standard naming convention for Python. Following PEP 8, variables should use snake_case to improve consistency and readability.
| print(' '.join(userInput[::-1])) | |
| print(' '.join(user_input[::-1])) |
Appmod Quality Check: FAILED❌❌ Quality gate failed - This pull request requires attention before merging. 📊 Quality Metrics
🎯 AssessmentAction required - Please address the identified issues before proceeding. 📋 View Detailed Report for comprehensive analysis and recommendations. Automated by Appmod Quality Assurance System |
Added comments explaining variable scope rules and author.
Added code changes