eecs490.org

Coding Standards

Good code must be readable, maintainable, and make proper use of abstractions, in addition to behaving correctly. As such, we will be grading programming projects not just for functionality, but also for programming practices and style.

Python

We will adhere to the standard style guides for Python, specifically PEP 8 for code and PEP 257 for documentation. We recommend reading through both guides before writing any code.

Automated Style Checking for Python

Python has a number of automated tools for style checking. We will use the following tools:

As these tools are constantly evolving, we will use pinned versions so that you have a fixed set of targets to which you can write your code. The following will install these pinned versions in your Python environment:

$ wget https://eecs490.github.io/eecs490.org/requirements.txt
$ pip3 install -r requirements.txt

You may need to run the second command with sudo.

For some projects, we will use these tools without any command-line options:

$ pycodestyle graph.py graph_test.py pagerank.py
$ pydocstyle graph.py graph_test.py pagerank.py
$ pylint graph.py graph_test.py pagerank.py

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

For other projects, we will customize the behavior with command-line arguments. We will provide Makefile targets to run the tools with the appropriate arguments:

$ make style-pycode
pycodestyle ucbase.py ucexpr.py ucfunctions.py ucstmt.py uctypes.py
$ make style-pydoc
pydocstyle ucbase.py ucexpr.py ucfunctions.py ucstmt.py uctypes.py
$ make style-pylint
pylint --max-args=6 --max-module-lines=1500 --disable=attribute-defined-outside-init --disable=no-member ucbase.py ucexpr.py ucfunctions.py ucstmt.py uctypes.py

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

$ make style
pycodestyle ucbase.py ucexpr.py ucfunctions.py ucstmt.py uctypes.py
pydocstyle ucbase.py ucexpr.py ucfunctions.py ucstmt.py uctypes.py
pylint --max-args=6 --max-module-lines=1500 --disable=attribute-defined-outside-init --disable=no-member ucbase.py ucexpr.py ucfunctions.py ucstmt.py uctypes.py

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

You may not use any directives to disable the checks in these tools. Specifically, we prohibit the text pylint or noqa from appearing in any of the Python files you submit.

Advice

In some cases, the automated tools take positions that are stricter than those in PEP 8. We will discuss some of these situations here and provide advice on how to satisfy the tools.

Visual Conflicts with Multiline Conditionals

PEP 8 explicitly takes no position on indentation for multiline conditionals, where the second an subsequent lines are not visually distinguishable from the nested suite:

    if (prompt is not None and line != '' and
        not line.lstrip().startswith(comment)):
        print(prompt + line)

On the other hand, Pylint does not permit multiline conditionals to be indented as above. Instead, indent the conditional as follows:

    if (
            prompt is not None and line != '' and
            not line.lstrip().startswith(comment)
    ):
        print(prompt + line)

The parentheses are on their own lines, with the actual lines of the test indented a total of eight spaces beyond the level of the conditional. This avoids a visual confict with the indentation of the nested suite.

Unused Arguments

Pylint will flag function arguments that are unused in the body. In some cases, however, the argument is required to adhere to a specific interface, such as when overriding a method or using higher-level functions. In such a case, use the common Python convention of a single underscore to indicate that the variable is ignored:

    def map(self, _):
        """Map the given function across the items in this list."""
        return self

No Self Use

Pylint also flags base-class methods that do not use the self parameter. Such a method can be written as a static method instead:

    @staticmethod
    def is_procedure():
        """Return whether this represents a Scheme procedure.

        Returns true for both primitive and user-defined Scheme
        procedures.
        """
        return False

Unlike in other languages, static methods can be overridden, since Python always uses dynamic binding. A static method can even be overridden by an instance method, as long as the method is called with a receiver that is an instance and not a class itself.

Programming Practices

Some projects will also be hand-graded for programming practices such as making appropriate use of recursion and object-orientation and avoiding code duplication.

Recursion

Recursive functions should avoid unnecessary code repetition and properly respect the abstraction barrier between an initial case and a recursive step. When work can be done by a recursive call, you should make the recursive call rather than repeating the computation in the caller.

Object Orientation

When writing object-oriented code, make appropriate use of inheritance and polymorphism to avoid code duplication and hard-coding types. Code that is shared among several classes should be written once in a base class and inherited. Derived-class methods that add functionality to that of the base-class method should make a call to the base-class method (using a mechanism such as super() in Python) rather than duplicating code. Method overriding should be used to customize behavior in a derived class, rather than type introspection (e.g. isinstance() in Python) followed by manual dispatch to custom code.