Skip to content

Commit

Permalink
Yet another huge documentation commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Bagrat Aznauryan committed Jun 22, 2015
1 parent b0bf12c commit 06d36ea
Show file tree
Hide file tree
Showing 19 changed files with 177 additions and 48 deletions.
10 changes: 8 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys

sys.path.insert(0, os.path.abspath('../'))

# -- General configuration ------------------------------------------------

Expand All @@ -31,7 +34,8 @@
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinxcontrib.napoleon'
# 'sphinx.ext.viewcode',
# 'sphinxcontrib.googleanalytics',
]

Expand Down Expand Up @@ -279,3 +283,5 @@
googleanalytics_id = 'UA-58471028-2'

rst_epilog = '.. |pylease_version| replace:: %s' % version

napoleon_google_docstring = True
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ This documentation includes all the information needed for you to use Pylease, a
pages/plugin
pages/config
pages/ext
pages/ref


1 change: 0 additions & 1 deletion doc/pages/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ However, at some point in time you will need to make some configuration to use P

Following is the list of all configuration parameters with their descriptions.

.. _version-files:
``version-files``
A list of files where the version must be updated to the new one. Here is an example value for this parameter:

Expand Down
34 changes: 33 additions & 1 deletion doc/pages/ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@ plug it into Pylease.

So basically there are two ways to extend Pylease:

- Add new commands
- Add before and after tasks for existing commands
- Add new commands

In the last subsection you will find a detailed tutorial on both options for extending Pylease.

Extending Existing Commands
---------------------------

Here is the step-by-setp guide on how to add extensions to Pylease.

The first step is inheriting the :class:`~pylease.ext.Extension` class, where you will place the initialisation code by implementing the
:func:`~pylease.ext.Extension.load` method. As an instance attribute you will have the :attr:`~pylease.ext.Extension._lizy` attribute,
which is an instance of :class:`~pylease.Pylease` class, and contains everything you need for your extension.

Extending an existing Pylease command is done by adding a :class:`~pylease.cmd.task.BeforeTask` or :class:`~pylease.cmd.task.AfterTask`
(or both) to it. What is needed to do is just implement those classes and add their instances to the command that is the subject of
extension. For both :class:`~pylease.cmd.task.BeforeTask` and :class:`~pylease.cmd.task.AfterTask` you need to inherit and implement
their :func:`~pylease.cmd.task.BeforeTask.execute` method, which must include the extension logic. Also, in case of
:class:`~pylease.cmd.task.AfterTask`, you are provided with the :attr:`~pylease.cmd.task.AfterTask._command_result` attribute, which is
the result returned by the command being extended.

So basically this is the scenario of extending a Pylease command:

- Inherit and implement :class:`~pylease.cmd.task.BeforeTask` or/and :class:`~pylease.cmd.task.AfterTask`
- Inherit :class:`~pylease.ext.Extension`
- In the :func:`~pylease.ext.Extension.load` implementation get the corresponding :class:`~pylease.cmd.Command` instance from the
:attr:`~pylease.ext.Extension._lizy` singleton
- Add the :class:`~pylease.cmd.task.BeforeTask` or/and :class:`~pylease.cmd.task.AfterTask` instances to the command instance

Adding New Commands
-------------------

To add a new command to Pylease it is enough to implement a class by inheriting the :class:`~pylease.cmd.Command` class and add it to your
package
``__init__.py``.
2 changes: 1 addition & 1 deletion doc/pages/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ And then just check if everything went fine:
.. parsed-literal::
$ pylease --version
Pylease version \ |pylease_version|\
Pylease version \ |version|\
Workflow
--------
Expand Down
32 changes: 30 additions & 2 deletions doc/pages/ref.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,30 @@
Complete Reference
==================
Class Reference
===============

.. automodule:: pylease

.. autoclass:: Pylease

.. autoclass:: InfoContainer

.. automethod:: set_info



.. automodule:: pylease.ext

.. autoclass:: Extension

.. automethod:: load

.. automodule:: pylease.cmd.task

.. autoclass:: BeforeTask

.. automethod:: execute

.. autoclass:: AfterTask

.. automethod:: execute

.. autoattribute:: _command_result
59 changes: 58 additions & 1 deletion pylease/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@


class Pylease(object):
"""
The main class of Pylease, which contains all the needed resources for extensions. This class is initialised once and by Pylease,
which is the so called ``lizy`` object. It is passed to :class:`~pylease.cmd.Command` and :class:`~pylease.ext.Extension` instances.
Attributes:
info_container (pylease.InfoContainer): Contains information about current status of the project. Minimal information is
``name`` and ``version``.
commands (dict): A dictionary of Pylease commands, including commands defined in extensions if any. The values of the dictionary
are instances of :class:`~pylease.cmd.Command` class.
parser (argparse.ArgumentParser): The root parser of Pylease. Use this object to add command line arguments to Pylease on the
same level as ``--version`` and ``--help``.
config (dict): A dictionary representing the configuration parsed from ``setup.cfg`` defined under ``[pylease]`` section. If a
configuration value in the configuration file is defined as ``key1 = valA, valB, valC`` then the value of the ``key1`` key of
this attribute will be an instance of :class:`~list` and be equal to ``['valA', 'valB, 'valC']``.
"""
def __init__(self, parser, cmd_subparsers, info_container):
super(Pylease, self).__init__()

Expand Down Expand Up @@ -53,7 +68,49 @@ def get_plugins(self):
return self._get_config_list_value('use-plugins') or []

def _load_extensions(self):
extension_packages = self._get_config_list_value('use-plugins') or []
extension_packages = self.get_plugins()

for package in extension_packages:
__import__(package)


class InfoContainer(object):
# pylint: disable=too-few-public-methods
"""
A simple container that maps a provided dictionary to its attributes. This provides the current status of the project,
and the minimal built-in information attributes are the following:
Attributes:
name (str): The name of the project.
version (str): The current versin of the project
is_empty (bool): The status of current working directory, i.e. indicates whether it is empty or not.
"""
def __init__(self):
super(InfoContainer, self).__init__()

self.name = None
self.version = None
self.is_empty = False

def set_info(self, **kwargs):
"""
Used to extend the information about the project.
Example:
Below are two options on how to use/extend the ``InfoContainer``::
info = InfoContainer()
# Option 1
info.set_info(info1='value2', info2='value2')
# Option 2
more_info = {'info3': 'value3'}
info.set_info(**more_info)
# Then you can access your info as instance attributes
print(info.info2) # will print 'value2'
"""
for key in kwargs:
setattr(self, key, kwargs[key])
2 changes: 1 addition & 1 deletion pylease/command/__init__.py → pylease/cmd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import print_function
from abc import ABCMeta, abstractmethod
import os
from pylease.command.rollback import Rollback, Stage
from pylease.cmd.rollback import Rollback, Stage

from pylease.logger import LOGME as logme # noqa
from pylease.ctxmgmt import Caution
Expand Down
File renamed without changes.
14 changes: 13 additions & 1 deletion pylease/command/task.py → pylease/cmd/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@


class BeforeTask(object):
"""
"""
# pylint: disable=too-few-public-methods
# The number of public methods is reasonable for this kind of class

Expand Down Expand Up @@ -43,16 +46,25 @@ def __call__(self, lizy, args):

@abstractmethod
def execute(self, lizy, args):
"""
The place where the extension logic goes on.
Arguments:
lizy (pylease.Pylease): The :class:`~pylease.Pylease` singleton that provides all the needed information about the project.
args (argparse.Namespace): The arguments supplied to the command line.
"""
pass # pragma: no cover


class AfterTask(BeforeTask):
# pylint: disable=too-few-public-methods, W0223
# AfterTask is also abstract
# The number of public methods is reasonable for this kind of class

__metaclass__ = ABCMeta

@property
def _command_result(self):
"""
A dictionary containing information by the completion of the command execution.
"""
return self._command.result # pragma: no cover
11 changes: 11 additions & 0 deletions pylease/extension/__init__.py → pylease/ext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@


class Extension(object):
"""
The entry point to implementing Pylease extensions. Pylease loads subclasses of this class and invokes the
:func:`~pylease.ext.Extension.load` method.
Attributes:
_lizy (pylease.Pylease): The :class:`~pylease.Pylease` singleton, that is initialised and passed to all subclass instances.
"""
# pylint: disable=abstract-class-not-used
__metaclass__ = ABCMeta

Expand All @@ -20,6 +27,10 @@ def __init__(self, lizy):

@abstractmethod
def load(self):
"""
This method is being called by Pylease when all the extensions are being loaded. All the initialisation code must be implemented
in the body of this method.
"""
pass # pragma: no cover

@classmethod
Expand Down
8 changes: 4 additions & 4 deletions pylease/extension/git.py → pylease/ext/git.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from subprocess import call
import subprocess
from pylease.command.rollback import Rollback, Stage
from pylease.cmd.rollback import Rollback, Stage
from pylease.ex import PyleaseError
from pylease.command.task import AfterTask
from pylease.extension import Extension
from pylease.cmd.task import AfterTask
from pylease.ext import Extension
from pylease.logger import LOGME as logme # noqa


Expand All @@ -30,7 +30,7 @@ def load(self):
make_command.add_after_task(GitAfterTask())


class GitAfterTask(AfterTask): # pragma: no cover - Unable to test this other than manually TODO: try to test
class GitAfterTask(AfterTask): # pragma: no cover - Unable to test this other than manually TODO: try to
# pylint: disable=too-few-public-methods
TAG_MESSAGE_FMT = 'Prepare release v{version}'

Expand Down
6 changes: 3 additions & 3 deletions pylease/extension/pypi.py → pylease/ext/pypi.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import sys
from pylease.command.rollback import Rollback, Stage
from pylease.cmd.rollback import Rollback, Stage
from pylease.logger import LOGME as logme # noqa
from pylease.command.task import AfterTask
from pylease.extension import Extension
from pylease.cmd.task import AfterTask
from pylease.ext import Extension


class PypiExtension(Extension):
Expand Down
8 changes: 4 additions & 4 deletions pylease/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import pylease
import pylease.logger as logger
from pylease.extension import Extension
from pylease.extension import git, pypi # noqa
from pylease.command import Command
from pylease.ext import Extension
from pylease.ext import git, pypi # noqa
from pylease.cmd import Command
from pylease.ctxmgmt import ReplacedSetup
from pylease.vermgmt import InfoContainer
from pylease import InfoContainer

__author__ = 'bagrat'

Expand Down
17 changes: 0 additions & 17 deletions pylease/vermgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,3 @@ def __str__(self):
suffix = '.dev{ver}'.format(ver=self.dev) if self.dev > 0 else ''

return '{prefix}{suffix}'.format(prefix=prefix, suffix=suffix)


class InfoContainer(object):
# pylint: disable=too-few-public-methods
"""
A simple container that maps a provided dictionary to its attributes.
"""
def __init__(self):
super(InfoContainer, self).__init__()

self.name = None
self.version = None
self.is_empty = False

def set_info(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
7 changes: 3 additions & 4 deletions tests/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
from mock import patch, MagicMock, call
from nose.tools import ok_, eq_
from pylease.ex import PyleaseError
from pylease import Pylease
from pylease import Pylease, InfoContainer
from pylease.util import SubclassIgnoreMark
from pylease.command import Command, NamedCommand
from pylease.command.task import BeforeTask, AfterTask
from pylease.vermgmt import InfoContainer
from pylease.cmd import Command, NamedCommand
from pylease.cmd.task import BeforeTask, AfterTask


class CommandTest(TestCase):
Expand Down
8 changes: 4 additions & 4 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from nose.tools import eq_, ok_
import sys
import pylease
from pylease.command import StatusCommand, InitCommand
from pylease.extension import Extension
from pylease.cmd import StatusCommand, InitCommand
from pylease.ext import Extension
from pylease.main import main
from tests import PyleaseTest, MockedSetupPy, CapturedStdout, MockedFile, MockedFileWrite

Expand Down Expand Up @@ -132,7 +132,7 @@ def test_pylease_must_load_external_extensions(self):
extension_package_name = "some_extension"
extension_class_name = "SomeExtension"
extension_contents = textwrap.dedent("""
from pylease.extension import Extension
from pylease.ext import Extension
from mock import MagicMock
class {}(Extension):
Expand Down Expand Up @@ -192,7 +192,7 @@ def test_init_command_must_initialize_a_new_project(self):
os.listdir = MagicMock()
os.listdir.return_value = []

with patch('pylease.command.open', mock_open(), create=True) as open_mock:
with patch('pylease.cmd.open', mock_open(), create=True) as open_mock:
main(['init', project_name])

os.mkdir = orig_mkdir
Expand Down
2 changes: 1 addition & 1 deletion tests/test_rollback.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from mock import MagicMock
from nose.tools import eq_, ok_
from pylease.command.rollback import Rollback, Stage
from pylease.cmd.rollback import Rollback, Stage
from tests import PyleaseTest

stage1 = 'stage1'
Expand Down
Loading

0 comments on commit 06d36ea

Please sign in to comment.