forked from github/pelican
215 lines
7.9 KiB
ReStructuredText
215 lines
7.9 KiB
ReStructuredText
Contributing and feedback guidelines
|
|
####################################
|
|
|
|
There are many ways to contribute to Pelican. You can improve the
|
|
documentation, add missing features, and fix bugs (or just report them). You
|
|
can also help out by reviewing and commenting on
|
|
`existing issues <https://github.com/getpelican/pelican/issues>`_.
|
|
|
|
Don't hesitate to fork Pelican and submit an issue or pull request on GitHub.
|
|
When doing so, please adhere to the following guidelines.
|
|
|
|
.. include:: ../CONTRIBUTING.rst
|
|
|
|
Setting up the development environment
|
|
======================================
|
|
|
|
While there are many ways to set up one's development environment, we recommend
|
|
using `Virtualenv <https://virtualenv.pypa.io/en/stable/>`_. This tool allows
|
|
you to set up separate environments for separate Python projects that are
|
|
isolated from one another so you can use different packages (and package
|
|
versions) for each.
|
|
|
|
If you don't have ``virtualenv`` installed, you can install it via::
|
|
|
|
$ pip install virtualenv
|
|
|
|
Use ``virtualenv`` to create and activate a virtual environment::
|
|
|
|
$ virtualenv ~/virtualenvs/pelican
|
|
$ cd ~/virtualenvs/pelican
|
|
$ . bin/activate
|
|
|
|
Clone the Pelican source into a subfolder called ``src/pelican``::
|
|
|
|
$ git clone https://github.com/getpelican/pelican.git src/pelican
|
|
$ cd src/pelican
|
|
|
|
Install the development dependencies::
|
|
|
|
$ pip install -r requirements/developer.pip
|
|
|
|
Install Pelican and its dependencies::
|
|
|
|
$ python setup.py develop
|
|
|
|
Or using ``pip``::
|
|
|
|
$ pip install -e .
|
|
|
|
To conveniently test on multiple Python versions, we also provide a ``.tox``
|
|
file.
|
|
|
|
|
|
Building the docs
|
|
=================
|
|
|
|
If you make changes to the documentation, you should preview your changes
|
|
before committing them::
|
|
|
|
$ pip install -r requirements/docs.pip
|
|
$ cd docs
|
|
$ make html
|
|
|
|
Open ``_build/html/index.html`` in your browser to preview the documentation.
|
|
|
|
Running the test suite
|
|
======================
|
|
|
|
Each time you add a feature, there are two things to do regarding tests:
|
|
check that the existing tests pass, and add tests for the new feature
|
|
or bugfix.
|
|
|
|
The tests live in ``pelican/tests`` and you can run them using the
|
|
"discover" feature of ``unittest``::
|
|
|
|
$ python -Wd -m unittest discover
|
|
|
|
After making your changes and running the tests, you may see a test failure
|
|
mentioning that "some generated files differ from the expected functional tests
|
|
output." If you have made changes that affect the HTML output generated by
|
|
Pelican, and the changes to that output are expected and deemed correct given
|
|
the nature of your changes, then you should update the output used by the
|
|
functional tests. To do so, **make sure you have both** ``en_EN.utf8`` **and**
|
|
``fr_FR.utf8`` **locales installed**, and then run the following two commands::
|
|
|
|
$ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/custom/ \
|
|
-s samples/pelican.conf.py samples/content/
|
|
$ LC_ALL=fr_FR.utf8 pelican -o pelican/tests/output/custom_locale/ \
|
|
-s samples/pelican.conf_FR.py samples/content/
|
|
$ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/basic/ \
|
|
samples/content/
|
|
|
|
You may also find that some tests are skipped because some dependency (e.g.,
|
|
Pandoc) is not installed. This does not automatically mean that these tests
|
|
have passed; you should at least verify that any skipped tests are not
|
|
affected by your changes.
|
|
|
|
You should run the test suite under each of the supported versions of Python.
|
|
This is best done by creating a separate Python environment for each version.
|
|
Tox_ is a useful tool to automate running tests inside ``virtualenv``
|
|
environments.
|
|
|
|
.. _Tox: https://tox.readthedocs.io/en/latest/
|
|
|
|
Python 2/3 compatibility development tips
|
|
=========================================
|
|
|
|
Here are some tips that may be useful for writing code that is compatible with
|
|
both Python 2.7 and Python 3:
|
|
|
|
- Use new syntax. For example:
|
|
|
|
- ``print .. -> print(..)``
|
|
- ``except .., e -> except .. as e``
|
|
|
|
- Use new methods. For example:
|
|
|
|
- ``dict.iteritems() -> dict.items()``
|
|
- ``xrange(..) - > list(range(..))``
|
|
|
|
- Use ``six`` where necessary. For example:
|
|
|
|
- ``isinstance(.., basestring) -> isinstance(.., six.string_types)``
|
|
- ``isinstance(.., unicode) -> isinstance(.., six.text_type)``
|
|
|
|
- Assume every string and literal is Unicode:
|
|
|
|
- Use ``from __future__ import unicode_literals``
|
|
- Do not use the prefix ``u'`` before strings.
|
|
- Do not encode/decode strings in the middle of something. Follow the code to
|
|
the source/target of a string and encode/decode at the first/last possible
|
|
point.
|
|
- In particular, write your functions to expect and to return Unicode.
|
|
- Encode/decode strings if the string is the output of a Python function that
|
|
is known to handle this badly. For example, ``strftime()`` in Python 2.
|
|
- Do not use the magic method ``__unicode()__`` in new classes. Use only
|
|
``__str()__`` and decorate the class with ``@python_2_unicode_compatible``.
|
|
|
|
- ``setlocale()`` in Python 2 fails when we give the locale name as Unicode,
|
|
and since we are using ``from __future__ import unicode_literals``, we do
|
|
that everywhere! As a workaround, enclose the locale name with ``str()``;
|
|
in Python 2 this casts the name to a byte string, while in Python 3 this
|
|
should do nothing, because the locale name was already Unicode.
|
|
- Do not start integer literals with a zero. This is a syntax error in Python 3.
|
|
- Unfortunately there seems to be no octal notation that is valid in both
|
|
Python 2 and 3. Use decimal notation instead.
|
|
|
|
|
|
Logging tips
|
|
============
|
|
|
|
Try to use logging with appropriate levels.
|
|
|
|
For logging messages that are not repeated, use the usual Python way::
|
|
|
|
# at top of file
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# when needed
|
|
logger.warning("A warning with %s formatting", arg_to_be_formatted)
|
|
|
|
Do not format log messages yourself. Use ``%s`` formatting in messages and pass
|
|
arguments to logger. This is important, because Pelican logger will preprocess
|
|
some arguments (like Exceptions) for Py2/Py3 compatibility.
|
|
|
|
Limiting extraneous log messages
|
|
--------------------------------
|
|
|
|
If the log message can occur several times, you may want to limit the log to
|
|
prevent flooding. In order to do that, use the ``extra`` keyword argument for
|
|
the logging message in the following format::
|
|
|
|
logger.warning("A warning with %s formatting", arg_to_be_formatted,
|
|
extra={'limit_msg': 'A generic message for too many warnings'})
|
|
|
|
Optionally, you can also set ``'limit_args'`` as a tuple of arguments in
|
|
``extra`` dict if your generic message needs formatting.
|
|
|
|
Limit is set to ``5``, i.e, first four logs with the same ``'limit_msg'`` are
|
|
outputted normally but the fifth one will be logged using
|
|
``'limit_msg'`` (and ``'limit_args'`` if present). After the fifth,
|
|
corresponding log messages will be ignored.
|
|
|
|
For example, if you want to log missing resources, use the following code::
|
|
|
|
for resource in resources:
|
|
if resource.is_missing:
|
|
logger.warning(
|
|
'The resource %s is missing', resource.name,
|
|
extra={'limit_msg': 'Other resources were missing'})
|
|
|
|
The log messages will be displayed as follows::
|
|
|
|
WARNING: The resource prettiest_cat.jpg is missing
|
|
WARNING: The resource best_cat_ever.jpg is missing
|
|
WARNING: The resource cutest_cat.jpg is missing
|
|
WARNING: The resource lolcat.jpg is missing
|
|
WARNING: Other resources were missing
|
|
|
|
|
|
Outputting traceback in the logs
|
|
--------------------------------
|
|
|
|
If you're logging inside an ``except`` block, you may want to provide the
|
|
traceback information as well. You can do that by setting ``exc_info`` keyword
|
|
argument to ``True`` during logging. However, doing so by default can be
|
|
undesired because tracebacks are long and can be confusing to regular users.
|
|
Try to limit them to ``--debug`` mode like the following::
|
|
|
|
try:
|
|
some_action()
|
|
except Exception as e:
|
|
logger.error('Exception occurred: %s', e,
|
|
exc_info=settings.get('DEBUG', False))
|