[+] A1 Starter Files
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
\documentclass[fontsize=11pt]{article}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[margin=0.75in]{geometry}
|
||||
|
||||
\title{CSC111 Assignment 1: Linked Lists}
|
||||
\author{TODO: FILL IN THE NAME OF EACH OF YOUR GROUP MEMBERS HERE}
|
||||
\date{\today}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
|
||||
\section*{Part 1: Faster Searching in Linked Lists}
|
||||
|
||||
\begin{enumerate}
|
||||
|
||||
\item[1.]
|
||||
Complete this part in the provided \texttt{a1\_part1.py} starter file.
|
||||
Do \textbf{not} include your solution in this file.
|
||||
|
||||
\item[2.]
|
||||
Complete this part in the provided \texttt{a1\_part1\_test.py} starter file.
|
||||
Do \textbf{not} include your solution in this file.
|
||||
|
||||
\item[3.]
|
||||
\begin{enumerate}
|
||||
|
||||
\item[(a)]
|
||||
TODO: Write your answer here.
|
||||
|
||||
\item[(b)]
|
||||
TODO: Write your answer here.
|
||||
\end{enumerate}
|
||||
|
||||
\item[4.]
|
||||
TODO: Write your answer here.
|
||||
|
||||
\end{enumerate}
|
||||
|
||||
\section*{Part 2: Linked List Visualization}
|
||||
Complete this part in the provided \texttt{a1\_part2.py} starter file.
|
||||
Do \textbf{not} include your solution in this file.
|
||||
|
||||
\end{document}
|
||||
@@ -0,0 +1,169 @@
|
||||
"""CSC111 Winter 2021 Assignment 1: Linked Lists
|
||||
|
||||
Do not change this file -- it will not be submitted, and we will supply our own copy
|
||||
for grading purposes.
|
||||
|
||||
Copyright and Usage Information
|
||||
===============================
|
||||
|
||||
This file is provided solely for the personal and private use of students
|
||||
taking CSC111 at the University of Toronto St. George campus. All forms of
|
||||
distribution of this code, whether as given or with any changes, are
|
||||
expressly prohibited. For more information on copyright for CSC111 materials,
|
||||
please consult our Course Syllabus.
|
||||
|
||||
This file is Copyright (c) 2021 David Liu and Isaac Waller.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Iterable, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class _Node:
|
||||
"""A node in a linked list.
|
||||
|
||||
Note that this is considered a "private class", one which is only meant
|
||||
to be used in this module by the LinkedList class, but not by client code.
|
||||
|
||||
Instance Attributes:
|
||||
- item: The data stored in this node.
|
||||
- next: The next node in the list, if any.
|
||||
"""
|
||||
item: Any
|
||||
next: Optional[_Node] = None # By default, this node does not link to any other node
|
||||
|
||||
|
||||
class LinkedList:
|
||||
"""A linked list implementation of the List ADT.
|
||||
"""
|
||||
# Private Instance Attributes:
|
||||
# - _first: The first node in the linked list, or None if the list is empty.
|
||||
_first: Optional[_Node]
|
||||
|
||||
def __init__(self, items: Iterable) -> None:
|
||||
"""Initialize a new linked list containing the given items.
|
||||
"""
|
||||
self._first = None
|
||||
for item in items:
|
||||
self.append(item)
|
||||
|
||||
def to_list(self) -> list:
|
||||
"""Return a built-in Python list containing the items of this linked list.
|
||||
|
||||
The items in this linked list appear in the same order in the returned list.
|
||||
"""
|
||||
items_so_far = []
|
||||
|
||||
curr = self._first
|
||||
while curr is not None:
|
||||
items_so_far.append(curr.item)
|
||||
curr = curr.next
|
||||
|
||||
return items_so_far
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Return the number of elements in this list.
|
||||
|
||||
>>> lst = LinkedList([])
|
||||
>>> len(lst) # Equivalent to lst.__len__()
|
||||
0
|
||||
>>> lst = LinkedList([1, 2, 3])
|
||||
>>> len(lst)
|
||||
3
|
||||
"""
|
||||
curr = self._first
|
||||
len_so_far = 0
|
||||
while curr is not None:
|
||||
len_so_far += 1
|
||||
curr = curr.next
|
||||
|
||||
return len_so_far
|
||||
|
||||
def __contains__(self, item: Any) -> bool:
|
||||
"""Return whether item is in this linked list.
|
||||
"""
|
||||
curr = self._first
|
||||
while curr is not None:
|
||||
if curr.item == item:
|
||||
return True
|
||||
|
||||
curr = curr.next
|
||||
return False
|
||||
|
||||
def __getitem__(self, i: int) -> Any:
|
||||
"""Return the item stored at index i in this linked list.
|
||||
|
||||
Raise an IndexError if index i is out of bounds.
|
||||
|
||||
Preconditions:
|
||||
- i >= 0
|
||||
"""
|
||||
curr = self._first
|
||||
curr_index = 0
|
||||
|
||||
while curr is not None:
|
||||
if curr_index == i:
|
||||
return curr.item
|
||||
|
||||
curr = curr.next
|
||||
curr_index = curr_index + 1
|
||||
|
||||
# If we've reached the end of the list and no item has been returned,
|
||||
# the given index is out of bounds.
|
||||
raise IndexError
|
||||
|
||||
def pop(self, i: int) -> Any:
|
||||
"""Remove and return the item at index i.
|
||||
|
||||
Raise IndexError if i >= len(self).
|
||||
|
||||
Preconditions:
|
||||
- i >= 0
|
||||
|
||||
>>> lst = LinkedList([1, 2, 10, 200])
|
||||
>>> lst.pop(1)
|
||||
2
|
||||
>>> lst.to_list()
|
||||
[1, 10, 200]
|
||||
"""
|
||||
if i == 0:
|
||||
if self._first is None:
|
||||
raise IndexError
|
||||
else:
|
||||
item = self._first.item
|
||||
self._first = self._first.next
|
||||
return item
|
||||
else:
|
||||
curr = self._first
|
||||
curr_index = 0
|
||||
|
||||
while not (curr is None or curr_index == i - 1):
|
||||
curr = curr.next
|
||||
curr_index = curr_index + 1
|
||||
|
||||
if curr is None:
|
||||
raise IndexError
|
||||
else:
|
||||
if curr.next is None:
|
||||
raise IndexError
|
||||
else:
|
||||
item = curr.next.item
|
||||
curr.next = curr.next.next
|
||||
return item
|
||||
|
||||
def append(self, item: Any) -> None:
|
||||
"""Add the given item to the end of this linked list.
|
||||
"""
|
||||
new_node = _Node(item)
|
||||
|
||||
if self._first is None:
|
||||
self._first = new_node
|
||||
else:
|
||||
curr = self._first
|
||||
while curr.next is not None:
|
||||
curr = curr.next
|
||||
|
||||
# After the loop, curr is the last node in the LinkedList.
|
||||
assert curr is not None and curr.next is None
|
||||
curr.next = new_node
|
||||
@@ -0,0 +1,132 @@
|
||||
"""CSC111 Winter 2021 Assignment 1: Linked Lists, Part 1
|
||||
|
||||
Instructions (READ THIS FIRST!)
|
||||
===============================
|
||||
|
||||
This Python module contains three linked list subclasses corresponding to the three
|
||||
moving heuristics described on the assignment handout.
|
||||
|
||||
You need to complete their implementations by overriding the __contains__ method
|
||||
(and only for CountLinkedList, additional methods as required).
|
||||
|
||||
Copyright and Usage Information
|
||||
===============================
|
||||
|
||||
This file is provided solely for the personal and private use of students
|
||||
taking CSC111 at the University of Toronto St. George campus. All forms of
|
||||
distribution of this code, whether as given or with any changes, are
|
||||
expressly prohibited. For more information on copyright for CSC111 materials,
|
||||
please consult our Course Syllabus.
|
||||
|
||||
This file is Copyright (c) 2021 David Liu and Isaac Waller.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional
|
||||
|
||||
from a1_linked_list import LinkedList, _Node
|
||||
|
||||
|
||||
################################################################################
|
||||
# Heuristic 1 (move to front)
|
||||
################################################################################
|
||||
class MoveToFrontLinkedList(LinkedList):
|
||||
"""A linked list implementation that uses a "move to front" heuristic for searches.
|
||||
|
||||
Representation Invariants:
|
||||
- all items in this linked list are unique
|
||||
"""
|
||||
def __contains__(self, item: Any) -> bool:
|
||||
"""Return whether item is in this linked list.
|
||||
|
||||
If the item is found, move it to the front of this list.
|
||||
|
||||
>>> linky = MoveToFrontLinkedList([10, 20, 30, 40, 50, 60])
|
||||
>>> linky.__contains__(40)
|
||||
True
|
||||
>>> linky.to_list()
|
||||
[40, 10, 20, 30, 50, 60]
|
||||
"""
|
||||
|
||||
|
||||
################################################################################
|
||||
# Heuristic 2 (swap)
|
||||
################################################################################
|
||||
class SwapLinkedList(LinkedList):
|
||||
"""A linked list implementation that uses a "swap" heuristic for searches.
|
||||
|
||||
Representation Invariants:
|
||||
- all items in this linked list are unique
|
||||
"""
|
||||
def __contains__(self, item: Any) -> bool:
|
||||
"""Return whether item is in this linked list.
|
||||
|
||||
If the item is found, swap it with the item before it, if any.
|
||||
You may do this by reassigning _Node item or next attributes (or both).
|
||||
|
||||
>>> linky = SwapLinkedList([10, 20, 30, 40, 50, 60])
|
||||
>>> linky.__contains__(40)
|
||||
True
|
||||
>>> linky.to_list()
|
||||
[10, 20, 40, 30, 50, 60]
|
||||
"""
|
||||
|
||||
|
||||
################################################################################
|
||||
# Heuristic 3 (count)
|
||||
################################################################################
|
||||
# NOTE: this heuristic requires a new kind of _Node that has an additional "count" attribute.
|
||||
@dataclass
|
||||
class _CountNode(_Node):
|
||||
"""A node in a CountLinkedList.
|
||||
|
||||
Instance Attributes:
|
||||
- item: The data stored in this node.
|
||||
- next: The next node in the list, if any.
|
||||
- access_count: The number of times this node has been accessed (used by the count heuristic)
|
||||
"""
|
||||
next: Optional[_CountNode] = None
|
||||
access_count: int = 0
|
||||
|
||||
|
||||
class CountLinkedList(LinkedList):
|
||||
"""A linked list implementation that uses a "swap" heuristic for searches.
|
||||
|
||||
Representation Invariants:
|
||||
- all items in this linked list are unique
|
||||
- nodes are sorted in non-increasing order by access_count
|
||||
|
||||
NOTE: In order to make use of the _CountNode class above, you'll need to override every
|
||||
LinkedList method in a1_linked_list.py that creates new _Node objects to create _CountNode
|
||||
objects instead. Your code for the overridden methods should be otherwise identical.
|
||||
"""
|
||||
_first: Optional[_CountNode]
|
||||
|
||||
def __contains__(self, item: Any) -> bool:
|
||||
"""Return whether item is in this linked list.
|
||||
|
||||
If the item is found, increase its count and reorder the nodes in
|
||||
non-increasing count order---see assignment handout for details.
|
||||
|
||||
>>> linky = CountLinkedList([10, 20, 30, 40, 50, 60])
|
||||
>>> linky.__contains__(40)
|
||||
True
|
||||
>>> linky.to_list()
|
||||
[40, 10, 20, 30, 50, 60]
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import python_ta
|
||||
python_ta.check_all(config={
|
||||
'max-line-length': 100,
|
||||
'disable': ['E1136'],
|
||||
'extra-imports': ['a1_linked_list'],
|
||||
'max-nested-blocks': 4
|
||||
})
|
||||
|
||||
import python_ta.contracts
|
||||
python_ta.contracts.check_all_contracts()
|
||||
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
@@ -0,0 +1,32 @@
|
||||
"""CSC111 Winter 2021 Assignment 1: Linked Lists, Part 1
|
||||
|
||||
Instructions (READ THIS FIRST!)
|
||||
===============================
|
||||
|
||||
Please write your tests for Part 1 in this module. Make sure to review the assignment
|
||||
instructions carefully for this part! You may find it helpful to consult this
|
||||
section of the Course Notes:
|
||||
https://www.teach.cs.toronto.edu/~csc110y/fall/notes/B-python-libraries/02-pytest.html
|
||||
|
||||
While you must include unit tests, you may also use property-based tests in your test suite.
|
||||
|
||||
We will *not* be running PythonTA on this file; however, you should follow good programming
|
||||
design and style in this file anyway, just like you would for all other work.
|
||||
|
||||
Copyright and Usage Information
|
||||
===============================
|
||||
|
||||
This file is provided solely for the personal and private use of students
|
||||
taking CSC111 at the University of Toronto St. George campus. All forms of
|
||||
distribution of this code, whether as given or with any changes, are
|
||||
expressly prohibited. For more information on copyright for CSC111 materials,
|
||||
please consult our Course Syllabus.
|
||||
|
||||
This file is Copyright (c) 2021 David Liu and Isaac Waller.
|
||||
"""
|
||||
import pytest
|
||||
from a1_part1 import MoveToFrontLinkedList, SwapLinkedList, CountLinkedList
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main(['a1_part1_test.py', '-v'])
|
||||
@@ -0,0 +1,256 @@
|
||||
"""CSC111 Winter 2021 Assignment 1: Linked Lists, Part 2
|
||||
|
||||
Instructions (READ THIS FIRST!)
|
||||
===============================
|
||||
|
||||
This Python module generates a graphical representation of linked lists.
|
||||
You need to implement a few functions that visualize different components of
|
||||
the list as described in the handout, and that make your visualization interactive.
|
||||
|
||||
Copyright and Usage Information
|
||||
===============================
|
||||
|
||||
This file is provided solely for the personal and private use of students
|
||||
taking CSC111 at the University of Toronto St. George campus. All forms of
|
||||
distribution of this code, whether as given or with any changes, are
|
||||
expressly prohibited. For more information on copyright for CSC111 materials,
|
||||
please consult our Course Syllabus.
|
||||
|
||||
This file is Copyright (c) 2021 David Liu and Isaac Waller.
|
||||
"""
|
||||
import random
|
||||
from typing import Tuple
|
||||
|
||||
import pygame
|
||||
from pygame.colordict import THECOLORS
|
||||
|
||||
from a1_linked_list import LinkedList, _Node
|
||||
|
||||
################################################################################
|
||||
# Graphics constants
|
||||
################################################################################
|
||||
# You should not change SCREEN_SIZE or GRID_SIZE, but may add your own constants underneath.
|
||||
SCREEN_SIZE = (800, 800) # (width, height)
|
||||
GRID_SIZE = 8
|
||||
|
||||
|
||||
################################################################################
|
||||
# Pygame helper functions (don't change these!)
|
||||
################################################################################
|
||||
def initialize_screen(screen_size: tuple[int, int], allowed: list) -> pygame.Surface:
|
||||
"""Initialize pygame and the display window.
|
||||
|
||||
allowed is a list of pygame event types that should be listened for while pygame is running.
|
||||
"""
|
||||
pygame.display.init()
|
||||
pygame.font.init()
|
||||
screen = pygame.display.set_mode(screen_size)
|
||||
screen.fill(THECOLORS['white'])
|
||||
pygame.display.flip()
|
||||
|
||||
pygame.event.clear()
|
||||
pygame.event.set_blocked(None)
|
||||
pygame.event.set_allowed([pygame.QUIT] + allowed)
|
||||
|
||||
return screen
|
||||
|
||||
|
||||
def draw_text(screen: pygame.Surface, text: str, pos: tuple[int, int]) -> None:
|
||||
"""Draw the given text to the pygame screen at the given position.
|
||||
|
||||
pos represents the *upper-left corner* of the text.
|
||||
"""
|
||||
font = pygame.font.SysFont('inconsolata', 22)
|
||||
text_surface = font.render(text, True, THECOLORS['black'])
|
||||
width, height = text_surface.get_size()
|
||||
screen.blit(text_surface,
|
||||
pygame.Rect(pos, (pos[0] + width, pos[1] + height)))
|
||||
|
||||
|
||||
def draw_grid(screen: pygame.Surface) -> None:
|
||||
"""Draws a square grid on the given surface.
|
||||
|
||||
The drawn grid has GRID_SIZE columns and rows.
|
||||
You can use this to help you check whether you are drawing nodes and edges in the right spots.
|
||||
"""
|
||||
color = THECOLORS['grey']
|
||||
width, height = screen.get_size()
|
||||
|
||||
for col in range(1, GRID_SIZE):
|
||||
x = col * (width // GRID_SIZE)
|
||||
pygame.draw.line(screen, color, (x, 0), (x, height))
|
||||
|
||||
for row in range(1, GRID_SIZE):
|
||||
y = row * (height // GRID_SIZE)
|
||||
pygame.draw.line(screen, color, (0, y), (width, y))
|
||||
|
||||
|
||||
################################################################################
|
||||
# 1. Drawing nodes and links
|
||||
################################################################################
|
||||
def draw_node(screen: pygame.Surface, node: _Node, pos: Tuple[int, int]) -> None:
|
||||
"""Draw a node on the screen at the given position.
|
||||
|
||||
pos represents the coordinates of the *top-left* corner of the node.
|
||||
|
||||
Your drawing of the the node should include:
|
||||
- A rectangle split vertically into two halves.
|
||||
- The item stored in the node, displayed in the left half.
|
||||
You may assume the string representation of item is at most 3 characters
|
||||
long. You'll need to use the draw_text function we've provided above.
|
||||
|
||||
NOTE: Do not draw the arrow representing the link to the next node in this function.
|
||||
You'll implement that part of the visualization separately in the draw_link function.
|
||||
|
||||
We strongly recommend initializing new constants at the top of this file to represent
|
||||
node width, height, and colour.
|
||||
"""
|
||||
|
||||
|
||||
def draw_link(screen: pygame.Surface, start: Tuple[int, int], end: Tuple[int, int]) -> None:
|
||||
"""Draw a line representing a link from `start` to `end`.
|
||||
|
||||
To indicate which end is the "start" of the line, draw a small circle at the starting point
|
||||
(like the diagrams we've seen in class).
|
||||
|
||||
The rest of your link can be a simple line; you may, but are not required, to draw an
|
||||
arrowhead at the end of the line.
|
||||
"""
|
||||
|
||||
|
||||
def draw_three_nodes(screen_size: Tuple[int, int]) -> None:
|
||||
"""Draw three nodes on a pygame screen of the given size.
|
||||
|
||||
You may choose the coordinates for the nodes, as long as they do not overlap with each other
|
||||
and are separated by at least 10 pixels. Each link must start in the CENTRE of the start node's
|
||||
right half, and must end on the border of the end node (not inside the end node).
|
||||
This matches the style of node from lecture.
|
||||
|
||||
The third node should link to "None", which you should visualize by calling draw_text.
|
||||
"""
|
||||
screen = initialize_screen(screen_size, [])
|
||||
node1 = _Node(1)
|
||||
node2 = _Node(2)
|
||||
node3 = _Node(3)
|
||||
node1.next = node2
|
||||
node2.next = node3
|
||||
|
||||
# TODO: Complete this function so that it draws the above three nodes and the
|
||||
# links between them and "None". Once you are done, remove this comment.
|
||||
...
|
||||
|
||||
# Don't change the code below (it simply waits until you close the Pygame window)
|
||||
pygame.display.flip()
|
||||
pygame.event.wait()
|
||||
pygame.display.quit()
|
||||
|
||||
|
||||
################################################################################
|
||||
# 2. Drawing a full linked list
|
||||
################################################################################
|
||||
def draw_list(screen: pygame.Surface, lst: LinkedList, show_grid: bool = False) -> None:
|
||||
"""Draw the given linked list on the screen.
|
||||
|
||||
The linked list nodes should be drawn in a grid pattern with GRID_SIZE rows and columns.
|
||||
See the assignment handout for details and constraints on your drawing.
|
||||
|
||||
If the show_grid parameter is True, the grid is drawn on the board. You can use
|
||||
the grid to help you make sure you're drawing nodes in the correct locations.
|
||||
By default, show_grid is False.
|
||||
|
||||
Preconditions:
|
||||
- len(lst) < GRID_SIZE * GRID_SIZE
|
||||
|
||||
As with draw_node, we strongly recommend initializing new constants at the top of this file
|
||||
to store numbers used to position the nodes.
|
||||
|
||||
We have started the linked list traversal pattern for you. Note that we're accessing
|
||||
a private LinkedList attribute _first, which is generally not a good practice but we're
|
||||
allowing you to do so here to simplify your code a little.
|
||||
"""
|
||||
if show_grid:
|
||||
draw_grid(screen)
|
||||
|
||||
curr = lst._first
|
||||
curr_index = 0
|
||||
|
||||
while curr is not None:
|
||||
...
|
||||
|
||||
curr = curr.next
|
||||
curr_index = curr_index + 1
|
||||
|
||||
|
||||
################################################################################
|
||||
# 3. Handling user events
|
||||
################################################################################
|
||||
def run_visualization(screen_size: tuple[int, int], ll_class: type,
|
||||
show_grid: bool = False) -> None:
|
||||
"""Run the linked list visualization.
|
||||
|
||||
Initialize a screen of the given size, and show the grid when show_grid is True.
|
||||
ll_class is the type of linked list to use.
|
||||
|
||||
Preconditions:
|
||||
- ll_class is LinkedList or issubclass(ll_class, LinkedList)
|
||||
|
||||
This function is provided for you for Part 3, and you *should not change it*.
|
||||
Instead, your task is to implement the helper function handle_mouse_click (and
|
||||
any other helpers you decide to add).
|
||||
"""
|
||||
# Initialize the Pygame screen, allowing for mouse click events.
|
||||
screen = initialize_screen(screen_size, [pygame.MOUSEBUTTONDOWN])
|
||||
|
||||
# Initialize a random linked list of length 50.
|
||||
lst = ll_class(random.sample(range(-99, 1000), 50))
|
||||
|
||||
while True:
|
||||
# Draw the list (on a white background)
|
||||
screen.fill(THECOLORS['white'])
|
||||
draw_list(screen, lst, show_grid)
|
||||
pygame.display.flip()
|
||||
|
||||
# Wait for an event (either pygame.MOUSEBUTTONDOWN or pygame.QUIT)
|
||||
event = pygame.event.wait()
|
||||
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
# Call our event handling method
|
||||
handle_mouse_click(lst, event, screen.get_size())
|
||||
elif event.type == pygame.QUIT:
|
||||
break
|
||||
|
||||
pygame.display.quit()
|
||||
|
||||
|
||||
def handle_mouse_click(lst: LinkedList, event: pygame.event.Event,
|
||||
screen_size: Tuple[int, int]) -> None:
|
||||
"""Handle a mouse click event.
|
||||
|
||||
A pygame mouse click event object has two attributes that are important for this method:
|
||||
- event.pos: the (x, y) coordinates of the mouse click
|
||||
- event.button: an int representing which mouse button was clicked.
|
||||
1: left-click, 3: right-click
|
||||
|
||||
The screen_size is a tuple of (width, height), and should be used together with
|
||||
event.pos to determine which cell is being clicked. If a click happens exactly on
|
||||
the boundary between two cells, you may decide which cell is selected.
|
||||
|
||||
Preconditions:
|
||||
- event.type == pygame.MOUSEBUTTONDOWN
|
||||
- screen_size[0] >= 200
|
||||
- screen_size[1] >= 200
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import python_ta
|
||||
python_ta.check_all(config={
|
||||
'max-line-length': 100,
|
||||
'disable': ['E1136'],
|
||||
'exclude-protected': ['_first'],
|
||||
'extra-imports': ['random', 'pygame', 'pygame.colordict', 'a1_linked_list'],
|
||||
'generated-members': ['pygame.*']
|
||||
})
|
||||
|
||||
import python_ta.contracts
|
||||
python_ta.contracts.check_all_contracts()
|
||||
Reference in New Issue
Block a user