Created
October 22, 2010 09:19
-
-
Save nibrahim/640218 to your computer and use it in GitHub Desktop.
Code samples in beamer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\documentclass {beamer} | |
\usepackage{graphics} | |
\usepackage{listings} | |
\usetheme{Berkeley} | |
\title {Plugin frameworks} | |
\subtitle {3 approaches to designing plugin APIs} | |
\author[noufal] {Noufal Ibrahim \\ \texttt{[email protected]}} | |
\date {25 September 2010} | |
\institute {PyCon India 2010, MSRIT, Bangalore} | |
\begin{document} | |
\lstset{language=Python, | |
basicstyle=\tiny, | |
numbers=left, | |
numberstyle=\tiny, | |
numbersep=5pt, | |
commentstyle=\color{blue}, | |
showstringspaces=false | |
} | |
\begin{frame} | |
\titlepage | |
\end{frame} | |
\section {Introduction} | |
\subsection {About me} | |
\begin{frame} | |
\frametitle {About me} | |
\begin{itemize} | |
\item Freelance programmer/trainer based in Bangalore | |
\item Organiser of PyCon India 2009, 2010 | |
\end{itemize} | |
\end{frame} | |
\subsection {About this talk} | |
\begin{frame} | |
\frametitle {About this talk} | |
\begin{itemize} | |
\item This talk about approaches to writing plugin frameworks using examples | |
\begin {enumerate} | |
\item TRAC - The project planning/tracking tool from edgewall software. | |
\item py.test - The test harness from the PyPy project. | |
\item Emacs - The extensible text editor from the GNU project. | |
\end {enumerate} | |
\item Informed by my lifelong(?) experiences with Emacs and my work | |
with \texttt{py.test} and \texttt{trac} over the past two years | |
\item Inspired by a Stack Overflow discussion I had at \texttt{http://bit.ly/bznQ2g} | |
\end{itemize} | |
\end{frame} | |
\subsection {Plugin frameworks} | |
\begin {frame} | |
\frametitle {Plugin frameworks} | |
\begin{itemize} | |
\item Controlled ways to run user code inside your application | |
\pause | |
\item Used to shift the burden of writing your program to your user | |
\pause | |
\item Used to defer important decisions for later so that they can | |
be made when you know enough | |
\pause | |
\item Used to write \emph{frameworks} that can be used for multiple | |
tasks | |
\pause | |
\item To reduce the size of the application | |
\pause | |
\item Generally fun to do | |
\end{itemize} | |
\end {frame} | |
\section {Case \#1 : Trac} | |
\subsection {Trac Component architecture} | |
\begin{frame} | |
\frametitle {Trac : Component architecture} | |
\begin{itemize} | |
\item Designed using a \emph{Component architecture}. | |
\item Each component exposes ``extension points'' that can plugged | |
into by other components. | |
\includegraphics[height=3cm]{xtnpt.png} | |
\item Components are singletons | |
\item Simplified version of the \texttt{Zope Component Architecture} | |
\item Details at \texttt{http://trac.edgewall.org/wiki/TracDev/ComponentArchitecture} | |
\item Very popular architecture. Used in other applications like | |
Eclipse. | |
\end{itemize} | |
\end{frame} | |
\subsection {Writing a plugin} | |
\begin{frame} | |
\frametitle {Trac : Writing a plugin} | |
\begin{itemize} | |
\item Write a new component (subclass \texttt{component}). | |
\item Interfaces are created by subclassing \texttt{Interface} and | |
then wrapping them in an \texttt{ExtensionPoint}. | |
\item The \texttt{Interface} will have methods defined in them that | |
can be overridden by user created \texttt{Components} that | |
\emph{implement} the interface. | |
\end{itemize} | |
\end{frame} | |
\subsection {Example} | |
\begin{frame}[fragile] | |
\frametitle {Trac : Example plugin} | |
\begin{lstlisting} | |
# Following is the code in the application | |
# The interface and it's specification | |
class ITodoObserver(Interface): | |
def todo_added(name, description): | |
"Called when a to-do item is added." | |
class TodoList(Component): | |
# Declaration of an extension point | |
observers = ExtensionPoint(ITodoObserver) | |
def __init__(self): | |
self.todos = {} | |
def add(self, name, description): | |
assert not name in self.todos, 'To-do already in list' | |
self.todos[name] = description | |
for observer in self.observers: # Using the extension point | |
observer.todo_added(name, description) | |
# A plugin that prints out details when something is added | |
class TodoPrinter(Component): | |
# Stating which interface is implemented | |
implements(ITodoObserver) | |
def todo_added(self, name, description): | |
prinyt 'TODO:', name | |
print ' ', description | |
\end{lstlisting} | |
\end{frame} | |
\subsection {Pros/Cons} | |
\begin {frame} | |
\frametitle {Trac : Pros/Cons} | |
\begin{itemize} | |
\item Pros | |
\begin{itemize} | |
\item Very ``Object Oriented'' | |
\end{itemize} | |
\item Cons | |
\begin{itemize} | |
\item High entry barrier (need to learn ``plugin API'') | |
\item Lots of boilerplate code | |
\end{itemize} | |
\end{itemize} | |
\end {frame} | |
\section {Case \#2 : py.test} | |
\subsection {Metaprogramming} | |
\begin{frame} | |
\frametitle {py.test : Metaprogramming} | |
\begin{itemize} | |
\item Takes the ``best api is no api'' to the extreme. | |
\item Relies on naming conventions to insert code into the framework. | |
\item Searches multiple sources for ``plugin'' files to load | |
(setuptools entry points, \texttt{-p} option, | |
\texttt{conftest.py} etc.) | |
\item Inside the plugin file, hooks can be defined using the | |
\texttt{pytest\_} prefix (e.g. \texttt{pytest\_addoption} to add a | |
command line option). | |
\end{itemize} | |
\end{frame} | |
\subsection{Writing a plugin} | |
\begin{frame} | |
\frametitle{py.test : Writing a plugin} | |
\begin{itemize} | |
\item Implement the hooks that are necessary for the plugin to work | |
(e.g. add a few command line options, initialise some stuff before | |
the tests start, print out some extra information when tests are done). | |
\item Inform \texttt{py.test} that this plugin file needs to be | |
picked up at startup time. | |
\item Similar in some sense to the first approach but using modules | |
instead of singleton classes. | |
\end{itemize} | |
\end{frame} | |
\subsection {Example} | |
\begin{frame}[fragile] | |
\frametitle{py.test : Example} | |
\begin{lstlisting} | |
class BugZillaInteg(object): | |
def pytest_report_teststatus(self,report): | |
"Hook called when test status is created" | |
if report.failed: | |
if self.analysed(report.item.function.__doc__): | |
return "analysed", "A", "ANALYSED" | |
def __init__(self, config, bugzilla): | |
self.config = config | |
self.bugzilla = bugzilla | |
def pytest_configure(config): | |
# Logging in into bugzilla | |
bzilla = pyzilla.BugZilla(config.getvalue("bugzilla_url"), | |
config.getvalue("bugzilla_verbose")) | |
bzilla.login (username = config.getvalue("bugzilla_username"), | |
password = config.getvalue("bugzilla_pw")) | |
# Register the plugin | |
config.pluginmanager.register(BugZillaInteg(config, bzilla), "bugzilla") | |
\end{lstlisting} | |
\end{frame} | |
\subsection {Pros/Cons} | |
\begin{frame} | |
\frametitle {py.test : Pros/Cons} | |
\begin{itemize} | |
\item Pros | |
\begin{itemize} | |
\item Very easy to write a plugin | |
\item Very easy to visualise what's going on. | |
\item High level of flexibility. | |
\end{itemize} | |
\item Cons | |
\begin{itemize} | |
\item The internals of py.test can get complex. | |
\end{itemize} | |
\end{itemize} | |
\end{frame} | |
\section {Case \#3 : Emacs} | |
\subsection {Embedded scripting language} | |
\begin{frame} | |
\frametitle{Emacs : Embedded scripting language} | |
\begin{itemize} | |
\item Build your application as a ``library'' for a scripting | |
language. | |
\item The primitives will be hardcoded and the rest built on top in | |
the scripting language. | |
\item No real ``API''. The boundaries between plugin and core get | |
blurred. | |
\item Emacs is a successful example. It's an ``editor'' that works | |
as an email client, IRC client, PIM, web browser, twitter client, | |
video editor, mp3 player and other things. | |
\end{itemize} | |
\end{frame} | |
\subsection{Writing a plugin} | |
\begin{frame} | |
\frametitle {Emacs : Writing a plugin} | |
\begin{itemize} | |
\item Write a script just like one would do for any language | |
\item Evaluate the script (or drop it into the init file) | |
\end{itemize} | |
\end{frame} | |
\lstset{language=lisp} | |
\subsection {Example} | |
\begin{frame}[fragile] | |
\frametitle{Emacs : Example} | |
\begin{lstlisting} | |
(defun list-issues () | |
"Shows all TBDs in current tree in all files of the same type as current" | |
(interactive) | |
(let ((dir (file-name-directory (buffer-file-name))) | |
(ext (file-name-extension (buffer-file-name)))) | |
(rgrep "TBD" (concat "*." ext) dir)) | |
(other-window 1)) | |
(global-set-key (kbd "<f11>") 'list-issues) | |
\end{lstlisting} | |
\end{frame} | |
\section {Thoughts} | |
\begin{frame} | |
\frametitle {Thoughts} | |
General feelings on how these things should be done. | |
\pause | |
\begin{itemize} | |
\item Try to keep the non extensible core as small as possible. | |
\item Implement the smallest subset of what you need and make the | |
rest plugins. | |
\item Move all non essential parts to plugin land. | |
\item Reduce boilerplate as much as possible. Make it \emph{easy} | |
(and not just possible) for users to extend your software. Rely on | |
conventions rather than writing lots of code. | |
\item If you're going the Emacs route, embed a \emph{real} scripting | |
language and not just a toy or ad-hoc one. | |
\end{itemize} | |
\end{frame} | |
\end{document} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment