This section describes the requirements and conventions used to contribute Python programming to the edX platform.
Generally, do not edit files just to change the style. But do aim for this style with new or modified code.
See also Code Quality.
Each class should have a __repr__() method defined, so that calling repr() on an instance of the class returns something meaningful that distinguishes objects from each other to a human being. This is useful for debugging purposes.
Follow PEP 8.
Follow these guidelines:
# NO NO NO!:
results = my_object.some_method(arg1, # this is very
arg2, # very ugly and makes
arg3, # the code squished over on the right.
)
# YES:
results = my_object.some_method(
arg1,
arg2,
arg3,
)
# OR:
results = my_object.some_method(
arg1, arg2, arg3
)
Important points:
PEP8 recommends a most-general to most-specific import order, which means this order:
Alphabetize each group of imports, and use a single blank line to separate groups.
Note
Most Open edX repositories use the isort library, which will automatically order imports to follow PEP8.
For unused args, you can prefix the arguments with an underscore (_) to mark them as unused (as convention), and pylint will accept that.
Adding a TODO in one place requires you to make a pylint fix in another (just to force us to clean up more code).
No bare except
clauses. except:
should be except Exception:
, which
will prevent it from catching system-exiting exceptions, which we probably
should not be doing anyway. If we need to, we can catch BaseException
(There’s no point in catching BaseException
, that includes the exceptions
we didn’t want to catch with except:
in the first place.) (ref:
http://docs.python.org/2/library/exceptions.html#bltin-exceptions). Catching
Exception
, however, will still generate a Pylint warning (“W0703: catching
too general exception.”) If you still feel that catching Exception
is
justified, silence the pylint warning with a pragma: # pylint: disable=broad-
except
.
Although we try to be vigilant and resolve all quality violations, some Pylint violations are just too challenging to resolve, so we opt to ignore them via use of a pragma. A pragma tells Pylint to ignore the violation in the given line. An example is:
self.assertEquals(msg, form._errors['course_id'][0]) # pylint: disable=protected-access
The pragma starts with a #
two spaces after the end of the line. We
prefer that you use the full name of the error (pylint: disable=unused-
argument
as opposed to pylint: disable=W0613
), so that it is more clear what
you are disabling in the line.
It’s better to use a class or a namedtuple
to pass around data that has a
fixed shape than to use a dict
. It makes it easier to debug (because there
is a fixed, named set of attributes), and it helps prevent accidental errors
of either setting new attributes into the dictionary (which might, for
instance, get serialized unexpectedly), or might be typos.
Follow PEP 257.
The preferred style is so-called “Google Style” with readable headers for different sections, and all arguments and return values defined.
Note
There is one exception to the preferred style. REST APIs created using Django REST Framework (DRF) must use a hybrid format that is suitable both for DRF and ReadTheDocs. For more information see the edX REST API Conventions.
For additional information see these references.
Here’s how you write documentation in a mostly “Google Style” manner:
def func(arg1, arg2):
"""
Summary line.
Extended description of function.
Arguments:
arg1 (int): Description of arg1
arg2 (str): Description of arg2
Returns:
bool: Description of return value
"""
Note
There are some exceptions:
Most of our code is written using an older style:
def calculate_grade(course, student):
"""
Sum up the grade for a student in a particular course.
Navigates the entire course, adding up the student's grades. Note that
blah blah blah, and also beware that blah blah blah.
`course` is an `EdxCourseThingy`. The student must be registered in the
course, or a `NotRegistered` exception will be raised.
`student` is an `EdxStudentThingy`.
Returns a dict with two keys: `total` is a float, the student's total
score, and `outof` is the maximum possible score.
"""
If you only have a single line in your docstring, first consider that this is almost certainly not enough documentation, and write some more. But if you do have just one line, format it in a similar way to a multi-line docstring:
def foo(a, b):
"""
Computes the foo of a and b.
"""
Not like this:
def foo(a, b):
"""Computes the foo of a and b.""" # NO NO NO
We intentionally stray from PEP 257 in this case. The formatting inconsistency between single and multi-line docstrings can result in merge conflicts when upstream and downstream branches change the same docstring. See this GitHub comment for more context.