PK/EA˟99gaffer-0.4.4/util.html util Module — gaffer documentation

util Module

gaffer.util.bytes2human(n)[source]

Translates bytes into a human repr.

gaffer.util.bytestring(s)[source]
gaffer.util.check_gid(val)[source]

Return a gid, given a group value

If the group value is unknown, raises a ValueError.

gaffer.util.check_uid(val)[source]

Return an uid, given a user value. If the value is an integer, make sure it’s an existing uid.

If the user value is unknown, raises a ValueError.

gaffer.util.daemonize()[source]

Standard daemonization of a process.

gaffer.util.from_nanotime(n)[source]

convert from nanotime to seconds

gaffer.util.get_maxfd()[source]
gaffer.util.getcwd()[source]

Returns current path, try to use PWD env first

gaffer.util.is_ipv6(addr)[source]
gaffer.util.nanotime(s=None)[source]

convert seconds to nanoseconds. If s is None, current time is returned

gaffer.util.parse_address(netloc, default_port=8000)[source]
gaffer.util.setproctitle_(title)[source]
gaffer.util.substitute_env(s, env)[source]

Previous topic

pidfile Module

Next topic

tornado_pyuv Module

This Page




PK/EA`gaffer-0.4.4/manager.html manager Module — gaffer documentation

manager Module

The manager module is a core component of gaffer. A Manager is responsible of maintaining processes and allows you to interract with them.

Classes

class gaffer.manager.Manager(loop=None)[source]

Bases: object

Manager - maintain process alive

A manager is responsible of maintaining process alive and manage actions on them:

  • increase/decrease the number of processes / process template
  • start/stop processes
  • add/remove process templates to manage

The design is pretty simple. The manager is running on the default event loop and listening on events. Events are sent when a process exit or from any method call. The control of a manager can be extended by adding apps on startup. For example gaffer provides an application allowing you to control processes via HTTP.

Running an application is done like this:

# initialize the application with the default loop
loop = pyuv.Loop.default_loop()
m = Manager(loop=loop)

# start the application
m.start(apps=[HttpHandler])

.... # do smth

m.stop() # stop the controlller
m.run() # run the event loop

Note

The loop can be omitted if the first thing you do is launching a manager. The run function is here for convenience. You can of course just run loop.run() instead

Warning

The manager should be stopped the last one to prevent any lock in your application.

add_process(name, cmd, **kwargs)[source]

add a process to the manager. all process should be added using this function

  • name: name of the process
  • cmd: program command, string)
  • args: the arguments for the command to run. Can be a list or a string. If args is a string, it’s splitted using shlex.split(). Defaults to None.
  • env: a mapping containing the environment variables the command will run with. Optional
  • uid: int or str, user id
  • gid: int or st, user group id,
  • cwd: working dir
  • detach: the process is launched but won’t be monitored and won’t exit when the manager is stopped.
  • shell: boolean, run the script in a shell. (UNIX only),
  • os_env: boolean, pass the os environment to the program
  • numprocesses: int the number of OS processes to launch for this description
  • flapping: a FlappingInfo instance or, if flapping detection should be used. flapping parameters are:
    • attempts: maximum number of attempts before we stop the process and set it to retry later
    • window: period in which we are testing the number of retry
    • retry_in: seconds, the time after we restart the process and try to spawn them
    • max_retry: maximum number of retry before we give up and stop the process.
  • redirect_output: list of io to redict (max 2) this is a list of custom labels to use for the redirection. Ex: [“a”, “b”] will redirect stdout & stderr and stdout events will be labeled “a”
  • redirect_input: Boolean (False is the default). Set it if you want to be able to write to stdin.
  • graceful_timeout: graceful time before we send a SIGKILL to the process (which definitely kill it). By default 30s. This is a time we let to a process to exit cleanly.
get_group(groupname)[source]

return list of named process of this group

get_groups()[source]

return the groups list

get_process(name_or_pid)[source]
get_process_id()[source]

generate a process id

get_process_info(name)[source]

get process info

get_process_state(name)[source]
get_process_stats(name_or_id)[source]

return process stats for a process template or a process id

get_process_status(name)[source]

return the process status:

{
  "active":  str,
  "running": int,
  "max_processes": int
}
  • active can be active or stopped
  • running: the number of actually running OS processes using this template.
  • max_processes: The maximum number of processes that should run. It is is normally the same than the runnin value.
manage_process(name)[source]
monitor(name_or_id, listener)[source]

get stats changes on a process template or id

on(evtype, listener)

subscribe to the manager event eventype

‘on’ is an alias to this function

once(evtype, listener)

subscribe to the manager event eventype

‘on’ is an alias to this function

processes_stats()[source]

iterator returning all processes stats

remove_group(groupname)[source]

remove a group and all its processes. All processes are stopped

remove_process(name)[source]

remove the process and its config from the manager

restart(callback=None)[source]

restart all processes in the manager. This function is threadsafe

restart_group(groupname)[source]

restart all processes in a group

restart_process(name)[source]

restart a process

run()[source]

Convenience function to use in place of loop.run() If the manager is not started it raises a RuntimeError.

Note: if you want to use separately the default loop for this thread then just use the start function and run the loop somewhere else.

running_processes()[source]

return running processes

send_signal(name_or_id, signum)[source]

send a signal to a process or all processes contained in a state

start(apps=[])[source]

start the manager.

start_group(groupname)[source]

start all process templates of the group

start_process(name)[source]
start_processes()[source]

start all processes

stop(callback=None)[source]

stop the manager. This function is threadsafe

stop_group(groupname)[source]

stop all processes templates of the group

stop_process(name_or_id)[source]

stop a process by name or id

If a name is given all processes associated to this name will be removed and the process is marked at stopped. If the internal process id is givien, only the process with this id will be stopped

stop_processes()[source]

stop all processes in the manager

subscribe(evtype, listener)[source]

subscribe to the manager event eventype

‘on’ is an alias to this function

subscribe_once(evtype, listener)[source]

subscribe once to the manager event eventype

‘once’ is an alias to this function

ttin(name, i=1)[source]

increase the number of system processes for a state. Change is handled once the event loop is idling

ttou(name, i=1)[source]

decrease the number of system processes for a state. Change is handled once the event loop is idling

unmonitor(name_or_id, listener)[source]

get stats changes on a process template or id

unsubscribe(evtype, listener)[source]

unsubscribe from the event eventype

update_process(name, cmd, **kwargs)[source]

update a process information.

When a process is updated, all current processes are stopped then the state is updated and new processes with new info are started

wakeup()[source]

Table Of Contents

Previous topic

Core gaffer framework

Next topic

process Module

This Page




PK/EADH}>>gaffer-0.4.4/webhooks.html Webhooks — gaffer documentation

Webhooks

Webhooks allow to register an url to a specific event (or alls) and the event will be posted on this URL. Each events can triger a post on a given url.

for example to listen all create events on http://echohttp.com/echo you can add this line in the webhooks sections of the gaffer setting file:

[webhooks]
create = http://echohttp.com/echo you

Or programatically:

from gaffer.manager import Manager
from gaffer.webhooks import WebHooks
hooks = [("create", "http://echohttp.com/echo you ")
webhooks = WebHooks(hooks=hooks)

manager = Manager()
manager.start(apps=[webhooks])

This gaffer application is started like other applications in the manager. All Gaffer events are supported.

The webhooks Module

class gaffer.webhooks.WebHooks(hooks=[])[source]

Bases: object

webhook app

active[source]
close()[source]
decref()[source]
incref()[source]
jobcount[source]
maybe_start_monitor()[source]
maybe_stop_monitor()[source]
refcount[source]
register_hook(event, url)[source]

associate an url to an event

restart()[source]
start(loop, manager)[source]

start the webhook app

stop()[source]

stop the webhook app, stop monitoring to events

unregister_hook(event, url)[source]

unregister an url for this event

Table Of Contents

Previous topic

Gaffer events

Next topic

procfile Module

This Page




PK/EA3c 66gaffer-0.4.4/http.html HTTP api — gaffer documentation

HTTP api

an http API provided by the gaffer.http_handler.HttpHandler` gaffer application can be used to control gaffer via HTTP. To embed it in your app just initialize your manager with it:

manager = Manager(apps=[HttpHandler()])

The HttpHandler can be configured to accept multiple endpoinds and can be extended with new HTTP handlers. Internally we are using Tornado so you can either extend it with rules using pure totrnado handlers or wsgi apps.

Request Format and Responses

Gaffer supports GET, POST, PUT, DELETE, OPTIONS HTTP verbs.

All messages (except some streams) are JSON encoded. All messages sent to gaffers should be json encoded.

Gaffer supports cross-origin resource sharing (aka CORS).

HTTP endpoints

Main http endpoints are described in the description of the gafferctl commands in Gafferctl:

Gafferctl is using extensively this HTTP api.

Output streams

The output streams can be fetched by doing:

GET /streams/<pid>/<nameofeed>

It accepts the following query parameters:

  • feed : continuous, longpoll, eventsource
  • heartbeat: true or seconds, send an empty line each sec (if true 60)

ex:

$ curl localhost:5000/streams/1/stderr?feed=continuous
STDERR 12
STDERR 13
STDERR 14
STDERR 15
STDERR 16
STDERR 17
STDERR 18
STDERR 19
STDERR 20
STDERR 21
STDERR 22
STDERR 23
STDERR 24
STDERR 25
STDERR 26
STDERR 27
STDERR 28
STDERR 29
STDERR 30
STDERR 31

$ curl localhost:5000/streams/1/stderr?feed=longpoll
STDERR 215

$ curl localhost:5000/streams/1/stderr?feed=eventsource
event: stderr
data: STDERR 20

event: stderr
data: STDERR 21

event: stderr
data: STDERR 22

$ curl localhost:5000/streams/1/stdout?feed=longpoll
STDOUTi 14

Write to STDIN

It is now possible to write to stdin via the HTTP api by sending:

POST to /streams/<pid>/ttin

Where <pid> is an internal process ide that you can retrieve by calling GET /processses/<name>/_pids

ex:

$ curl -XPOST -d $'ECHO\n' localhost:5000/streams/2/stdin
{"ok": true}

$ curl localhost:5000/streams/2/stdout?feed=longpoll
ECHO

Websocket stream for STDIN/STDOUT

It is now possible to get stin/stdout via a websocket. Writing to ws://HOST:PORT/wstreams/<pid> will send the data to stdin any information written on stdout will be then sent back to the websocket.

See the echo client/server example in the example folder:

$ python echo_client.py
Sent
Reeiving...
Received 'ECHO

'
(test)enlil:examples benoitc$ python echo_client.py
Sent
Reeiving...
Received 'ECHO

Note

unfortunately the echo_client script can only be launched with python 2.7 :/

Note

to redirect stderr to stdout just use the same name when you setting the redirect_output property on process creation.




PK/EArAAgaffer-0.4.4/gafferd.html Gafferd — gaffer documentation

Gafferd

Gafferd is a server able to launch and manage processes. It can be controlled via the HTTP api .

Usage

$ gafferd -h
usage: gafferd [-h] [-c CONFIG_FILE] [-p PLUGINS_DIR] [-v] [-vv] [--daemon]
               [--pidfile PIDFILE] [--bind BIND] [--certfile CERTFILE]
               [--keyfile KEYFILE] [--backlog BACKLOG]
               [config]

Run some watchers.

positional arguments:
  config                configuration file

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG_FILE, --config CONFIG_FILE
                        configuration file
  -p PLUGINS_DIR, --plugins-dir PLUGINS_DIR
                        default plugin dir
  -v                    verbose mode
  -vv                   like verbose mode but output stream too
  --daemon              Start gaffer in the background
  --pidfile PIDFILE
  --bind BIND           default HTTP binding
  --certfile CERTFILE   SSL certificate file for the default binding
  --keyfile KEYFILE     SSL key file for the default binding
  --backlog BACKLOG     default backlog

Config file example

[gaffer]
http_endpoints = public

[endpoint:public]
bind = 127.0.0.1:5000
;certfile=
;keyfile=

[webhooks]
;create = http://some/url
;proc.dummy.spawn = http://some/otherurl


[process:dummy]
cmd = ./dummy.py
;cwd = .
;uid =
;gid =
;detach = false
;shell = false
; flapping format: attempts=2, window=1., retry_in=7., max_retry=5
;flapping = 2, 1., 7., 5
numprocesses = 1
redirect_output = stdout, stderr
; redirect_input  = true
; graceful_timeout = 30

[process:echo]
cmd = ./echo.py
numprocesses = 1
redirect_output = stdout, stderr
redirect_input  = true

Plugins

Plugins are a way to enhance the basic gafferd functionality in a custom manner. Plugins allows you to load any gaffer application and site plugins. You can for example use the plugin system to add a simple UI to administrate gaffer using the HTTP interface.

A plugin has the following structure:

/pluginname
    _site/
    plugin/
        __init__.py
        ...
        ***.py

A plugin can be discovered by adding one ore more module that expose a class inheriting from gaffer.Plugin. Every plugin file should have a __all__ attribute containing the implemented plugin class. Ex:

from gaffer import Plugin

__all__ = ['DummyPlugin']

from .app import DummyApp


class DummyPlugin(Plugin):
    name = "dummy"
    version = "1.0"
    description = "test"

    def app(self, cfg):
        return DummyApp()

The dummy app here only print some info when started or stopped:

class DummyApp(object):

    def start(self, loop, manager):
        print("start dummy app")

    def stop(sef):
        print("stop dummy")

    def rester(self):
        print("restart dummy")

See the Overview for more infos. You can try it in the example folder:

$ cd examples
$ gafferd -c gaffer.ini -p plugins/

Install plugins

Installing plugins can be done by placing the plugin in the plugin folder. The plugin folder is either set in the setting file using the plugin_dir in the gaffer section or using the -p option of the command line.

The default plugin dir is set to ~/.gafferd/plugins .

Site plugins

Plugins can have “sites” in them, any plugin that exists under the plugins directory with a _site directory, its content will be statically served when hitting /_plugin/[plugin_name]/ url. Those can be added even after the process has started.

Installed plugins that do not contain any Python related content, will automatically be detected as site plugins, and their content will be moved under _site.

Mandatory Plugins

If you rely on some plugins, you can define mandatory plugins using the mandatory attribute of a the plugin class, for example, here is a sample config:

class DummyPlugin(Plugin):
    ...
    mandatory = ['somedep']

Table Of Contents

Previous topic

Unload a Procfile application to gafferd

Next topic

Gafferctl

This Page




PK0EAQررgaffer-0.4.4/genindex.html Index — gaffer documentation

Index

A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W

A

active (gaffer.httpclient.Process attribute)
(gaffer.httpclient.ProcessId attribute)
(gaffer.process.Process attribute)
(gaffer.process.ProcessWatcher attribute)
(gaffer.webhooks.WebHooks attribute)
add() (gaffer.httpclient.Process method)
add_callback() (gaffer.tornado_pyuv.IOLoop method)
add_handler() (gaffer.tornado_pyuv.IOLoop method)
add_process() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
add_timeout() (gaffer.tornado_pyuv.IOLoop method)
as_configparser() (gaffer.procfile.Procfile method)
as_dict() (gaffer.procfile.Procfile method)

B

BaseSigHandler (class in gaffer.sig_handler)
bytes2human() (in module gaffer.util)
bytestring() (in module gaffer.util)

C

check_gid() (in module gaffer.util)
check_uid() (in module gaffer.util)
close() (gaffer.events.EventEmitter method)
(gaffer.httpclient.HTTPClient method)
(gaffer.process.Process method)
(gaffer.tornado_pyuv.IOLoop method)
(gaffer.webhooks.WebHooks method)
closed (gaffer.process.Process attribute)
Color (class in gaffer.console_output)
ConsoleOutput (class in gaffer.console_output)
create() (gaffer.pidfile.Pidfile method)

D

daemonize() (in module gaffer.util)
decref() (gaffer.webhooks.WebHooks method)
DEFAULT_ACTIONS (gaffer.console_output.ConsoleOutput attribute)

E

encode() (in module gaffer.httpclient)
ERROR (gaffer.tornado_pyuv.IOLoop attribute)
EventEmitter (class in gaffer.events)
EventsourceClient (class in gaffer.httpclient)

F

fetch() (gaffer.httpclient.HTTPClient method)
from_nanotime() (in module gaffer.util)

G

gaffer.console_output (module)
gaffer.events (module)
gaffer.http_handler (module)
gaffer.httpclient (module)
gaffer.manager (module)
gaffer.pidfile (module)
gaffer.process (module)
gaffer.procfile (module)
gaffer.sig_handler (module)
gaffer.tornado_pyuv (module)
gaffer.util (module)
gaffer.webhooks (module)
GafferConflict
GafferNotFound
get_env() (gaffer.procfile.Procfile method)
get_group() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
get_groupname() (gaffer.procfile.Procfile method)
get_groups() (gaffer.manager.Manager method)
get_maxfd() (in module gaffer.util)
get_process() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
get_process_id() (gaffer.manager.Manager method)
get_process_info() (gaffer.manager.Manager method)
(in module gaffer.process)
get_process_state() (gaffer.manager.Manager method)
get_process_stats() (gaffer.manager.Manager method)
get_process_status() (gaffer.manager.Manager method)
get_watcher() (gaffer.httpclient.Server method)
getcwd() (in module gaffer.util)
groups() (gaffer.httpclient.Server method)

H

handle_callback_exception() (gaffer.tornado_pyuv.IOLoop method)
handle_quit() (gaffer.sig_handler.BaseSigHandler method)
(gaffer.sig_handler.SigHandler method)
handle_reload() (gaffer.sig_handler.BaseSigHandler method)
(gaffer.sig_handler.SigHandler method)
HTTPClient (class in gaffer.httpclient)
HttpEndpoint (class in gaffer.http_handler)
HttpHandler (class in gaffer.http_handler)

I

incref() (gaffer.webhooks.WebHooks method)
info (gaffer.process.Process attribute)
info() (gaffer.httpclient.Process method)
initialized() (gaffer.tornado_pyuv.IOLoop static method)
install() (gaffer.tornado_pyuv.IOLoop method)
(in module gaffer.tornado_pyuv)
instance() (gaffer.tornado_pyuv.IOLoop static method)
IOLoop (class in gaffer.tornado_pyuv)
is_ipv6() (in module gaffer.util)
is_process() (gaffer.httpclient.Server method)

J

jobcount (gaffer.webhooks.WebHooks attribute)
json_body() (gaffer.httpclient.Server method)

K

kill() (gaffer.process.Process method)

L

log_stack() (gaffer.tornado_pyuv.IOLoop method)

M

make_uri() (in module gaffer.httpclient)
manage_process() (gaffer.manager.Manager method)
Manager (class in gaffer.manager)
maybe_start_monitor() (gaffer.webhooks.WebHooks method)
maybe_stop_monitor() (gaffer.webhooks.WebHooks method)
monitor() (gaffer.manager.Manager method)
(gaffer.process.Process method)
monitor_io() (gaffer.process.Process method)

N

nanotime() (in module gaffer.util)
NONE (gaffer.tornado_pyuv.IOLoop attribute)
numprocesses (gaffer.httpclient.Process attribute)

O

on() (gaffer.manager.Manager method)
once() (gaffer.manager.Manager method)
output() (gaffer.console_output.Color method)

P

parse() (gaffer.procfile.Procfile method)
parse_address() (in module gaffer.util)
parse_cmd() (gaffer.procfile.Procfile method)
PeriodicCallback (class in gaffer.tornado_pyuv)
pid (gaffer.process.Process attribute)
Pidfile (class in gaffer.pidfile)
pids (gaffer.httpclient.Process attribute)
pipes_count (gaffer.process.RedirectIO attribute)
Process (class in gaffer.httpclient)
(class in gaffer.process)
processes() (gaffer.httpclient.Server method)
(gaffer.procfile.Procfile method)
processes_stats() (gaffer.manager.Manager method)
ProcessId (class in gaffer.httpclient)
ProcessWatcher (class in gaffer.process)
Procfile (class in gaffer.procfile)
publish() (gaffer.events.EventEmitter method)

Q

QUIT_SIGNALS (gaffer.sig_handler.BaseSigHandler attribute)

R

READ (gaffer.tornado_pyuv.IOLoop attribute)
RedirectIO (class in gaffer.process)
RedirectStdin (class in gaffer.process)
refcount (gaffer.webhooks.WebHooks attribute)
refresh() (gaffer.process.ProcessWatcher method)
register_hook() (gaffer.webhooks.WebHooks method)
remove_group() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
remove_handler() (gaffer.tornado_pyuv.IOLoop method)
remove_process() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
remove_timeout() (gaffer.tornado_pyuv.IOLoop method)
rename() (gaffer.pidfile.Pidfile method)
render() (gaffer.httpclient.EventsourceClient method)
(gaffer.httpclient.Watcher method)
request() (gaffer.httpclient.Server method)
restart() (gaffer.console_output.ConsoleOutput method)
(gaffer.http_handler.HttpEndpoint method)
(gaffer.http_handler.HttpHandler method)
(gaffer.httpclient.Process method)
(gaffer.manager.Manager method)
(gaffer.sig_handler.BaseSigHandler method)
(gaffer.webhooks.WebHooks method)
restart_group() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
restart_process() (gaffer.manager.Manager method)
run() (gaffer.httpclient.EventsourceClient method)
(gaffer.manager.Manager method)
running (gaffer.httpclient.Process attribute)
running() (gaffer.httpclient.Server method)
(gaffer.tornado_pyuv.IOLoop method)
running_processes() (gaffer.manager.Manager method)

S

save_process() (gaffer.httpclient.Server method)
send_signal() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
Server (class in gaffer.httpclient)
set_blocking_log_threshold() (gaffer.tornado_pyuv.IOLoop method)
set_blocking_signal_threshold() (gaffer.tornado_pyuv.IOLoop method)
setproctitle_() (in module gaffer.util)
SigHandler (class in gaffer.sig_handler)
signal() (gaffer.httpclient.Process method)
(gaffer.httpclient.ProcessId method)
spawn() (gaffer.process.Process method)
start() (gaffer.console_output.ConsoleOutput method)
(gaffer.http_handler.HttpEndpoint method)
(gaffer.http_handler.HttpHandler method)
(gaffer.httpclient.EventsourceClient method)
(gaffer.httpclient.Process method)
(gaffer.manager.Manager method)
(gaffer.process.RedirectIO method)
(gaffer.process.RedirectStdin method)
(gaffer.process.Stream method)
(gaffer.sig_handler.BaseSigHandler method)
(gaffer.sig_handler.SigHandler method)
(gaffer.tornado_pyuv.IOLoop method)
(gaffer.tornado_pyuv.PeriodicCallback method)
(gaffer.webhooks.WebHooks method)
start_group() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
start_process() (gaffer.manager.Manager method)
start_processes() (gaffer.manager.Manager method)
stats() (gaffer.httpclient.Process method)
status (gaffer.process.Process attribute)
status() (gaffer.httpclient.Process method)
stdio (gaffer.process.RedirectIO attribute)
stop() (gaffer.console_output.ConsoleOutput method)
(gaffer.http_handler.HttpEndpoint method)
(gaffer.http_handler.HttpHandler method)
(gaffer.httpclient.EventsourceClient method)
(gaffer.httpclient.Process method)
(gaffer.httpclient.ProcessId method)
(gaffer.manager.Manager method)
(gaffer.process.Process method)
(gaffer.process.ProcessWatcher method)
(gaffer.process.RedirectIO method)
(gaffer.process.RedirectStdin method)
(gaffer.sig_handler.BaseSigHandler method)
(gaffer.tornado_pyuv.IOLoop method)
(gaffer.tornado_pyuv.PeriodicCallback method)
(gaffer.webhooks.WebHooks method)
stop_group() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
stop_process() (gaffer.manager.Manager method)
stop_processes() (gaffer.manager.Manager method)
Stream (class in gaffer.process)
sub() (gaffer.httpclient.Process method)
subscribe() (gaffer.events.EventEmitter method)
(gaffer.httpclient.EventsourceClient method)
(gaffer.manager.Manager method)
(gaffer.process.ProcessWatcher method)
(gaffer.process.RedirectIO method)
(gaffer.process.Stream method)
subscribe_once() (gaffer.events.EventEmitter method)
(gaffer.httpclient.EventsourceClient method)
(gaffer.manager.Manager method)
(gaffer.process.ProcessWatcher method)
substitute_env() (in module gaffer.util)

T

ttin() (gaffer.manager.Manager method)
ttou() (gaffer.manager.Manager method)

U

unlink() (gaffer.pidfile.Pidfile method)
unmonitor() (gaffer.manager.Manager method)
(gaffer.process.Process method)
unmonitor_io() (gaffer.process.Process method)
unregister_hook() (gaffer.webhooks.WebHooks method)
unsubscribe() (gaffer.events.EventEmitter method)
(gaffer.httpclient.EventsourceClient method)
(gaffer.manager.Manager method)
(gaffer.process.ProcessWatcher method)
(gaffer.process.RedirectIO method)
(gaffer.process.Stream method)
unsubscribe_all() (gaffer.events.EventEmitter method)
unsubscribe_once() (gaffer.events.EventEmitter method)
update_handler() (gaffer.tornado_pyuv.IOLoop method)
update_process() (gaffer.httpclient.Server method)
(gaffer.manager.Manager method)
url_encode() (in module gaffer.httpclient)
url_quote() (in module gaffer.httpclient)

V

validate() (gaffer.pidfile.Pidfile method)
version (gaffer.httpclient.Server attribute)

W

wake() (gaffer.tornado_pyuv.Waker method)
Waker (class in gaffer.tornado_pyuv)
wakeup() (gaffer.manager.Manager method)
Watcher (class in gaffer.httpclient)
WebHooks (class in gaffer.webhooks)
WRITE (gaffer.tornado_pyuv.IOLoop attribute)
write() (gaffer.process.Process method)
(gaffer.process.RedirectStdin method)
writelines() (gaffer.process.Process method)
(gaffer.process.RedirectStdin method)

This Page




PK/EA44gaffer-0.4.4/sig_handler.html sig_handler Module — gaffer documentation

sig_handler Module

class gaffer.sig_handler.BaseSigHandler[source]

Bases: object

A simple gaffer application to handle signals

QUIT_SIGNALS = (3, 15, 2)
handle_quit(handle, *args)[source]
handle_reload(handle, *args)[source]
restart()[source]
start(loop)[source]
stop()[source]
class gaffer.sig_handler.SigHandler[source]

Bases: gaffer.sig_handler.BaseSigHandler

A simple gaffer application to handle signals

handle_quit(handle, *args)[source]
handle_reload(handle, *args)[source]
start(loop, manager)[source]

Previous topic

console_output Module

This Page




PK/EAW22gaffer-0.4.4/http_handler.html http_handler Module — gaffer documentation

http_handler Module

class gaffer.http_handler.HttpEndpoint(uri='127.0.0.1:5000', backlog=128, ssl_options=None)[source]

Bases: object

restart()[source]
start(loop, app)[source]
stop()[source]
class gaffer.http_handler.HttpHandler(endpoints=[], handlers=None, **settings)[source]

Bases: object

simple gaffer application that gives an HTTP API access to gaffer.

This application can listen on multiple endpoints (tcp or unix sockets) with different options. Each endpoint can also listen on different interfaces

restart()[source]
start(loop, manager)[source]
stop()[source]

Previous topic

Gaffer applications

Next topic

console_output Module

This Page




PK/EA;5''gaffer-0.4.4/command-line.html Command Line — gaffer documentation

Command Line

Gaffer is a process management framework but also a set of command lines tools allowing yout to manage on your machine or a cluster. All the command line tools are obviously using the framework.

gaffer`is an interface to the :doc:`gaffer HTTP api and inclusde support for loading/unloadin apps, scaling them up and down, ... . It can also be used as a manager for Procfile-based applications similar to foreman but using the gaffer framework. It is running your application directly using a Procfile or export it to a gafferd configuration file or simply to a JSON file that you could send to gafferd using the HTTP api.

Gafferd is a server able to launch and manage processes. It can be controlled via the HTTP api. It is controlled by gafferctl and can be used to handle many processes.

The tool Gafferctl allows you to control a local or remote gafferd node via the HTTP API. You can show processes informations, add new processes, changes their configureation, get changes on the nodes in rt ....

Previous topic

CHANGES

Next topic

Gaffer

This Page




PK0EA^ ^ gaffer-0.4.4/objects.inv# Sphinx inventory version 2 # Project: gaffer # Version: # The remainder of this file is compressed using zlib. xڭMw(_asv'y/d룞,Dj %[>@T(}Hl#(tNO'Z'Hkm#O4^"g5J;鶪yFfSpJBN_4_9ʡПl{na EM>@>m$a2ͷH I@ %ׄ$䂗LzS1(I6O2P2KO6ӢSA;ـQd9DnO>,+r8Aڜ?+w5s.Ӝʑw8TݡY$Y(9OKzO~ 9y=EeVӂb+.t[m$ m8m]*`*6tGN5W-y5hWC缾 3X#,^زxHeyKEv `(|WKZi=ǺL7pM~~ee)) "jVCmڊH.=I~LonBh1C/~M{ qD{i"8z$W@{AybƌJhʹbWm!Q~xPSK45Ņ9{akKÖu3(nӪYOWO6wYw1w`ԬB[n1 Z^}^KZrkMoՂoS7Z$"Cr:s`n1^)1ӥz8/C@Xvc%04i[Q6p1CKA˔zo_b Գɣe8Mwmn8FENŅ"݁֯n`[sʂ2ylC;IBHٱex&,ҶNc3kvvE}&5UƄ,qˍ?WMh+Oiܫ$3̨כ\Tg]b_#U =rCLV+N+%yǟ(a5A@Ј]txovş弡l@ 9AR¤7҆G4 _M%JI;Zw1J5iIhz:AQ„̨^XCbc@W$Sѕ(YٙuLK`+q>eՀty\N".`.<'x%"I? -Uqhc1֜u,&!->Ghtu 1H.V49 .S^ f3J0&ׂсT>&u1YEN'6yh' Duc?`މ sr:T7|O! 8%`Vg>?xu,;&IMϬ J lqԛǎ6Rd3TH| R\fcOŵoص '8î7߫7(BIV 'sV_mj.I߹^8{Ь`uC xDl̈hS۟ 29r2sVQfԵ@R*u(yC1u8NGxCv҈tRD9FbmŔl\"TySo%y,i8rF ^@梽YەΉ'q4I| M/g##KU>"}I"2N8'ҋvΣM/}V6tW$}\&=:1i%93T<[%fKS:lZxbh_ji6-䕚謮TŒGmcq2凕O+Ũ 99y}w^Nז+i/xv$uc>_DLJ AɩIr T(ݰOdEzZ.m#Ok|4S{!J77|5fUUb6Y XC #!E ]+DA$i<vuӃQ%+C#w#@IdTH CLr`gM%U等!\ %60K^bIO5x8zXKA1 !X;?IL$}ĠʀcQ"kں`e{A3Qt$C]Y\ҽ }%Dvb01{j7_S.b/Z%ol=Ct_a]cj{FUzjFzOI>D0%[B#Zt*;S%>1J'vML\>%5-ӂtA`Wo*Vxƣ1#߯]}J4({WǜMR(f=g5X\xoVC+kNcV5+`ɥ9#kՉ2be^1Qdy(/A̓֋Iyxg#%$*>deGEDg6esrI/8VM@,|mQ6vPhcX]Qg;e5{1R6I[3<> 'W K_W;%5f9HW.[q;ߟn|i+ Ý%o6_i5J!x;OLPK0EAht''gaffer-0.4.4/py-modindex.html Python Module Index — gaffer documentation

Python Module Index

g
 
g
gaffer
    gaffer.console_output
    gaffer.events
    gaffer.http_handler
    gaffer.httpclient
    gaffer.manager
    gaffer.pidfile
    gaffer.process
    gaffer.procfile
    gaffer.sig_handler
    gaffer.tornado_pyuv
    gaffer.util
    gaffer.webhooks

This Page




PK/EA8.88gaffer-0.4.4/index.html Welcome to gaffer’s documentation! — gaffer documentation

Welcome to gaffer’s documentation!

Gaffer

Application deployement, monitoring and supervision made simple.

Gaffer is a set of Python modules and tools to easily maintain and interact with your applications.

Features

  • Framework to manage and interact your processes

  • Fully evented. Use the libuv event loop using the pyuv library

  • Server and Command Line tools to manage your processes

  • Procfile applications support (see Gaffer)

  • HTTP Api (multiple binding, unix sockets & HTTPS supported)

  • Flapping: handle cases where your processes crash too much

  • Possibility to interact with STDIO:
    • websocket stream to write to stdin and receive from stdout (muliple clients can read and write at the same time)
    • subscribe on stdout/stderr feed via longpolling, continuous stream, eventsource or websockets
    • write your own client/server using the framework
  • Subscribe to process statistics per process or process templates and get them in quasi RT.

  • Easily extensible: add your own endpoint, create your client, embed gaffer in your application, ...

  • Compatible with python 2.6x, 2.7x, 3.x

Note

gaffer source code is hosted on Github

Indices and tables

Table Of Contents

Next topic

Getting started

This Page




PK/EAaӣ##gaffer-0.4.4/httpclient.html httpclient Module — gaffer documentation

httpclient Module

Gaffer provides you a simple Client to control a gaffer node via HTTP.

Example of usage:

import pyuv

from gaffer.httpclient import Server

# initialize a loop
loop = pyuv.Loop.default_loop()

s = Server("http://localhost:5000", loop=loop)

# add a process without starting it
process = s.add_process("dummy", "/some/path/to/dummy/script", start=False)

# start a process
process.start()

# increase the number of process by 2 (so 3 will run)
process.add(2)

# stop all processes
process.stop()

loop.run()
class gaffer.httpclient.EventsourceClient(loop, url, **kwargs)[source]

Bases: object

simple client to fetch Gaffer streams using the eventsource stream.

Example of usage:

loop = pyuv.Loop.default_loop()

def cb(event, data):
    print(data)

# create a client
url = http://localhost:5000/streams/1/stderr?feed=continuous'
client = EventSourceClient(loop, url)

# subscribe to the stderr event
client.subscribe("stderr", cb)

# start the client
client.start()
render(event, data)[source]
run()[source]
start()[source]
stop()[source]
subscribe(event, listener)[source]
subscribe_once(event, listener)[source]
unsubscribe(event, listener)[source]
exception gaffer.httpclient.GafferConflict[source]

Bases: exceptions.Exception

exption raised on HTTP 409

exception gaffer.httpclient.GafferNotFound[source]

Bases: exceptions.Exception

exception raised on HTTP 404

class gaffer.httpclient.HTTPClient(async_client_class=None, loop=None, **kwargs)[source]

Bases: object

A blocking HTTP client.

This interface is provided for convenience and testing; most applications that are running an IOLoop will want to use AsyncHTTPClient instead. Typical usage looks like this:

http_client = httpclient.HTTPClient()
try:
    response = http_client.fetch("http://www.friendpaste.com/")
    print response.body
except httpclient.HTTPError as e:
    print("Error: %s" % e)
close()[source]

Closes the HTTPClient, freeing any resources used.

fetch(request, **kwargs)[source]

Executes a request, returning an HTTPResponse.

The request may be either a string URL or an HTTPRequest object. If it is a string, we construct an HTTPRequest using any additional kwargs: HTTPRequest(request, **kwargs)

If an error occurs during the fetch, we raise an HTTPError.

class gaffer.httpclient.Process(server, process)[source]

Bases: object

Process object. Represent a remote process state

active[source]

return True if the process is active

add(num=1)[source]

increase the number of processes for this template

info()[source]

return the process info dict

numprocesses[source]

return the maximum number of processes that can be launched for this template

pids[source]

return a list of running pids

restart()[source]

restart the process

running[source]

return the number of processes running for this template

signal(num_or_str)[source]

send a signal to all processes of this template

start()[source]

start the process if not started, spawn new processes

stats()[source]
status()[source]

Return the status

{
    "active": true,
    "running": 1,
    "numprocesses": 1
}
stop()[source]

stop the process

sub(num=1)[source]

decrease the number of processes for this template

class gaffer.httpclient.ProcessId(server, pid, process)[source]

Bases: object

Process Id object. It represent a pid

active[source]

return True if the process is active

signal(num_or_str)[source]

Send a signal to the pid

stop()[source]

stop the process

class gaffer.httpclient.Server(uri, loop=None, **options)[source]

Bases: object

Server, main object to connect to a gaffer node. Most of the calls are blocking. (but running in the loop)

add_process(name, cmd, **kwargs)[source]

add a process. Use the same arguments as in save_process.

If a process with the same name is already registred a GafferConflict exception is raised.

get_group(name)[source]

return the list of all process templates of this group

get_process(name_or_id)[source]

get a process by name or id.

If id is given a ProcessId instance is returned in other cases a Process instance is returned.

get_watcher(heartbeat='true')[source]

return a watcher to listen on /watch

groups()[source]

return the list of all groups

is_process(name)[source]

is the process exists ?

json_body(resp)[source]
processes()[source]

get list of registered processes

remove_group(name)[source]

remove the group and all process templates of the group

remove_process(name)[source]

Stop a process and remove it from the managed processes

request(method, path, headers=None, body=None, **params)[source]
restart_group(name)[source]

restart all process templates of the group

running()[source]

get list of running processes by pid

save_process(name, cmd, **kwargs)[source]

save a process.

Args:

  • name: name of the process
  • cmd: program command, string)
  • args: the arguments for the command to run. Can be a list or a string. If args is a string, it’s splitted using shlex.split(). Defaults to None.
  • env: a mapping containing the environment variables the command will run with. Optional
  • uid: int or str, user id
  • gid: int or st, user group id,
  • cwd: working dir
  • detach: the process is launched but won’t be monitored and won’t exit when the manager is stopped.
  • shell: boolean, run the script in a shell. (UNIX only),
  • os_env: boolean, pass the os environment to the program
  • numprocesses: int the number of OS processes to launch for this description

If _force_update=True is passed, the existing process template will be overwritten.

send_signal(name_or_id, num_or_str)[source]

Send a signal to the pid or the process name

start_group(name)[source]

start all process templates of the group

stop_group(name)[source]

stop all process templates of the group

update_process(name, cmd, **kwargs)[source]

update a process.

version[source]

get gaffer version

class gaffer.httpclient.Watcher(loop, url, **kwargs)[source]

Bases: gaffer.httpclient.EventsourceClient

simple EventsourceClient wrapper that decode the JSON to a python object

render(event, data)[source]
gaffer.httpclient.encode(v, charset='utf8')[source]
gaffer.httpclient.make_uri(base, *args, **kwargs)[source]

Assemble a uri based on a base, any number of path segments, and query string parameters.

gaffer.httpclient.url_encode(obj, charset='utf8', encode_keys=False)[source]
gaffer.httpclient.url_quote(s, charset='utf-8', safe='/:')[source]

URL encode a single string with a given encoding.

Previous topic

tornado_pyuv Module

Next topic

Gaffer applications

This Page




PK0EA1vllgaffer-0.4.4/searchindex.jsSearch.setIndex({objects:{"gaffer.process.Process":{status:[16,3,1,""],spawn:[16,2,1,""],monitor_io:[16,2,1,""],unmonitor_io:[16,2,1,""],monitor:[16,2,1,""],pid:[16,3,1,""],stop:[16,2,1,""],writelines:[16,2,1,""],write:[16,2,1,""],active:[16,3,1,""],info:[16,3,1,""],kill:[16,2,1,""],closed:[16,3,1,""],unmonitor:[16,2,1,""],close:[16,2,1,""]},"gaffer.httpclient.EventsourceClient":{run:[45,2,1,""],render:[45,2,1,""],subscribe:[45,2,1,""],stop:[45,2,1,""],start:[45,2,1,""],subscribe_once:[45,2,1,""],unsubscribe:[45,2,1,""]},"gaffer.sig_handler.BaseSigHandler":{handle_reload:[0,2,1,""],QUIT_SIGNALS:[0,3,1,""],stop:[0,2,1,""],start:[0,2,1,""],handle_quit:[0,2,1,""],restart:[0,2,1,""]},"gaffer.manager":{Manager:[6,1,1,""]},"gaffer.tornado_pyuv":{IOLoop:[23,1,1,""],install:[23,5,1,""],PeriodicCallback:[23,1,1,""],Waker:[23,1,1,""]},"gaffer.sig_handler":{BaseSigHandler:[0,1,1,""],SigHandler:[0,1,1,""]},"gaffer.process.RedirectStdin":{write:[16,2,1,""],writelines:[16,2,1,""],stop:[16,2,1,""],start:[16,2,1,""]},"gaffer.httpclient.HTTPClient":{close:[45,2,1,""],fetch:[45,2,1,""]},gaffer:{sig_handler:[0,0,1,""],procfile:[21,0,1,""],http_handler:[40,0,1,""],tornado_pyuv:[23,0,1,""],process:[16,0,1,""],util:[30,0,1,""],manager:[6,0,1,""],webhooks:[36,0,1,""],console_output:[20,0,1,""],events:[18,0,1,""],pidfile:[19,0,1,""],httpclient:[45,0,1,""]},"gaffer.tornado_pyuv.PeriodicCallback":{start:[23,2,1,""],stop:[23,2,1,""]},"gaffer.http_handler":{HttpHandler:[40,1,1,""],HttpEndpoint:[40,1,1,""]},"gaffer.process.Stream":{start:[16,2,1,""],unsubscribe:[16,2,1,""],subscribe:[16,2,1,""]},"gaffer.httpclient.Server":{remove_process:[45,2,1,""],processes:[45,2,1,""],is_process:[45,2,1,""],remove_group:[45,2,1,""],get_process:[45,2,1,""],add_process:[45,2,1,""],json_body:[45,2,1,""],get_watcher:[45,2,1,""],request:[45,2,1,""],stop_group:[45,2,1,""],restart_group:[45,2,1,""],running:[45,2,1,""],version:[45,3,1,""],save_process:[45,2,1,""],groups:[45,2,1,""],get_group:[45,2,1,""],update_process:[45,2,1,""],send_signal:[45,2,1,""],start_group:[45,2,1,""]},"gaffer.events.EventEmitter":{unsubscribe_once:[18,2,1,""],unsubscribe_all:[18,2,1,""],publish:[18,2,1,""],subscribe:[18,2,1,""],subscribe_once:[18,2,1,""],unsubscribe:[18,2,1,""],close:[18,2,1,""]},"gaffer.httpclient.ProcessId":{active:[45,3,1,""],signal:[45,2,1,""],stop:[45,2,1,""]},"gaffer.process.ProcessWatcher":{stop:[16,2,1,""],refresh:[16,2,1,""],subscribe:[16,2,1,""],subscribe_once:[16,2,1,""],unsubscribe:[16,2,1,""],active:[16,3,1,""]},"gaffer.procfile.Procfile":{parse_cmd:[21,2,1,""],processes:[21,2,1,""],get_groupname:[21,2,1,""],as_dict:[21,2,1,""],parse:[21,2,1,""],get_env:[21,2,1,""],as_configparser:[21,2,1,""]},"gaffer.events":{EventEmitter:[18,1,1,""]},"gaffer.pidfile":{Pidfile:[19,1,1,""]},"gaffer.webhooks.WebHooks":{decref:[36,2,1,""],incref:[36,2,1,""],maybe_stop_monitor:[36,2,1,""],refcount:[36,3,1,""],stop:[36,2,1,""],start:[36,2,1,""],register_hook:[36,2,1,""],unregister_hook:[36,2,1,""],restart:[36,2,1,""],active:[36,3,1,""],close:[36,2,1,""],maybe_start_monitor:[36,2,1,""],jobcount:[36,3,1,""]},"gaffer.tornado_pyuv.IOLoop":{NONE:[23,3,1,""],initialized:[23,6,1,""],install:[23,2,1,""],instance:[23,6,1,""],READ:[23,3,1,""],update_handler:[23,2,1,""],ERROR:[23,3,1,""],stop:[23,2,1,""],set_blocking_log_threshold:[23,2,1,""],WRITE:[23,3,1,""],start:[23,2,1,""],running:[23,2,1,""],add_handler:[23,2,1,""],remove_timeout:[23,2,1,""],add_callback:[23,2,1,""],set_blocking_signal_threshold:[23,2,1,""],close:[23,2,1,""],log_stack:[23,2,1,""],handle_callback_exception:[23,2,1,""],add_timeout:[23,2,1,""],remove_handler:[23,2,1,""]},"gaffer.process":{ProcessWatcher:[16,1,1,""],get_process_info:[16,5,1,""],Stream:[16,1,1,""],Process:[16,1,1,""],RedirectStdin:[16,1,1,""],RedirectIO:[16,1,1,""]},"gaffer.process.RedirectIO":{start:[16,2,1,""],stdio:[16,3,1,""],stop:[16,2,1,""],subscribe:[16,2,1,""],unsubscribe:[16,2,1,""],pipes_count:[16,3,1,""]},"gaffer.console_output.ConsoleOutput":{DEFAULT_ACTIONS:[20,3,1,""],start:[20,2,1,""],stop:[20,2,1,""],restart:[20,2,1,""]},"gaffer.httpclient":{ProcessId:[45,1,1,""],GafferNotFound:[45,4,1,""],EventsourceClient:[45,1,1,""],make_uri:[45,5,1,""],GafferConflict:[45,4,1,""],Process:[45,1,1,""],url_quote:[45,5,1,""],Watcher:[45,1,1,""],encode:[45,5,1,""],url_encode:[45,5,1,""],Server:[45,1,1,""],HTTPClient:[45,1,1,""]},"gaffer.pidfile.Pidfile":{rename:[19,2,1,""],create:[19,2,1,""],unlink:[19,2,1,""],validate:[19,2,1,""]},"gaffer.webhooks":{WebHooks:[36,1,1,""]},"gaffer.sig_handler.SigHandler":{start:[0,2,1,""],handle_reload:[0,2,1,""],handle_quit:[0,2,1,""]},"gaffer.manager.Manager":{stop_process:[6,2,1,""],get_process_stats:[6,2,1,""],restart_process:[6,2,1,""],add_process:[6,2,1,""],processes_stats:[6,2,1,""],stop_group:[6,2,1,""],subscribe:[6,2,1,""],subscribe_once:[6,2,1,""],get_process_state:[6,2,1,""],ttin:[6,2,1,""],get_groups:[6,2,1,""],start_processes:[6,2,1,""],get_group:[6,2,1,""],get_process_id:[6,2,1,""],monitor:[6,2,1,""],start:[6,2,1,""],wakeup:[6,2,1,""],run:[6,2,1,""],restart_group:[6,2,1,""],manage_process:[6,2,1,""],stop:[6,2,1,""],stop_processes:[6,2,1,""],update_process:[6,2,1,""],unsubscribe:[6,2,1,""],running_processes:[6,2,1,""],start_group:[6,2,1,""],get_process_status:[6,2,1,""],restart:[6,2,1,""],get_process_info:[6,2,1,""],remove_process:[6,2,1,""],on:[6,2,1,""],remove_group:[6,2,1,""],get_process:[6,2,1,""],ttou:[6,2,1,""],unmonitor:[6,2,1,""],send_signal:[6,2,1,""],start_process:[6,2,1,""],once:[6,2,1,""]},"gaffer.console_output.Color":{output:[20,2,1,""]},"gaffer.httpclient.Watcher":{render:[45,2,1,""]},"gaffer.http_handler.HttpEndpoint":{start:[40,2,1,""],stop:[40,2,1,""],restart:[40,2,1,""]},"gaffer.util":{check_gid:[30,5,1,""],check_uid:[30,5,1,""],substitute_env:[30,5,1,""],parse_address:[30,5,1,""],getcwd:[30,5,1,""],bytes2human:[30,5,1,""],get_maxfd:[30,5,1,""],from_nanotime:[30,5,1,""],setproctitle_:[30,5,1,""],daemonize:[30,5,1,""],bytestring:[30,5,1,""],nanotime:[30,5,1,""],is_ipv6:[30,5,1,""]},"gaffer.http_handler.HttpHandler":{start:[40,2,1,""],stop:[40,2,1,""],restart:[40,2,1,""]},"gaffer.tornado_pyuv.Waker":{wake:[23,2,1,""]},"gaffer.httpclient.Process":{status:[45,2,1,""],info:[45,2,1,""],stats:[45,2,1,""],sub:[45,2,1,""],signal:[45,2,1,""],stop:[45,2,1,""],running:[45,3,1,""],start:[45,2,1,""],add:[45,2,1,""],numprocesses:[45,3,1,""],active:[45,3,1,""],pids:[45,3,1,""],restart:[45,2,1,""]},"gaffer.procfile":{Procfile:[21,1,1,""]},"gaffer.console_output":{Color:[20,1,1,""],ConsoleOutput:[20,1,1,""]}},terms:{alo:3,all:[11,22,29,2,3,27,6,39,32,33,35,36,45,18,20],code:[3,35,10],add_callback:23,queri:[2,22,45],global:[35,39],yellow:20,ctime:3,prefix:[39,18],eventsourc:[2,10,45,22,35],subclass:23,redirectio:16,follow:[11,22,2,3,28,41,39,18],children:3,content:[10,8,14,25,27,28],"_force_upd":45,send_sign:[6,45],depend:3,graceful_timeout:[6,39,28],evtyp:[3,6,18],sigsegv:26,send:[1,22,29,2,3,26,39,16,6,11,33,45],totrnado:22,quasi:10,program:[11,8,14,16,3,27,26,4,6,39,45],run_loop:23,those:28,under:28,sigfp:26,sent:[6,22,14],runnin:6,liter:[0,21,40,23,16,30,6,45,19,20],everi:28,string:[39,8,14,16,3,27,4,6,45,20],fals:[39,8,14,23,16,3,27,28,4,6,18,45],flappinginfo:[3,6],later:[3,6],gafferd:[38,10,21,11,8,13,14,25,3,27,28,39,29,42,1,5,35,37,9],util:[30,10,15],verb:22,failur:39,cyan:20,remove_process:[6,45],span:[0,21,40,23,16,30,6,45,19,20],http_handler:[10,22,40,41],administr:28,level:3,get_process:[6,45,4,33],cmd:[39,8,14,16,3,27,28,4,6,45],list:[21,11,8,13,14,16,3,27,4,6,39,33,43,45,18,5,20],eventemitt:[3,18],iter:[6,21],"try":[30,3,6,28,45],emul:26,all_fd:23,shlex:[8,14,16,3,27,4,6,45],stderr:[10,39,22,16,3,28,6,45],default_port:30,dir:[39,8,14,16,3,27,28,4,6,45],localhost:[22,45],priority0:2,prevent:6,bytes2human:30,cfg:28,redirectstdin:16,eas:20,second:[22,23,2,3,30,6],video:[10,39],pass:[11,8,14,23,2,3,27,4,16,6,39,32,33,44,45],port:[39,22],integr:[3,39],ioloop:[23,45],supervis:[10,39],procfil:[38,10,21,1,29,39,15,11,32,7,44,35,37],interract:[3,6,39,41],httpendpoint:40,section:[36,39,28],abl:[29,16,3,28,6,33],get_process_st:6,find:3,current:[38,23,16,3,30,6,34,37,5],delet:[2,22,18,17,19],name_or_pid:6,version:[35,11,28,33,45],"new":[39,22,29,2,27,42,6,35,18,9,45],method:[23,3,41,6,35,45],whatev:39,check_gid:30,hasn:39,full:[39,27,8],mem:3,gener:[6,39,26,21],genet:2,here:[39,3,28,6,33,18],bodi:45,let:6,pub:3,path:[39,14,2,30,19,45],becom:[2,16,39],sinc:39,valu:[39,30,6,11,32,44],wait:39,gafferconflict:45,search:10,sender:16,overwritten:45,async_client_class:45,ourself:18,aliv:[3,6,39],sigxcpu:26,action:[3,6,26,23,20],http_client:45,implement:[3,28],start_process:[35,6],os_pid:2,environn:[21,39],overrid:23,via:[10,1,22,29,16,3,28,6,45],sourc:[0,21,40,23,10,16,30,6,36,45,18,19,20],app:[22,40,29,3,28,6,36],keyfil:[28,33],put:22,unix:[10,39,8,40,14,16,3,27,4,6,33,45],"boolean":[8,14,16,3,27,4,6,45],encode_kei:45,instal:[28,23],decrement:[42,33],"_start":31,emitt:18,is_ipv6:30,all_handl:23,from:[10,21,39,26,14,2,3,28,4,30,6,32,33,7,35,36,45,18,17,20],sigbu:26,mem_info2:3,mem_info1:3,regist:[16,36,45],handler:[35,22,40,23],call:[22,23,3,26,41,6,45],msg:3,simpler:35,type:[11,8,14,25,27,32,44],tell:39,process_info:16,more:[11,3,26,28,39,32,44],registr:45,is_process:45,flap:[3,6,39,28,10],relat:28,enhanc:28,warn:6,accept:[2,8,22,14],actual:6,"_sub":42,os_env:[39,8,14,3,27,4,6,35,45],"_plugin":28,none:[21,8,14,23,16,3,27,4,30,6,40,45,20],endpoint:[38,10,11,22,40,3,28,33,7,35,37],retriev:[16,22,5],friendpast:45,name_of_color:20,cmdline:3,alia:6,setup:39,work:[39,8,14,16,3,27,4,6,45],cat:[39,14],descriptor:26,endpoind:22,obvious:29,can:[1,2,3,4,6,7,8,5,10,11,14,16,20,21,22,27,28,29,41,33,35,36,37,38,39,40,45],pluginnam:28,root:[44,11,32],fetch:[3,45,22,4,33],def:[3,45,28,41],control:[39,22,29,26,3,28,6,45],siginfo:26,scal:7,stream:[10,22,16,3,28,35,45,20],give:[3,6,40],process:[1,2,3,4,5,6,7,8,9,10,11,12,14,16,17,18,20,21,22,13,24,25,26,27,28,30,31,32,33,34,35,37,38,39,29,15,42,43,44,45],lock:6,emist:18,share:22,templat:[10,39,2,3,4,6,33,43,45,18,20],abort:26,smth:[3,6],parttern:2,unsubscribe_al:18,magenta:20,occur:45,add_timeout:23,stin:22,alwai:3,gaffer:[0,1,2,3,6,7,5,10,11,29,16,18,19,20,21,22,23,28,30,41,32,33,35,36,37,38,39,40,15,44,45],multipl:[10,22,40,16,3,35],myapplic:[3,41],sigtstp:26,ping:35,write:[10,22,23,16,3,26,6],how:[3,39],env:[21,11,8,14,16,3,27,4,30,6,39,32,44,45],instead:[6,45,20],config:[6,39,28],updat:[2,27,6,33,18,45],get_groupnam:21,map:[8,14,16,3,27,4,6,45],mystderr:39,resourc:[22,45],restart_group:[6,45],manage_process:6,after:[39,3,26,28,6,35],usabl:21,befor:[3,6,27,39],exit_cod:18,exc_info:23,mai:[23,45],start_group:[6,45],as_configpars:21,sigttin:26,man:26,github:10,attempt:[26,3,6,39,28],bind:[3,28,10],stdio:[16,10],stdin:[10,39,22,14,16,3,6],retry_in:[3,6,39,28],inform:[11,22,29,14,16,3,26,6,39,5,20],maintain:[3,6,10],environ:[21,39,8,14,16,3,27,4,6,44,35,45],allow:[38,39,29,2,3,28,16,6,35,36,37,5],indic:10,callabl:[16,18],max_retri:[3,6,39,28],order:[3,39,35],oper:[39,7],charset:45,config_fil:28,help:[3,11,28,33],ouput:20,somedata:3,over:21,move:28,soon:3,report:23,through:16,subscriber3:18,keyboard:26,still:[35,39],dynam:[8,13,14,25,3,27,42,31,12,9],paramet:[39,22,2,3,6,32,45],group:[38,39,8,14,16,3,27,4,30,6,7,35,37,5,45],monitor:[10,8,13,14,42,16,3,27,4,25,6,17,35,36,45,9,33],fix:[35,39],instantli:7,window:[26,3,6,39,28],html:35,processes_stat:6,main:[16,21,22,45],easier:18,them:[10,11,29,3,27,28,6,39,35],"return":[16,4,6,17,12,13,14,9,20,21,8,23,24,25,27,28,30,31,33,34,42,43,45],pure:22,thei:16,handl:[0,39,29,10,3,6,35],encod:[22,33,45],safe:45,as_dict:21,initi:[22,23,3,6,35,45],framework:[10,11,29,15,39,35],quit_sign:0,update_process:[6,27,33,45],interrupt:26,aka:22,del_process:[33,17],detach:[39,8,14,16,3,27,28,4,6,18,45],introduct:39,down:[35,11,39,29,7],eventu:3,longpol:[2,10,22],name:[2,3,4,5,6,8,17,12,14,16,9,18,19,31,21,22,13,24,25,28,27,26,32,34,37,38,39,42,43,45],benoitc:22,simpl:[0,39,40,10,28,6,45],refresh:[16,3],separ:6,easili:[3,35,10],echohttp:36,ttou:6,trap:26,timeout:[39,23],each:[11,22,40,2,39,32,44,36,20],fulli:10,output_stream:20,intense_magenta:20,pipes_count:16,replac:3,"_stop":12,continu:[10,39,22,2,3,26,45],"static":[28,23],connect:[38,11,33,7,37,45],pyuv:[16,3,6,45,10],happen:2,sigalrm:26,event:[10,22,23,41,2,3,15,16,6,35,36,18,45],special:39,variabl:[39,8,14,16,3,27,4,6,35,45],givien:6,periodiccallback:23,god:3,stdouti:22,publish:[3,18],profil:26,rel:39,reader:26,print:[45,28,20],wich:3,cwd:[39,8,14,16,3,27,28,4,6,45],red:20,console_output:[10,41,35,20],prioriti:[35,39],insid:3,workflow:39,runtimeerror:6,manipul:35,getcwd:30,given:[30,6,45,21,36],free:45,standard:30,reason:43,base:[0,21,11,40,23,16,3,29,6,39,36,45,18,19,20],releas:35,eventyp:6,"byte":30,environmennt:39,sigxfsz:26,sigkil:[6,39,26],val:[30,18],thread:6,launch:[11,22,29,14,24,16,3,27,28,4,6,39,33,35,8,43,18,45],where:[3,39,22,18,10],could:[39,1,11,29],omit:[3,6],timer:[3,26],webkitallowfullscreen:[10,39],thing:6,place:[6,28],unknown:30,heartbeat:[2,35,22,45],interact:[3,10],first:[30,6,39],origin:22,softwar:26,singleton:23,certfil:[28,33],notifi:18,directli:[11,29,3,39,32,20],onc:[6,18,7],susbscrib:18,powerful:3,number:[11,8,14,24,3,27,26,4,42,6,39,32,33,34,44,9,45],echo:[28,36,22],hook:36,instruct:26,alreadi:[27,8,14,45],done:[11,3,28,6,39,7],wrapper:[45,20],size:26,exit_statu:2,differ:[3,40,20],unlink:19,sigquit:26,substitute_env:30,script:[22,14,16,3,27,4,6,44,8,45],associ:[6,36],caught:26,get_process_id:6,system:[39,28,26,43,6,35],messag:[26,11,12,13,14,24,2,28,27,8,4,25,33,42,17,34,22,43,18,9,31],sigpip:26,too:[10,28],illeg:26,termin:26,conveni:[6,45],somecallback:3,store:16,listen:[40,16,3,41,6,36,18,45],shell:[39,8,14,16,3,27,28,4,6,45],consol:20,option:[2,3,4,6,7,8,17,11,12,14,16,9,31,22,13,24,25,28,27,26,32,34,35,37,38,39,40,42,43,44,45],tool:[10,11,29,3,39,35],specifi:[38,11,32,44,37,5],broadcast:3,part:[2,39],pars:21,signum:[16,6,26],grace:[35,6,39],get_env:21,serv:28,http_endpoint:28,whenev:23,provid:[22,41,6,33,19,45],remov:[2,3,4,6,18,17,45],structur:[3,28,41],project:[38,37,5],custom_stream:16,listenerr:18,posix:26,sigemt:26,str:[8,14,16,3,27,4,6,45],unloadin:29,design:[3,6],locla:2,pre:[0,21,40,23,16,30,6,45,19,20],lowest:3,unmonitor_io:[16,3],waker:23,sometim:39,ani:[39,22,2,26,28,16,6,33,45,20],fail:35,expir:26,have:[39,14,3,28,35,18,20],tabl:10,need:[3,39,19,20],processnam:39,callback:[16,3,6,18,23],self:[3,28,41],violat:26,client:[10,45,22,33,35],note:[10,39,22,2,3,6,18,20],also:[3,11,40,39,29],without:[3,45],build:[3,21],fname:19,green:20,color_nam:20,stop_pid:[2,35,18,20],uppercas:39,even:[3,28],sure:30,writelin:[16,3],trace:26,deploy:10,usernam:3,object:[0,21,40,23,16,3,28,41,6,36,45,18,19,20],intense_cyan:20,default_act:20,discov:28,incref:36,react:3,unmonitor:[16,3,6],render:45,colorama:20,plugins_dir:28,segment:[26,45],nameofprocestempl:3,sub:[2,3,45,33,42],running_process:6,applicaton:3,demand:23,don:[3,43,18,20],eventsourcecli:45,url:[38,11,28,7,35,36,37,45],doc:[33,29],clear:18,tpl:2,request:[26,22,45],uri:[38,37,40,45],pipe:[16,26,33],dummi:[39,2,28,11,7,45],pidfil:[10,28,19,15],declar:3,wildcard:18,get_group:[6,45],width:[10,39],httphandler:[3,6,22,40],wsgi:22,runit:3,show:[11,28,29],api:[10,1,22,40,29,3,28,39,11,33,7,35],exption:45,dummy_bas:[39,11],verbos:[35,28],url_encod:45,callback_tim:23,concurr:[44,11,32],save_process:45,handle_quit:0,ttin:[6,22],get_process_stat:6,data:[2,16,22,45],maybe_start_monitor:36,mulipl:10,redirect:[16,3,6,22,20],access:[3,40],onli:[39,22,14,16,3,27,28,4,6,8,45],explicitli:23,just:[3,6,22,18,35],pretti:[3,6],"true":[26,45,39,12,14,23,24,2,28,27,8,25,13,43,42,17,34,22,20,9,31],configur:[21,1,22,29,3,28,39,11],activ:[39,24,16,25,6,34,35,36,45],state:[6,45],than:[6,39],controll:[3,6],io_label:16,dict:[3,21,45],folder:[38,39,22,28,37,5],local:[39,29],consoleoutput:20,sighup:26,count:3,hit:28,get:[10,11,22,29,13,24,2,4,25,6,39,33,34,43,17,45],intense_r:20,watch:[39,2,3,33,35,45],repr:30,ssl:28,register_hook:36,cannot:26,redict:[16,3,6],nameofev:18,increas:[3,6,39,9,45],name_or_id:[6,26,45],him:3,restart:[0,39,40,2,3,27,28,41,6,36,45,18,20],temp:19,emb:[3,22,10],unregister_hook:36,"public":28,wstream:22,cleanli:6,feed:[2,10,22,45],evennt:18,common:18,contain:[11,8,14,16,3,27,28,4,6,32,44,45],sigvtalrm:26,certif:28,set:[10,21,1,22,40,29,16,3,27,28,6,39,35,36,20],get_process_statu:6,add_process:[3,6,8,33,45],frame:23,subscriber2:18,startup:[6,39],asynchttpcli:45,maximum:[3,6,45],sef:28,see:[10,39,22,26,3,28],num:[44,11,32,45],mandatori:28,arg:[0,11,8,14,16,3,27,4,6,39,33,18,5,45],default_loop:[3,6,45],close:[16,36,18,23,45],io_loop:23,dummy1:[39,11,7],dumi:39,statu:[39,12,14,24,16,27,26,4,33,25,6,42,17,34,8,43,45,9,31],detect:[3,6,28],"_loop":23,extend:[3,6,22,35],"__all__":28,pattern:[2,18],sigint:26,cours:[3,6],label:[16,3,6,39],written:22,won:[8,14,16,3,27,4,6,45],groupnam:6,"import":[36,28,45],bytestr:30,httperror:45,attribut:[16,28],altern:[44,11,32],signatur:3,kei:[39,28],sigil:26,sigwinch:26,sigchld:26,extens:[10,22],sigio:26,otherwis:19,triger:36,come:3,stale:19,addit:[16,32,45],both:[39,7],gaffernotfound:45,cor:22,last:[16,6],easi:3,plugin:[35,39,28],admin:39,processwatch:16,fcntl:26,parse_address:30,configpars:21,tutori:39,numprocess:[39,8,14,24,25,3,27,28,4,6,33,34,45],mani:[3,39,18,29],sigterm:[39,26],com:[10,36,39,45],load:[21,11,29,14,3,28,39,32,33,44,35,37],get_process_info:[16,6],intense_blu:20,simpli:[3,11,39,29,23],check_uid:30,point:26,color:[2,20],overview:[3,39,28,10],period:[3,6],dispatch:35,height:[10,39],"_add":9,header:45,param:45,shutdown:35,concurrency_set:21,websocket:[10,22],speak:3,quit:26,xpost:22,url_quot:45,been:[8,23,14,2,27,18],enlil:22,compon:6,json:[21,1,8,29,14,25,39,27,11,33,22,45],trigger:[35,18],unsubscrib:[16,6,18,45],subscrib:[10,16,3,6,45,18,20],immedi:[8,14],valueerror:30,want:[39,23,16,3,6,45],maybe_stop_monitor:36,ssl_option:40,pid:[22,13,2,3,16,43,33,18,19,45],imag:26,nanotim:30,convert:30,argument:[38,11,8,14,16,3,27,28,4,6,39,35,37,45],monitor_io:[16,3],sighandl:0,child:26,repres:45,sigttou:26,present:26,statist:[3,10],"case":[10,39,8,14,3,27,45],from_nanotim:30,save:45,look:[39,45],basesighandl:0,vimeo:[10,39],properti:[22,14,24,25,3,27,8,4,42,43,17,34,12,9,31],plugun:39,cluster:29,defin:[26,28,32,44],invok:26,error:[8,23,14,27,26,45],exist:[30,26,28,45],loop:[0,40,23,10,16,3,28,41,6,36,45,18,20],manner:28,spawn:[2,3,28,16,6,35,45,18,20],stdour:3,increment:[33,9],stdout:[10,39,22,16,3,28,6],demo:39,non:26,player:[10,39],cant:39,kwarg:[6,18,45],num_or_str:45,crash:10,"__init__":[3,28,41],around:[3,20],httprequest:45,addr:30,all_ev:16,welcom:10,mystdout:39,perform:3,make:[30,3,18,19,35],nanosecond:30,cross:22,same:[10,39,22,14,16,3,6,44,45],set_blocking_log_threshold:23,member:39,python:[10,11,22,3,28,39,35,45],instanc:[23,16,3,6,32,45],decod:45,split:[8,14,16,3,27,4,6,45],wakeup:6,document:[3,39,10],conflict:[27,8,14],complet:39,ifram:[10,39],http:[2,3,4,6,8,17,10,11,12,14,9,31,22,13,24,25,28,27,26,29,33,34,35,36,37,38,39,40,42,43,45],remove_timeout:23,tornado_pyuv:[10,23,15],utf8:45,blue:20,posit:28,remot:[2,35,39,29,45],rais:[30,6,45],user:[39,8,14,16,3,27,26,4,30,6,45],improv:35,handle_callback_except:23,respons:[26,22,13,14,24,25,3,27,8,4,42,6,17,34,12,43,45,9,31],typic:45,log_stack:23,setproctitle_:30,off:[44,11],remove_group:[6,45],colour:20,itself:[39,23],custom_channel:16,inherit:[35,28],joyent:3,exampl:[11,22,14,3,28,6,39,33,36,18,45],command:[1,2,3,4,5,6,7,8,9,10,11,12,14,16,17,31,21,22,13,24,25,26,27,28,32,33,34,35,37,38,39,29,42,43,44,45],thi:[1,2,3,4,6,8,17,11,12,13,14,16,9,18,20,22,23,24,25,26,27,28,31,32,33,34,36,37,38,39,40,41,42,43,44,45],reeiv:22,sigprof:26,everyth:[12,14,9,24,25,27,26,4,42,43,31,34,8,17],stdoutt:[16,3],libuv:[3,10],propos:3,otherurl:28,sec:[2,22],construct:45,sigusr2:26,sigurg:26,execut:[39,8,14,27,26,4,45],mysdtout:39,resp:45,tcp:[3,33,40],inclusd:29,sigtrap:26,tooll:39,kill:[39,16,26,6,33,35,20],refactor:35,processs:22,yet:39,subscribe_onc:[16,6,18,45],previous:16,remove_handl:23,oldpid:19,gather:14,now:[11,22,3,39,7,35],expos:[43,39,28],mark:6,"_signal":26,except:[26,22,23,45],load_process:[39,33,14],framebord:[10,39],add:[10,39,8,13,14,2,3,28,29,25,6,33,35,36,9,45],valid:19,redirect_input:[16,3,6,39,28],els:6,supervisor:[3,39],intense_green:20,modul:[0,21,40,23,10,16,3,28,15,30,41,6,33,36,45,18,19,20],match:18,real:26,applic:[0,16,3,6,7,8,10,11,14,20,22,25,27,28,29,41,32,35,36,37,38,39,40,44,45],which:[3,6,39,35],format:[1,22,14,28,11,32,44,18],read:[10,39,26,33,23],sigsi:26,sigcont:26,know:20,background:[26,28],somewher:6,httprespons:45,reap:[2,35,18,20],mkstemp:19,make_uri:45,daemon:[30,39,28],intense_yellow:20,like:[14,3,28,6,35,36,45],specif:[36,39,19],threadsaf:[3,6],should:[11,22,24,16,3,28,6,39,32,33,44],signal:[0,39,23,16,26,6,33,45],anyth:3,integ:[30,39],server:[10,22,29,3,28,45],add_handl:23,collect:3,unsubscribe_onc:18,necessari:23,singl:45,docutil:[0,21,40,23,16,30,6,45,19,20],output:[39,22,2,3,28,35,20],mode:[39,28],page:[10,39],backlog:[28,40],exceed:26,titl:30,old:27,environmenet:39,foreman:[35,11,29,39],interv:[16,3],compat:10,some:[39,22,28,43,44,35,5,45],back:22,stop_group:[6,45],urgent:26,intern:[22,13,3,43,6,18],"export":[39,1,11,29],mettod:3,librari:10,autorestart:35,scale:[35,11,39,29,7],channel:[16,35],normal:[6,23],definit:[6,39],per:10,"_pid":[43,22],webhook:[10,39,28,15,35,36],hangup:26,substitut:35,retri:[3,6],exit:[11,8,14,2,3,27,28,4,16,6,35,45,18,20],happend:18,creattion:2,condit:26,proc:[2,28,18],setrlimit:26,either:[1,22,28,39,33,45],machin:29,core:[10,6,26,15],max_process:6,run:[11,8,14,23,24,16,3,27,28,4,29,34,13,6,39,32,25,7,44,45,33],get_maxfd:30,on_exit_cb:16,"_site":28,usag:[39,14,28,11,33,18,45],host:[10,22],gafferctl:[2,4,8,17,10,12,14,9,31,21,22,13,24,25,27,26,33,34,35,39,29,42,43],post:[8,14,26,27,22,42,31,35,12,36,9],sigabrt:26,"throw":23,describ:[3,39,22,14],src:[10,39],about:[16,39],obj:45,socket:[3,26,40,10],most:45,plugin_nam:28,separr:2,unfortun:22,idl:[6,18],allowfullscreen:[10,39],sadli:39,discard:26,mean:39,block:[18,45],processid:45,parse_cmd:21,stop_process:[35,6],own:[3,33,10],sigusr1:26,"float":26,inti:[3,41],www:45,automat:28,unregist:36,stop:[0,2,3,4,6,17,12,14,16,18,20,8,23,28,27,26,41,33,35,36,39,40,42,45],empti:[2,22],formerli:26,get_watch:45,wrap:16,chang:[10,39,29,2,3,26,6,33,35],restart_process:6,yout:29,your:[38,10,11,22,29,3,6,39,32,33,7,44,35,37,5],manag:[0,2,3,4,6,8,10,11,14,16,18,19,20,21,22,27,28,29,41,35,36,39,40,15,45],redirect_output:[39,22,16,3,28,6],nameofe:22,log:23,wai:[3,39,28,18],mozallowfullscreen:[10,39],support:[10,11,22,29,39,7,35,36],custom:[39,23,16,3,28,6],avail:[16,3,23],start:[0,2,3,6,9,10,11,12,14,16,31,18,20,8,23,24,27,28,41,32,33,34,35,36,39,40,43,45],reli:28,gid:[39,8,14,16,3,27,28,4,30,6,45],interfac:[11,40,29,3,28,39,18,45],includ:[39,21,1,11],dummyapp:28,httpclient:[10,33,45],"function":[21,16,28,6,35,18],creation:[22,20],human:30,offer:18,basic:[3,28],refcount:36,term_sign:[2,18],sigstop:26,pwd:30,sigiot:26,translat:30,wnt:39,renam:19,line:[1,2,3,4,5,7,8,9,10,11,12,14,17,20,21,22,13,24,25,26,27,28,31,32,34,35,36,37,38,39,29,42,43,44],highest:39,json_bodi:45,info:[16,6,28,45],made:10,utf:45,setitim:26,possibl:[10,39,22,3,26,35],"default":[26,38,11,8,14,23,2,3,27,28,4,16,6,39,32,44,35,37,5,45],displai:20,deadlin:23,until:3,asynchron:[16,3,18,35],below:[39,33],plugin_dir:28,limit:26,site:[35,28],sampl:28,similar:[3,11,29,39],emit:[3,18],impl:23,recored:[38,37,5],featur:[10,35],somedep:28,uid:[39,8,14,16,3,27,28,4,30,6,45],creat:[26,10,39,8,23,14,42,2,3,27,28,25,16,33,19,36,45,13,18,9,20],"int":[39,8,14,2,3,27,4,16,6,18,45],dure:45,directorti:39,filenam:1,dummyplugin:28,watcher:[16,28,8,14,45],alarm:26,decreas:[3,6,39,45,42],ini:[3,1,28,39],file:[21,1,29,14,26,3,28,39,11,32,33,44,35,36,19],curl:[2,22],subcrib:18,check:39,inc:[42,39,9],assembl:45,somenam:[39,27,8,4,14],namedarg:18,sig_handl:[0,41,10],templatenam:18,index:10,when:[39,22,14,16,3,27,28,4,41,6,35,8,45,20],detail:4,virtual:26,orient:39,tornado:[3,22],other:[3,36,39,45],transport:3,test:[39,22,14,3,28,6,45],nicc:3,you:[38,1,22,29,23,2,3,28,39,16,6,11,33,36,37,5,45],node:[38,11,29,2,39,33,7,35,37,45],programat:36,stat:[16,3,6,45],rester:28,decref:36,methhod:[3,41],"class":[0,21,40,23,15,16,3,28,30,41,6,36,45,18,19,20],much:10,echo_cli:22,max_siz:18,update_handl:23,max:[16,3,6],receiv:[16,3,22,10],unload:[38,35,11,39],directori:[32,11,28,44],handle_reload:0,descript:[26,22,14,2,3,27,28,4,42,6,17,34,8,45,9,33],nameorid:3,rule:[39,22],netloc:30,ignor:26,time:[10,39,3,26,30,6,35],set_blocking_signal_threshold:23,wake:23,cpu:[3,26],jobcount:36},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:exception","5":"py:function","6":"py:staticmethod"},titles:["sig_handler Module","Export a Procfile","Watch changes in gaffer","Overview","Fetch a process template","List your process informations","manager Module","Scaling your process","Add a process to monitor","Increment the number of OS processes","Welcome to gaffer’s documentation!","Gaffer","Stop a process","Add a process to monitor","Load a process from a file","Core gaffer framework","process Module","Get a process description","Gaffer events","pidfile Module","console_output Module","procfile Module","HTTP api","tornado_pyuv Module","Number of processes that should be launched","Return the status of a process","Send a signal to a process","Update a process description","Gafferd","Command Line","util Module","Start a process","Start a process","Gafferctl","Number of running processes for this process description","CHANGES","Webhooks","Load a Procfile application to gafferd","Unload a Procfile application to gafferd","Getting started","http_handler Module","Gaffer applications","Decrement the number of OS processes","Get launched process ids for a process template","Run one-off command","httpclient Module"],objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","exception","Python exception"],"5":["py","function","Python function"],"6":["py","staticmethod","Python static method"]},filenames:["sig_handler","pm/export","commands/watch","overview","commands/get_process","pm/ps","manager","pm/scale","commands/add_process","commands/add","index","gaffer","commands/stop","commands/processes","commands/load_process","processframework","process","commands/del_process","events","pidfile","console_output","procfile","http","tornado_pyuv","commands/numprocesses","commands/status","commands/kill","commands/update_process","gafferd","command-line","util","commands/start","pm/start","gafferctl","commands/running","news","webhooks","pm/load","pm/unload","getting-started","http_handler","applications","commands/sub","commands/pids","pm/run","httpclient"]})PK/EAg(Ya2a2gaffer-0.4.4/procfile.html procfile Module — gaffer documentation

procfile Module

module to parse and manage a Procfile

class gaffer.procfile.Procfile(procfile, envs=None)[source]

Bases: object

Procfile object to parse a procfile and a list of given environnment files.

as_configparser(concurrency_settings=None)[source]

return a ConfigParser object. It can be used to generate a gafferd setting file or a configuration file that can be included.

as_dict(name, concurrency_settings=None)[source]

return a procfile line as a JSON object usable with the command gafferctl load .

get_env(envs=[])[source]

build the procfile environment from a list of procfiles

get_groupname()[source]
parse(procfile)[source]

main function to parse a procfile. It returns a dict

parse_cmd(v)[source]
processes()[source]

iterator over the configuration

Previous topic

Webhooks

Next topic

pidfile Module

This Page




PK/EAȟTT!gaffer-0.4.4/getting-started.html Getting started — gaffer documentation

Getting started

This tutorial exposes the usage of gaffer as a tool. For a general overview or how to integrate it in your application you should read the overview page.

Introduction

Gaffer allows you to launch OS processes and supervise them. 3 command line tools allows you to use it for now:

  • Gafferd is the process supervisor and should be launched first before to use other tools.
  • Gaffer is a Procfile application manager and allows you to load your Procfile applications in gafferd and watch their status.
  • Gafferctl is a more generic tooll than gaffer and is more admin oriented. It allows you to setup any process templates and manage your processes. You can also use it to watch the activity in gafferd (process activity or general activity)

A process template is the way you describe the launch of an OS process, how many you want to launch on startup, how many time you want to restart it in case of failures (flapping).... A process template can be loaded using any tool or on gafferd startup using its configuration file.

Workflow

To use gaffer tools you need to:

  1. First launch gafferd
  2. use either gaffer or gafferctl to manage your processes

Launch gafferd

For more informations of gafferd go on its documentation page .

To launch gafferd run the following command line:

$ gafferd -c /path/to/gaffer.ini

If you want to launch custom plugins with gafferd you can also set the path to them:

$ gafferd -c /path/to/gaffer.ini -p /path/to/plugun

Note

default plugin path is relative to the user launching gaffer and is set to ~/.gaffer/plugins.

Note

To launch it in daemon mode use the --daemon option.

Then with the default configuration, you can check if gafferd is alive

The configuration file

The configuration file can be used to set the global configuration of gafferd, setup some processes and webhooks.

Note

Since the configuration is passed to the plugin you can also use this configuration file to setup your plugins.

Here is a simple example of a config to launch the dumy process from the example folder:

[process:dummy]
cmd = ./dummy.py
numprocesses = 1
redirect_output = stdout, stderr

Note

Process can be grouped. You can then start and stop all processes of a group and see if a process is member of a group using the HTTP api. (sadly this is not yet possible to do it using the command line).

For example if you want dummy be part of the group test, then [process:dummy] will become [process:test:dummy] . A process template as you can see can only be part of one group.

Groups are useful when you want to manage a configuration for one application or processes / users.

Each process section should be prefixed by process:. Possible parameters are:

  • cmd: the full command line to launch. eg. ./dummy.p¨
  • args: arguments to pass as a string. eg. -some value --option=a
  • cwd: path to working directorty
  • uid: user name or id used to execute the process
  • gid: group name or id used to execute the process
  • detach: if you wnt to completely detach the process from gafferd (gaffer will still continue to supervise it)
  • shell: The process is executed in a shell (unix only)
  • flapping: flapping rule. eg. 2, 1., 7., 5 which means attempts=2, window=1., retry_in=7., max_retry=5
  • redirect_input: to allows you to interract with stdin
  • redirect_output: to watch both stdout & stderr. output names can be whatever you cant. For example you. eg. redirect_output = mystdout, mystderr stdout will be labelled mysdtout in this case.
  • graceful_timeout: time to wait before definitely kill a process. By default 30s. When killing a process, gaffer is first sending a SIGTERM signal then after a graceful timeout if the process hasn’t stopped by itself send a SIGKILL signal. It allows you to handle the way your process will stop.
  • os_env: true or false, to pass all operating system variables to the process environment.
  • priority: Integer. Allows you to fix the order in which gafferd will start the processes. 0 is the highest priority. By default all processes have the same order.

Sometimes you also want to pass a custom environnement to your process. This is done by creating a special configuration section named env:processname. Each environmenets sections are prefixed by env:. For example to pass a special PORT environment variable to dummy:

[env:dummy]
port = 80

All environment variables key are passed in uppercase to the process environment.

Manage your Procfile applications

The gaffer command line tool is an interface to the gaffer HTTP api and include support for loading/unloading Procfile applications, scaling them up and down, ... .

It can also be used as a manager for Procfile-based applications similar to foreman but using the gaffer framework. It is running your application directly using a Procfile or export it to a gafferd configuration file or simply to a JSON file that you could send to gafferd using the HTTP api.

Example of use

For example using the following Procfile:

dummy: python -u dummy_basic.py
dummy1: python -u dummy_basic.py

You can launch all the programs in this procfile using the following command line:

$ gaffer start
_images/gafferp.png

Or load them on a gaffer node:

$ gaffer load

All processes in the Procfile will be then loaded to gafferd and started.

If you want to start a process with a specific environment file you can create a .env in he application folder (or use the command line option to tell to gaffer which one to use). Each environmennt variables are passed by lines. Ex:

PORT=80

and then scale them up and down:

$ gaffer scale dummy=3 dummy1+2
Scaling dummy processes... done, now running 3
Scaling dummy1 processes... done, now running 3
_images/gaffer_ps.png

have a look on the Gaffer page for more informations about the commands.

Control gafferd with gafferctl

gafferctl can be used to run any command listed below. For example, you can get a list of all processes templates:

$ gafferctl processes

You can simply add a process using the load command:

$ gafferctl load_process ../test.json
$ cat ../test.json | gafferctl load_process -
$ gafferctl load_process - < ../test.json

test.json can be:

{
    "name": "somename",
    "cmd": "cmd to execute":
    "args": [],
    "env": {}
    "uid": int or "",
    "gid": int or "",
    "cwd": "working dir",
    "detach: False,
    "shell": False,
    "os_env": False,
    "numprocesses": 1
}

You can also add a process using the add command:

gafferctl add name inc

where name is the name of the process to create and inc the number of new OS processes to start.

To start a process run the following command:

$ gafferctl start name

And stop it using the stop command.

To scale up a process use the add command. For example to increase the number of processes from 3:

$ gafferctl add name 3

To decrease the number of processes use the command stop/

The command watch allows you to watch changes n a local or remote gaffer node.

_images/gaffer_watch1.png

For more informations go on the Gafferctl page.

Demo




PK/EAYV)44gaffer-0.4.4/news.html CHANGES — gaffer documentation

CHANGES

2012/12/20 - version 0.4.4

  • improve Events dispatching
  • add support for multiple channel in a process
  • add ping handler for monitoring
  • some fixes in the http api
  • fix stop_processes function

2012/11/02 - version 0.4.3

  • process os environment now inherits from the gafferd environment
  • fix autorestart feature: now handled asynchronously which allows us to still handle “stop command when a process fails”

2012/11/01 - version 0.4.2

  • fix os_env option

2012/10/29 - version 0.4.0

  • add environent variables support in the gafferd setting file.
  • add a plugin system to easily extend Gafferd using HTML sites or gaffer applications in Python

2012/10/18 - version 0.3.1

  • add environment variables substitution in the process command line and arguments.

2012/10/18 - version 0.3.0

  • add the Gaffer command line tool: load, unload your procfile applications to gaffer, scale them up and down. Or just use it as a procfile manager just like foreman .
  • add gafferctl Watch changes in gaffer command to watch a node activity remotely.
  • add priority feature: now processes can be launch in order
  • add the possibility to manipulate groups of processes
  • add the possibility to set the default endpoint in gafferd from the command line
  • add -v and --vv options to gafferd to have a verbose output.
  • add an eventsource client in the framework to manipulate gaffer streams.
  • add Manager.start_processes method. Start all processes.
  • add console_output application to the framework
  • add new global Gaffer events to the manager: spawn, reap, stop_pid, exit.
  • fix shutdown
  • fix heartbeat

2012/10/15 - version 0.2.0

  • add Webhooks: post to an url when a gaffer event is triggered
  • add graceful shutdown. kill processes after a graceful time
  • add Load a process from a file command
  • code refactoring: make the code simpler

2012/10/12 - version 0.1.0

Initial release




PK/EAQcScSgaffer-0.4.4/tornado_pyuv.html tornado_pyuv Module — gaffer documentation

tornado_pyuv Module

class gaffer.tornado_pyuv.IOLoop(impl=None, _loop=None)[source]

Bases: object

ERROR = 24
NONE = 0
READ = 1
WRITE = 4
add_callback(callback)[source]
add_handler(fd, handler, events)[source]
add_timeout(deadline, callback)[source]
close(all_fds=False, all_handlers=False)[source]
handle_callback_exception(callback)[source]

This method is called whenever a callback run by the IOLoop throws an exception.

By default simply logs the exception as an error. Subclasses may override this method to customize reporting of exceptions.

The exception itself is not passed explicitly, but is available in sys.exc_info.

static initialized()[source]

Returns true if the singleton instance has been created.

install()[source]

Installs this IOLoop object as the singleton instance.

This is normally not necessary as instance() will create an IOLoop on demand, but you may want to call install to use a custom subclass of IOLoop.

static instance()[source]
log_stack(signal, frame)[source]
remove_handler(fd)[source]
remove_timeout(timeout)[source]
running()[source]

Returns true if this IOLoop is currently running.

set_blocking_log_threshold(seconds)[source]
set_blocking_signal_threshold(seconds, action)[source]
start(run_loop=True)[source]
stop()[source]
update_handler(fd, events)[source]
class gaffer.tornado_pyuv.PeriodicCallback(callback, callback_time, io_loop=None)[source]

Bases: object

start()[source]
stop()[source]
class gaffer.tornado_pyuv.Waker(loop)[source]

Bases: object

wake()[source]
gaffer.tornado_pyuv.install()[source]

Previous topic

util Module

Next topic

httpclient Module

This Page




PK0EAUBgaffer-0.4.4/.buildinfo# Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. config: e887e5fdefbde1f91751dd141946e4a1 tags: fbb0d17656682115ca4d033fb2f83ba1 PK/EAt33 gaffer-0.4.4/console_output.html console_output Module — gaffer documentation

console_output Module

module to return all streams from the managed processes to the console. This application is subscribing to the manager to know when a process is created or killed and display the information. When an OS process is spawned it then subscribe to its streams if any are redirected and print the output on the console. This module is used by Gaffer .

Note

if colorize is set to true, each templates will have a different colour

class gaffer.console_output.Color[source]

Bases: object

wrapper around colorama to ease the output creation. Don’t use it directly, instead, use the colored(name_of_color, lines) to return the colored ouput.

Colors are: cyan, yellow, green, magenta, red, blue, intense_cyan, intense_yellow, intense_green, intense_magenta, intense_red, intense_blue.

lines can be a list or a string.

output(color_name, lines)[source]
class gaffer.console_output.ConsoleOutput(colorize=True, output_streams=True, actions=None)[source]

Bases: object

The application that need to be added to the gaffer manager

DEFAULT_ACTIONS = ['spawn', 'reap', 'exit', 'stop_pid']
restart(start)[source]
start(loop, manager)[source]
stop()[source]

Previous topic

http_handler Module

Next topic

sig_handler Module

This Page




PK/EA.Y3Y3gaffer-0.4.4/gaffer.html Gaffer — gaffer documentation

Gaffer

The gaffer command line tool is an interface to the gaffer HTTP api and include support for loading/unloading Procfile applications, scaling them up and down, ... .

It can also be used as a manager for Procfile-based applications similar to foreman but using the gaffer framework. It is running your application directly using a Procfile or export it to a gafferd configuration file or simply to a JSON file that you could send to gafferd using the HTTP api.

Example of use

For example using the following Procfile:

dummy: python -u dummy_basic.py
dummy1: python -u dummy_basic.py

You can launch all the programs in this procfile using the following command line:

$ gaffer start
_images/gafferp.png

Or load them on a gaffer node:

$ gaffer load

and then scale them up and down:

$ gaffer scale dummy=3 dummy1+2
Scaling dummy processes... done, now running 3
Scaling dummy1 processes... done, now running 3
_images/gaffer_ps.png

Command line usage

$ gaffer
usage: gaffer [options] command [args]

manage Procfiles applications.

optional arguments:
  -h, --help            show this help message and exit
  -c CONCURRENCY, --concurrency CONCURRENCY
                        Specify the number of each process type to run. The
                        value passed in should be in the format
                        process=num,process=num
  -e ENVS [ENVS ...], --env ENVS [ENVS ...]
                        Specify one or more .env files to load
  -f FILE, --procfile FILE
                        Specify an alternate Procfile to load
  -d ROOT, --directory ROOT
                        Specify an alternate application root. This defaults
                        to the directory containing the Procfile
  --endpoint ENDPOINT   Gaffer node URL to connect
  --version             show program's version number and exit

Commands:
---------

    start   Start a process
    run     Run one-off command
    export  Export a Procfile
    load    Load a Procfile application to gafferd
    unload  Unload a Procfile application to gafferd
    scale   Scaling your process
    ps      List your process informations
    help    Get help on a command

Table Of Contents

Previous topic

Command Line

Next topic

Export a Procfile

This Page




PK/EAHH+JJJJgaffer-0.4.4/events.html Gaffer events — gaffer documentation

Gaffer events

Many events happend in gaffer.

Manager events

Manager events have the following format:

{
  "event": "<nameofevent">>,
  "name": "<templatename>"
}
  • create: a process template is created
  • start: a process template start to launch OS processes
  • stop: all OS processes of a process template are stopped
  • restart: all processes of a process template are restarted
  • update: a process template is updated
  • delete: a process template is deleted
  • spawn: a new process is spawned
  • reap: a process is reaped
  • exit: a process exited
  • stop_pid: a process has been stopped

Processes events

All processes’ events are prefixed by proc.<name> to make the pattern matching easier, where <name> is the name of the process template

Events are:

  • proc.<name>.start : the template <name> start to spawn processes

  • proc.<name>.spawn : one OS process using the process <name> template is spawned. Message is:

    {
      "event": "proc.<name>.spawn">>,
      "name": "<name>",
      "detach": false,
      "pid": int
    }

    Note

    pid is the internal pid

  • proc.<name>.exit: one OS process of the <name> template has exited. Message is:

    {
      "event": "proc.<name>.exit">>,
      "name": "<name>",
      "pid": int,
      "exit_code": int,
      "term_signal": int
    }
  • proc.<name>.stop: all OS processes in the template <name> are stopped.

  • proc.<name>.stop_pid: One OS process of the template <name> is stopped. Message is:

    {
      "event": "proc.<name>.stop_pid">>,
      "name": "<name>",
      "pid": int
    }
  • proc.<name>.stop_pid: One OS process of the template <name> is reapped. Message is:

    {
      "event": "proc.<name>.reap">>,
      "name": "<name>",
      "pid": int
    }

The events Module

This module offeres a common way to susbscribe and emit events. All events in gaffer are using.

Example of usage

event = EventEmitter()

# subscribe to all events with the pattern a.*
event.subscribe("a", subscriber)

# subscribe to all events "a.b"
event.subscribe("a.b", subscriber2)

# subscribe to all events (wildcard)
event.subscribe(".", subscriber3)

# publish an event
event.publish("a.b", arg, namedarg=val)

In this example all subscribers will be notified of the event. A subscriber is just a callable (event, *args, **kwargs)

Classes

class gaffer.events.EventEmitter(loop, max_size=200)[source]

Bases: object

Many events happend in gaffer. For example a process will emist the events “start”, “stop”, “exit”.

This object offer a common interface to all events emitters

close()[source]

close the event

This function clear the list of listeners and stop all idle callback

publish(evtype, *args, **kwargs)[source]

emit an event evtype

The event will be emitted asynchronously so we don’t block here

subscribe(evtype, listener, once=False)[source]

subcribe to an event

subscribe_once(evtype, listener)[source]

subscribe to event once. Once the evennt is triggered we remove ourself from the list of listenerrs

unsubscribe(evtype, listener, once=False)[source]

unsubscribe from an event

unsubscribe_all(events=[])[source]

unsubscribe all listeners from a list of events

unsubscribe_once(evtype, listener)[source]

Table Of Contents

Previous topic

process Module

Next topic

Webhooks

This Page




PK/EAd4TpTpgaffer-0.4.4/process.html process Module — gaffer documentation

process Module

The process module wrap a process and IO redirection

class gaffer.process.Process(loop, id, name, cmd, group=None, args=None, env=None, uid=None, gid=None, cwd=None, detach=False, shell=False, redirect_output=[], redirect_input=False, custom_streams=[], custom_channels=[], on_exit_cb=None)[source]

Bases: object

class wrapping a process

Args:

  • loop: main application loop (a pyuv Loop instance)
  • name: name of the process
  • cmd: program command, string)
  • args: the arguments for the command to run. Can be a list or a string. If args is a string, it’s splitted using shlex.split(). Defaults to None.
  • env: a mapping containing the environment variables the command will run with. Optional
  • uid: int or str, user id
  • gid: int or st, user group id,
  • cwd: working dir
  • detach: the process is launched but won’t be monitored and won’t exit when the manager is stopped.
  • shell: boolean, run the script in a shell. (UNIX only)
  • redirect_output: list of io to redict (max 2) this is a list of custom labels to use for the redirection. Ex: [“a”, “b”] will redirect stdoutt & stderr and stdout events will be labeled “a”
  • redirect_input: Boolean (False is the default). Set it if you want to be able to write to stdin.
  • custom_streams: list of additional streams that should be created and passed to process. This is a list of streams labels. They become available through streams attribute.
  • custom_channels: list of additional channels that should be passed to process.
active[source]
close()[source]
closed[source]
info[source]

return the process info. If the process is monitored it return the last informations stored asynchronously by the watcher

kill(signum)[source]

send a signal to the process

monitor(listener=None)[source]

start to monitor the process

Listener can be any callable and receive (“stat”, process_info)

monitor_io(io_label, listener)[source]

subscribe to registered IO events

pid[source]

return the process pid

spawn()[source]

spawn the process

status[source]

return the process status

stop()[source]

stop the process

unmonitor(listener)[source]

stop monitoring this process.

listener is the callback passed to the monitor function previously.

unmonitor_io(io_label, listener)[source]

unsubscribe to the IO event

write(data)[source]

send data to the process via stdin

writelines(data)[source]

send data to the process via stdin

class gaffer.process.ProcessWatcher(loop, pid)[source]

Bases: object

object to retrieve process stats

active[source]
refresh(interval=0)[source]
stop(all_events=False)[source]
subscribe(listener)[source]
subscribe_once(listener)[source]
unsubscribe(listener)[source]
class gaffer.process.RedirectIO(loop, process, stdio=[])[source]

Bases: object

pipes_count = 2
start()[source]
stdio[source]
stop(all_events=False)[source]
subscribe(label, listener)[source]
unsubscribe(label, listener)[source]
class gaffer.process.RedirectStdin(loop, process)[source]

Bases: object

redirect stdin allows multiple sender to write to same pipe

start()[source]
stop(all_events=False)[source]
write(data)[source]
writelines(data)[source]
class gaffer.process.Stream(loop, process, id)[source]

Bases: gaffer.process.RedirectStdin

create custom stdio

start()[source]
subscribe(listener)[source]
unsubscribe(listener)[source]
gaffer.process.get_process_info(process=None, interval=0)[source]

Return information about a process. (can be an pid or a Process object)

If process is None, will return the information about the current process.

Previous topic

manager Module

Next topic

Gaffer events

This Page




PK0EAHvgaffer-0.4.4/search.html Search — gaffer documentation

Search

Please activate JavaScript to enable the search functionality.

From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.




PK/EA ++gaffer-0.4.4/pidfile.html pidfile Module — gaffer documentation

pidfile Module

class gaffer.pidfile.Pidfile(fname)[source]

Bases: object

Manage a PID file. If a specific name is provided it and ‘”%s.oldpid” % name’ will be used. Otherwise we create a temp file using os.mkstemp.

create(pid)[source]
rename(path)[source]

delete pidfile

validate()[source]

Validate pidfile and make it stale if needed

Previous topic

procfile Module

Next topic

util Module

This Page




PK/EA/p ) )"gaffer-0.4.4/processframework.html Core gaffer framework — gaffer documentation

Previous topic

Webhooks

Next topic

manager Module

This Page




PK/EAkDL(L(gaffer-0.4.4/applications.html Gaffer applications — gaffer documentation

Gaffer applications

Gaffer applications are applications that are started by the manager. A gaffer application can be used to interract with the manager or listening on events.

An application is a class with the following structure:

class Myapplication(object):

    def __init__(self):
        # do inti

    def start(self, loop, manager):
        # this method is call by the manager to start the
        application

    def stop(self):
        # method called when the manager stop

    def restart(self):
        # methhod called when the manager restart

Following applications are provided by gaffer:

Table Of Contents

Previous topic

httpclient Module

Next topic

http_handler Module

This Page




PK/EA TTgaffer-0.4.4/overview.html Overview — gaffer documentation

Overview

Gaffer is a set of Python modules and tools to easily maintain and interact with your processes.

Depending on your needs you ca simply use the gaffer tools (eventually extend them) or embed the gaffer possibilities in your own apps.

Design

Gaffer is internally based on an event loop using the libuv from Joyent via the pyuv binding

All gaffer events are added to the loop and processes asynchronously wich make it pretty performant to handle multiple process and their control.

At the lowest level you will find the manager. A manager is responsible of maintaining process alive and manage actions on them:

  • increase/decrease the number of processes / process template
  • start/stop processes
  • add/remove process templates to manage

A process template describe the way a process will be launched and how many OS processes you want to handle for this template. This number can be changed dynamically. Current properties of this templates are:

  • name: name of the process
  • cmd: program command, string)
  • args: the arguments for the command to run. Can be a list or a string. If args is a string, it’s splitted using shlex.split(). Defaults to None.
  • env: a mapping containing the environment variables the command will run with. Optional
  • uid: int or str, user id
  • gid: int or st, user group id,
  • cwd: working dir
  • detach: the process is launched but won’t be monitored and won’t exit when the manager is stopped.
  • shell: boolean, run the script in a shell. (UNIX only),
  • os_env: boolean, pass the os environment to the program
  • numprocesses: int the number of OS processes to launch for this description
  • flapping: a FlappingInfo instance or, if flapping detection should be used. flapping parameters are:
    • attempts: maximum number of attempts before we stop the process and set it to retry later
    • window: period in which we are testing the number of retry
    • retry_in: seconds, the time after we restart the process and try to spawn them
    • max_retry: maximum number of retry before we give up and stop the process.
  • redirect_output: list of io to redict (max 2) this is a list of custom labels to use for the redirection. Ex: [“a”, “b”] will redirect stdoutt & stderr and stdout events will be labeled “a”
  • redirect_input: Boolean (False is the default). Set it if you want to be able to write to stdin.

The manager is also responsible of starting and stopping gaffer applications that you add to he manager to react on different events. A applicaton can fetch informations from the manager and interract with him.

Running an application is done like this:

# initialize the controller with the default loop
loop = pyuv.Loop.default_loop()
manager = Manager(loop=loop)

# start the controller
manager.start(applications=[HttpHandler()])

.... # do smth

manager.stop() # stop the controlller
manager.run() # run the event loop

The HttpHandler application allows you to interact with gaffer via HTTP. It is used by the gafferd server which is able for now to load process templates via an ini files and maintain an HTTP endpoint which can be configured to be accessible on multiples interfaces and transports (tcp & unix sockets) .

Note

Only applications instances are used by the manager. It allows you to initialize them with your own settings.

Building your own application is easy, basically an application has the following structure:

class MyApplication(object):

    def __init__(self):
        # do inti

    def start(self, loop, manager):
        # this method is call by the manager to start the controller

    def stop(self):
        # method called when the manager stop

    def restart(self):
        # methhod called when the manager restart

You can use this structure for anything you want, even add an app to the loop.

To help you in your work a pyuv implementation of tornado is integrated and a powerfull events modules will allows you to manage PUB/SUB events (or anything evented) inside your app. An EventEmitter is a threadsafe class to manage subscriber and publisher of events. It is internally used to broadcast processes and manager events.

Watch stats

Stats of a process ca, be monitored continuously (there is a refresh interval of 0.1s to fetch CPU informations) using the following mettod:

manager.monitor(<nameorid>, <listener>)

Where <nameorid> is the name of the process template. In this case the statistics of all the the OS processes using this template will be emitted. Stats events are collected in the listener callback.

Callback signature: callback(evtype, msg).

evtype is always “STATS” here and msg is a dict:

{
    "mem_info1: int,
    "mem_info2: int,
    "cpu": int,
    "mem": int,
    "ctime": int,
    "pid": int,
    "username": str,
    "nicce": int,
    "cmdline": str,
    "children": [{ stat dict, ... }]
}

To unmonitor the process in your app run:

manager.unmonitor(<nameorid>, <listener>)

Note

Internally a monitor subscribe you to an EventEmitter. A timer is running until there are subscribers to the process stats events.

Of course you can monitor directly to a process using the internal pid:

process = manager.running[pid]
process.monitor(<listener>)

...

process.unmonitor(<listener>)

IO Events

Subscribe to stdout/stderr process stream

You can subscribe to stdout/stderr process stream and even write to stdin if you want.

To be able to receive the stdour/stderr streams in your application, you need to create a process with the redirect_output setting:

manager.add_process("nameofprocestemplate", cmd,
    redirect_output["stdout", "stderr"])

Note

Name of outputs can be anything, only the order count so if you want to name stdout as a just replace stdout by a in the declaration.

If you don’t want to receive stderr, just omit it in the list. Alos if you want to redirect stderr to stdout just use the same name.

Then for example, to monitor the stdout output do:

process.monitor_io("stdout", somecallback)

Callback signature: callback(evtype, msg).

And to unmonitor:

process.unmonitor_io("stdout", somecallback)

Note

To subscribe to all process streams replace the stream name by ‘.’` .

Write to STDIN

Writing to stdin is pretty easy. Just do:

process.write("somedata")

or to send multiple lines:

process.writelines(["line", "line"])

You can write lines from multiple publisher and multiple publishers can write at the same time. This method is threadsafe.

HTTP API

See the HTTP api description for more informations.

Tools

Gaffer proposes different tools (and more will come soon) to manage your process without having to code. It can be used like supervisor, god, runit or other tools around. Speaking of runit a similar controlling will be available in 0.2 .

See the Command Line documentation for more informations.

Table Of Contents

Previous topic

Getting started

Next topic

CHANGES

This Page




PK/EA= 4 4gaffer-0.4.4/gafferctl.html Gafferctl — gaffer documentation

Gafferctl

gafferctl can be used to run any command listed below. For example, you can get a list of all processes templates:

$ gafferctl processes

gafferctl is an HTTP client able to connect to a UNIX pipe or a tcp connection and connect to a gaffer node. It is using the httpclient module to do it.

You can create your own client either by using the client API provided in the httpclient module or by reading the doc here and passing your own message to the gaffer node. All messages are encoded in JSON.

_images/gaffer_watch1.png

Usage

$ gafferctl help
usage: gafferctl [--version] [--connect=<endpoint>]
                 [--certfile] [--keyfile]
                 [--help]
                 <command> [<args>]

Commands:
    add             Increment the number of OS processes
    add_process     Add a process to monitor
    del_process     Get a process description
    get_process     Fetch a process template
    help            Get help on a command
    kill            Send a signal to a process
    load_process    Load a process from a file
    numprocesses    Number of processes that should be launched
    pids            Get launched process ids for a process template
    processes       Add a process to monitor
    running         Number of running processes for this process description
    start           Start a process
    status          Return the status of a process
    stop            Stop a process
    sub             Decrement the number of OS processes
    update_process  Update a process description

Table Of Contents

Previous topic

Gafferd

Next topic

Increment the number of OS processes

This Page




PKDA-UU%gaffer-0.4.4/_static/gaffer_watch.pngPNG  IHDR^FiCCPICC ProfileX XeX]s9twwwHwI7" IA$T$D%DTR@RTDE$A_y?okf}Yk^S1)44A@Ppd6v9@(<"B,,L8?OOuS{zExYw ߆  |"C1|px0g|7J#H>rhX:`O`hP0V%y sĂBq(Ka@'s O~߃KP`l|%DXw>f1$=hg塑V__ehGj!&`:=t~DV 0,9WZ@$7+ GTx}^0 8 @h³78W|c8H K\7A+]!``u  !Z!QHR!=!7 @P-݁Ah z-@+h DtvBB lG>0D" (FT!!Z]!b@$9ɍG*!uH'72@! vd?)rAѢP(U! C%2Q%T ZGh6(Zmv@Ejt3=^D`0 A"c21eFL'f bX59Ħcca`DZM9'9q"\>nEFEOBfNIKCvll/W)b|?JNNCLnIGL^L~||' B!لN W"(@$:#ZbqIAK!AaDIDQJB1N񑒌Rҕ2*2***U"U)I jZjijs L:Ae,'M%7HZ^ZZih0ttFttg ӭЗߣg@20120dxΰΨxq+&SS#3sss+, EŒE>t7Yl"lVllmsgaQqcSӏ{.z.-@b^un6nC(Ja_<<<<N>3||dJ hXd4" i U =+  DE|EJEFD ~ecbh1e`*IqxxDDGI>I'<~])y@R/iSۥȈxȔ<%&ɶ~(7%O+o&J[~GAQ!\AaEOMRҀ2ZY[9ICJMOuˇy|IRm^KMB}^[QZWSZsIKX_Gm)pf:*: :H] a==[9}}zuyxNCaQѺqq ڤ䵩iia[́YY A0K RwVVǭiZYѶɱyi+demGibWk^>~A!aȑϱ dT\"#,R%vws&HF=t529?9"_/g3TEslsU_5+[]x7oVF^L{G|WĹT,ܱ2jkk> }Iud~ezܷ Adެ~kױmvN^^();@W7_y#pf1s`lB7Hz[T: "3pDFV0CcS9sևl/?sRpIr[f7 ][_$U J G'Ϫ($,"*~HTMX]P_[SMYNRgpc.õŖVgmls3N9pJtq ?}͋qWwOo_KΠ!SatQѤc1q=ILB)' NZ۞KƑ3Yn9yg >/Z+Fg)*ջT|DyI'UɮV_I-먟QJM[][[ۂGލw?AJsW{:zG r?o8nhXr'2M&LE~:c7k6мk7oޱ/1/3м\%]C~/%_ÿlo|>r3횝]==*O@#0VXAKy/..u/KQLT,O 6U5Or1նSpsdvpF8o,q;:Kq1k 9W ACއ~ "h"&e_<~=n@) 'ҖO?,fZȞyqUP(Tzδ|HIri𢡄/+_US[S7[:e@ &^o7Ǵ$s=َ{%Tw6uuv,>{0Ipȓїc?649Ea^a T\T}d>l5ɵş/}R[Fc?7I?mo2@!Ph.!LfW"$ST}i:hucadc~ͲnY5Ƚ‹cP4>*+*-/"yJ*S:_lUk 7o+*vCFfVv)Dp}'CS#5cISi!{MV56lN:;;9s?B@~'#,pwgtSQ QP:Q^z7mF%)8fN~0VavEuk~\\ܗxx9y_U{ Hlk!Q:5~ I )!irI FJ*(m՟ՑZ2چ:p+`ֈ``n g;\V:~pznŁ)wW٣an >O72Y_?A9! a 9lbbNjOLjIL}|rTiƌLݬTa9ҹr*ˈ+W=k]ѐ|mD\;w|7q8͉ɡm/kgK_e/$_4_X_/lJoqo-+eIІۏs{{_joo\lOPܗe<iTXtXML:com.adobe.xmp 747 418 x.@IDATx} |T$$aADDۺYvSkZŖ.ն~ֶ~[mݷVmm֥PlET} y&';IH09yns;wLr|x<G#xz=KGG#x<ɺ_G#x<O{ny<G#x|׀G#x<G":1[G#x<5x<G#dNG#x<G C1rt[{<G#?,h{ /lg*G#x<{-+ux<G=>Yߍ7x<{8̞qz&>~ϝ=G#؃~OKd}NȻ;|=G#D;mgSĽ&Hw5qUx<G#tN%Nc爫{K;w43mخ&x<G#Pھ%="Y3tg|2!x<G#N6&w5Y$=d;gSlgR<x<G#Y'lڳiOjLߵd=D$yWLq2G#x<@Ȕ4gҹgz[ nOֻ Iϔz7^rQ/[|gٻ5Y߅D=.$ˤKd\[ڱNN]=G#IdLhK'wyI䒉fkڥλݖwaJ&*u]'jMħk/y<G#KCԯbv.L6su>ݕieT%qLuQ8]=*+K+kI&G#xKJK];7Uw}]9}i#hOuڳȎ:kIN]nz( ,*OWO'wDmLԕ7*sm]ƵG#x<"7eċʆ<]=\zDKntuWǻ2-E[w8Ot2WyG#x<gbMMTz 8dnlWʣqTI:adu%Тi'h:,qvQy`yq"T5g<G#aDݸg+(T:U'UNrQޥ;d=weNNlD)w]6zxx<G#hK+syy2QWN^uQڻrIYd'䋫|'YW]d=]hmݵT</'suqzWF>ZҧKG#x<`ɩvuEQK'./tl]W%]wq&Q>'ґQNߞ.OuO=G#pbuQƊQ&⤓\W 鳩]wO7F_wq|4v]QթtrQW8.uuEm}#x<@48#:4NW'^T6򑜔<ɥ3TqeYGZ/[գSDQX|0ڻr{<G#x/ .u7,qk+0NwmxEwm2co&W8qL\0nĵڴwmxEwm2co&W8qL\0nĵڴwmxEwm2co&W8qL\0nĵڴwmxEwm2co&W8qj/nmmUV_ַv0ygTӹ2,nLE%uɝdnLN]H|66'?9λwSL* .WDiK1XZZZjrh+Fh/ˆ2nKP~E~LlݘjLvsmS4c]N4.d'*ɳ_%#Ϣ!u~Ct{.]-u?;Y&bG^lX oX\>BmOz7yWuO:Q~HN-Q.\&ͤnqE.]-u?~shmjiKNQYY뮼yjO`:Ϧ.[6g(WUPd-¶oKGի.H&5{o;< h((-pvnˋ'T{QyEōJ}6gT)wL78~eRYh+{n(:c<}uw?\#7HEjN|Grɖ6+|Yx7Ng1f!n*šuҳ2͛|I)xR?u=,{:ܵ ZWe]8Qu8o_eO\e]>ΕgK:j[t\Oɲe:o-Fen]|G(m݃^Q6[l=̢pPKmfƒ) 㸲xmP?XW A$g 7-AB;R7 ˜W/9㺱L5VUyҩ=W8aiIG1䲥tLmRO-1]yߕ2S_]޵!6HW>9_A2wQG9ypm6 ׇ֫OzƤ 0Yg~k=^7\9y{j;ۺؓɫN6[jSɳ7Q;tbl]bEe:i4!+wyϵ7!XZ:D:x-Daz'{iK;FAL:R/8FuBl& mxشa_dGu^P=ƥɳlEX˟:'C:lu15N#_ԆlvEuԎ|ՎA"A~b,ZPq9&H?܏h_ku &;x^f kͰuH/)Ĉ:L2^>&X߶mnvף0iS]]뿅 cH;T(#KF?wmjllL3['/%WXGc?}ضk#^Y/?RœcŃz76uevXhy:cE}^p6:`:<%37=41ojd>h[76ccpe.Av7aWl&'՟]]c1~dYj?י{bt="C\?|us֍MYY.b<(**J]lAgrB~JyeRdf+L%*W;ҧD PTӂ:.U' iLrM}&HMr"̽^~%0G,wY^U#>4)8A)q ybbkϰ~4qh`rR,|X˘z;6'< 7:q^X*6x흅2z8kUgǞģ6Gq)2 yٓn&y0";FEiVcj[\sơ€u_6jKzQiCmW\PN^㗏G~ )3Qb0G{zťu&X˞;9 PЄm[їz{CwJHYmB|CSpqn€u_67E?9#ơ|[9s~}i}sb#ګMaؒ3>1d!ߵ_g\N AAl5Fl_c:x"Y"aq <`q;'d:uq+/~և:Eg}6l} _0t^K,r Gm|W%neFKFlXga]:SF,ܰ7n wߍ.ĥ0Ftz}d0 ϽUO=[x dPү:h33A,nm[gA[/!G¾Zk9h:^I? /7;>o؈f?['|&X2xq 1e0ߠ@xN*sHlrR]yn 7ّym>jze?- 媋җ<ŧ6 +R@iqFcVջr+c?XĻO|HYܾ|i9\eg #ea,G^z%['uJy[ S|> 4LఽkPӖf ؕm=srQQ||c.>{𹉽3RYݛfȒoDջ06sh/UͥW} W$ls;Z =Ԅ>7_Oau6j\,׻\ZӉb/=4o U߸| 7}| pwDɬ+1)3zI&eќ IY ?蔽pO,jwwg|a4|a[Lma9bwP#> cƌ b1&uÝS\(<7Q')}-hK^1X;ۢU;ҨAY^Xg& dk.׿P`>yx = Fos0|}uɍI~+ѲEqA8~<PWqW2^8t+_W`ʴ3>@C}]x.z(=kWbưExwj~egѿcr7Q # D7k+hK_-,J+6m(S;ҎTB7nh>)MPvύM^WLʈ,dW,PqyA}%塾*&}˞uڱ]j\IN^ERG[X,Җm]iKO;,۴H&ʒ&;rLbob빖'HyPBY' yQq6]6n+6oa b1-(϶Tyb77؛%rmpڥ}d4ִ}lC%iK:gklM5l}>+}K7R/{Ϙ.~s{}sٓqkK1\s4h ?xkpдA w.81Ud#)wy?5W~^+555^?BeT҅ j[Ke[=2p;(LF3ɣT!(Q?A,.ĩ&)$,z.&4: Kfnm`7[g'zBb<|Hs8xT{B7WZ ^sc+m^u``/x袸tb 9?z KPZv!eihGUmݡ/Q7R^Sy"΋p E-唩_g\݋mXS\ő=E{n<ْe0hc:)_ԙ/ AR9󗘽Q8X|/aT~#^-)0|8spp[?oWwlp^ 3FjcI_gbG^ YpqXwCx o>]>wN } oHoM7Guik; 5TfY*?{)c bC>Q(mXxn )_`u5ɺ6[|Dע֜ISƱر~Q1 䯜FYamjTzؑ)o_էww?x3nQx~q|aqȈArݎJ~"DlDlߴ EvMh'[zKyENοa5K{̥ZocJ7Lގ;O>< "` '/#ܒv .A}\J'Pڹ~=yEY׻"Ywc.KFM)#|'=rP*+)B- ~gwvi\I/~h5.|ܬ)V!sQ۲ Fٛ3!Ë !&q3H'<ض|d6^:y8, :\,y>lE*el^<&xІ;^iG?j22R7ύI?I1*se)w&cPօ yo>6a͖8Y"hX n;NTo _?9÷o^_d*9xk#̓+áÀgg}  =/~#ؾͽ1qԑT، 7_&LjZp4&?}pO:wۺ/ƿL93]n\Wzt|c󎟊GfG[~FE^t޹iΕ[zx6XhCŭrY) : e߿qCe8+yZg7ovw vo|P[c;[EK"o s ~swߌp;G1WO@UEm&Sd˺OfIМ_*DQirkmZԘ]v[;^Щ7\|ԥ4vEd O;a?koPkzaS3G}PԈ)35ƛ*,sL+ꋇ؁'|S7YRy¥x+rG&~r,ܫq"8l`o+6oÐ 'MDI曻:ԡiQߧ0g { =| -Qos5|4 og1h OG_/Ǚŏހ۝A g*'A;K|Zu/s]iOKQ)֠#Nz0΋3EžӇuRڈלȞ:h+G2ç>)c֭x'z㨣ĉPǶΩcp{u :WB9yxr!ɫȗu~&씊%w)yEA^rQFv.?6aJ[,/ٗ/0o UUHloEeS Jp1jrhʖ*䖔"~( 찒Xm|moB}{ N86{PߑhnD%8MYA9xvPhޑ>#_wQQ\'C¶.6Dqff-c &ę:mP<Rl٧:aN=Ơu_mP~!p\mSN=e,Q^HvPg^9NwLL1RQQuϷٯaq,S2w?OqJ ˵ݞwh;-%xY̫_,lOꚰ֞/WVkqm QWeG}_9C馫'ᘳd8H`ȱPj,; foI -L g?쑼cdT;?zq>cmzd ..Mg%會mǸí#8>9m W-nhi؊5u8G7̳ ۂ59`ib`Jjwg ^?{98 q[0~:k0'*w?x`<8SOϨ9 QFyMym:u~0sa= yxy$]uQS^(TB^ĤYok֡d!dpMbV|^2RɃEuKG*}yrIgC*0__ g?KTlZ Q_۪Q>ԅ\0,MUBݱ/9ʐ7ptי>ʮm l$}dxpp0*س0}PbK~#44u1r 1c?3dJxSڪ_cE`J)mh/ލE[Eq>rLw>l/V}V>dOyن8n nu0^Sq-&ea SC0fO3-^w Gf&-m|s%I  4/1/\lgyv -XOη&o M> %=D|\K8ĨE3q7s.f~Zʳ_ZCڭk{I;.(-e07vgўȫαP:QLwyMj Oos/IJ'5LG٧O?4Fi4\\!uVMzƧ.#I(agEɼ.?W.^96(NIʺc]z.gH>]J^c'%Q^>1]0Խhp*ǟM⹥Ka10נ17bP_\a²`1q讥6bڅcӊ‹o`ب88H7ǺU~(p],>q!+oqR4,:F?>bٰqV^i_C+qcUsz C_g.S{OaLmOQF?ډR:0lgNu|O|zGu3?%_mQƺbtqU'/Ƣ^EuK{z6K펥H,O~3ɕJʃZU˦ĂcMs)|ÏEk^Kk1|q䠗m7qu???v|iyr=fSD7ֿ* 9{:n e B(uog--3wހ,g[Fc׾ =C6n 4{ F~=lj18;lܼckwY8 ?mȳǃZZ½q`c:w u\myh>"zM3G׮1 'jgMKKq׮'Ū8}fi`o}%ϰ 8ߝy>펻=acQӝ~WWUg{`VGxh_|O^{O?:}ڴi/^(sK)/,@R\j?}IÓ\h(K}hkg|ȼ ]fe h^ PN;q>PLّP?{XѦ` OŖ7رi#]{`o ;MǟZHZpl&غn=իxť#Q>plJ Oya%/`4_u9ZmhO 4֎Zq?q4Θ;\<'e˘q'= y`?.ŕ^8ʖg]6|}=l/f?WLّ2׆_mц<JGA0 m4i#3)PG>SUzB,ɖuڼ[۳bGž`mYІNY8s!OysA1_:ir0.A؏g渭ۺmxjxe1 h;f%}UUN}1+Ӱ)tFM~sb: }-ϱ/v[RXo>+7ξ~CP~yUb=A#Ԉ-76y_jڀwJzqjZm丱(`?Z`[:t_ +Pm1^qqmq-j|3Y K F"Wnꍾ-v~R3U>һ@${}GAr{VbKbXy vTsUB~v~-sT1 kCf 5={v#CV\O<eeeB{تȞbgM;“OӁ'ԋεL6f&bE)\,ԩxs2d=< [g3tuWG>oeC@{Nm"&'8H@Lr8{HIjɈ#?1D2bXHIx1'~ }%&71rc?9/W7vlS.{-ڨ_Q=eat#v#J=ujmɏ3 yP(chrvXҳ~G~ԓ2?FͱhԩjLOO?<m 1^swO/@gw(wKהp*<0ߒqcױѾ=xg mďk{7%M Ax0 3aG=} "4FxDct*|gY}Yݐ V!1>{k6cG|>׾>&K'>joF }Q`:_fûžg:uʉu_{?HAaLM}آc1.?si=jvSćSB{4'/֙\']c!O?HNMźϸ*?'YA?јi~}R6jGq$Z :ǭZ ۧ?Թ$ϟ=dڊ-Y?~^ߟy u.Rv?|]晬Go&L('U{r.枊Gn=Դ9lS8&5, Ŷ6E/ѨI&Q]{ʏE LH)#f 6f ݋x6@]RPB؊EyUXgLRdl1TCwlix:dG*[SQ:cWSGhvTO=N =s,JَtbO}dga;Ǻ!;?dhUx"~ߟ߻;)cBҚ@I:uJTO]\}X$# ^~K;{g]a]U&єK&0IC6]tٰP|Go o&vJ(cQ2O2(q 6)c2Sg\ՙWI^I.Qa#}Yخ!ϢĖaaw/xYw c3O+F;mC)[W(wzœOuxiv/ھA(oŐb6*N}TU_\na\J>ю)g|QEWMJ{]cUΕu ρtGDՆꢒT:tGt\8 ?lKQ<#9 <G#tJX%J)H|\(L:ڰ҉J]B*:He#('/5.t+>]r'NN*:}|wXXQʍ qAMyz#x<G;pt:ۢ2&b<*,KRBlz9gMww:\>ڡltٕÝhغv{U c8hFl|>SI.~ l\T~"Lm#]QG#x<@7 4v%lR(N4sl77$O[$Yd#Pʕ&tquۉx>\bٝ2 4LV"չ:36Qk9]M̼1CI_g,qu"&5kwąpg8dK1h La_<@COTTT$mPUUU ;vH$jkkuuuDCCC1ܜhii }y,9?} A[nnZx!t,> Mٷ##V亿᮹<_/{^DҎ=fc׵k G UA8u,5mz#HԗLg1=}^;{:hݿx<9+s{*9eLl69"l:#g\Qkuȕɥo|{~YЇIgcIeciW["G̘8*6LgynR7]A>ɽk7D:l|+׶/; H醫Oǧ+.xtIy:]'/-ag8Ȫ5P 2]Yy c"ASG{>.>]jƤl',)dze;{34ڱHp^ Y(+wiLǥѴv!}io YT=xzLY;3Xs. qH>B[K`&Geag^2uLGЀN&N2cwfnN+exLXjk6TX+¿sNmC Y]A_YM8D*M+4qD~*]Yvc}\MuAvź odS~-FyG䞆uޝH۹E|w9Q[x,BDF#1Bzy(p[?j |h>eS}y_Ÿlj?/(|fw?MU/郾PS4`\i0K9DeNg>8 K~3< O >i/!/^xz2[Nuu*./YfR1;Hu8npC>ܧSᅭ)0#X)3쮵JٸSq WaU̲ri:9C[{+ &[{x0|Dot4 εG䧷mT#}B!ߛ.Ĺ[>Lp' qa2@*~W7v05̶ x| &7x`mA'bmM8黓1&iiopoG7Ov%L_pcp`~at5Ʒ9{%ta.ún80d{Sl +C1[_{U9G#c&JAx2<y^6.>tO7:+}ux=x/J:Xg x]*߃lBh>iW'le~~!˧Z=GR0uZkɱ=ġɳ),y;05yN̦.cbU[]_Y?4}*lmy%&-V\8{li~ٌlvSuhH{t#o$c*IODcPg̰{#iGԹlN2OS'xQS~D)xHZD^WzTؓFKT&8m&m}6*XPK& )R;Siڄkc{vI;x; ]p#w#̵qS$&Cw)Oau~߽#.tوW]v;꺛R&AxQw9,qm/lƎOjx{=ތ!U"isw*FO/G#x<w 'nJ5K |'POK54Zodz;^֐nl+k7csAZb+7t}=vjG#t$ф>nn`+}{Οr>?0qKqBS OǍE_Gk[gɇKøMoqI}u>6i^o]׽ͭ/nx@yŸe<3EJ\R\5p vplMOwlƜWŽc~H3s=ZG#t Lĕ|+)UԻ2H߻zf;w)*KJ?H}CF=  ~sDrZ1ۯTI]f1Q/?n?بXn:~&uu(8 7? $^ix?fDQb1Dܻj?4~PHvkd64 4 @vU㪫Xou_Ubj-{+M\N~Ԙ OC;{>|Xd'vlXW:OyfcV8f2)^|n9ccu{+PRPMsXy%]Yye;w,?؝E[aR&QZOm/#Yj= Tݔ`}gN>قs6uK?6|} QgJĬ#ĄӖq֣b>tXz3+=G#g:%q{=mp>*ܷ? m'>)Q3%&l=sۖ4&N W㴓OƎ;u|}CЯvb,*?i6NVjv7g .K|?4W--^VRol1%}03iu~K1M8L%;AR]Y~R,Xтೈ:,a7_[`8+y>cALbAoDIٗ8`VVedblAֽdJBXS+5#E}dᝢ@@ 8pXIeej#dbb۵ǰ6M-",n 3ʃtv[Gf$тfT./dずx]KON0֧."]!iJ ^eZ[]kxYdS Rr1KîI˩x/ɭ0Aac2ـTiefwRU&e(%VPΔ7njl=D4y,KF/DxǎBc6; (ap 79 il&Oy'ԱX  I7䜓weLeq0c6q'jUVǜ|嫟y~:2ӌ2u1Cs|ݜ@XAZp2TlZࢁ*KTnΣn' dPyVm4Zd~ k*Nh.+;{?ƥፂ$znCoWgc}u ɫSy٘C|&nmßQN EK^\&jxDԙl]O)HA^<]ۂz ^a<'I#yc4xv8%:h>),F/p5gmFCd]ro]Ѣ\m.[kV^l`6EY[mKD{ $8ɇ9hz?է&ȉ- VsxNZ G:hnsotN_@~7%d\s/5ًsfއxnA.Ex,?eaAjw{`G`U QVYǚFR8%s m|&^R~?"sI9yE5Wz{PEQ7R8GŚF&0cP9QfG[UҞK;\GqCuH6vi9ȑx$e|N`| D6gx` Jp]|ņn8#p8O6;,*ϓUt#3'˪iwŻTJT]KכNқډ+fI_t1dBZ7EQV0-;/@)x %ƽw9XY+Hj Z M}KکuhuXӌ,h'B8n̹d4JYȵeH3tmPx?LTdQרsB:z֤:ajm6{f 1in%e_ :H {ZlDZmV5}1J"n&QJvo#P8Jpkwd7 d}GLx2Bs/ QTHf){EJtSDǎ̦;LQhQIF;^8ф0}zP68o.QLqckmu,~-j*/ё>)LV:㰂, #;8xՄSa14 *=4܉-E6ޙg㿥ԄGڸuIiKdז5V|#7;0T !ȷY)'\YiO%bRc^jmöfE$T T~&Wy#hC hWz+7S:XjVEG=LA#ӁxN2ILTR*[8kU˸y5JjL/1; o/}CwN3U?cicyyF&KA}F ִ4Ff-!@@ pb~yue<2gÄ6&=ЙxA#!:#MMW7 nG 7F zLt "+@@ XI9!g)I2H# #wF1ȫIg7e@W#q fC #;e)I2jĒ7T1@@ pbc-Qxk |AoJ:a!Vyn7ouo_\$^kF! @@ R81kՂq®QmcVDz%]ʓK̟Ŋ||7A>[q]2WϨAK=ڈ*oe0cÝY3s*vWloÓ5b||%H3aPf):@/JrHg֊zKװ4ڌF"f4. o$WQn]qŎMԥw+\$Oecf$$ubD=:.0JGi/p,ue6[Saj RІh>O׶c~zbG:=(r$_PZC  \Kbly=NڇDPKFro]ѢYg$˩*>G'*/G.͙l`6Eĩ.lKD{ $ohz?է&ȉ- VsxNۂqG<{];1>[oο]<~x* ; t9#umRřcMhn!v.{[b"{ )? }ukA/Ok GY(1KBߋ{OA# 77CxAK,:~̫c"7:pgj\Da$oaiXW;rAۻhO&i:Ef9ZWYr8:S {H@1=DDV"qrki!)) Ut;l8<~ӱx_DYءKpkɄB7|X%g}qTO}.mx\+sP nݝ%&͈R2' ܎d))tss09V)d͖%Xkyrp$F2%pG32ܚy}dᝢ^K@X? J^&-@@ QD]YE:cePM߮RVǜ|嫟y~:2ӌ2u1Cs|ݜ@ZuAp#H@[+ *UhDGGr2<+s`0A=:Z'-qxh3x|LSn\(R:Ц,@G렚0܍x㡉G: E@@ 0ϑ%pFT4|ޑס_QccK}_Rcz ob;@XXO󘲤,fy,cyr ߛDGT#h&C$uz2(̶^dw$LeQk4mf>ڠƚLHM }:C0wѥ@@ 0uu塚d=TWvXK ~\)2y)YXA8ˢ?FVȚV Ft&JΧ02e2s@@ "Ŝ䝥yPy^ǂvB.ҍ3DZl fmEE2._5vtcX9KH+7'+c?e+6Z[skU9֫Qr 6DL.[8Hɋw~;g+`PY+{IvXT9 fl3 >y8tNŮxmx4^uc9$[a3c@`pRcxh! Kװ4ڌF"Ji\t :E]qŎMYCsJ>9DǑXL$(Q;eԍ?Dԙl]O)HA^<]ۂݻox/JYF1# ٧W#5? *dx*I@@ 0l*ʴrj+B9=zɺ./";ހE:te(D%Հg#0 tyqd @^?+ A7}iū 8r}˪)>/x8Ӷ`\^#DeNOvh/oek9 N@ָN fL{|كՇ5}Y_EaCًh#1Jo*iݭɇ71tEgiF4ܙ/ytX/ID۹yؾ`{Ճt.m7:SDI#N{Q&YpU8+/)P fQFnM|<gӕg.wP9o.Ǜy>X{Dԋr}Q׷`mUksʣ7̡rzL{$ x`27LNL9n~ec.An"'[9h3NbEtӎzZ%T /kgSn$`7I^C+W@Ckf2v%;OI?=a$ۢٵ$kV<}^"X'1Ɉ /*>mz<ב_辗v`8_L򔔋m71 bTÛ&X(#I[=cMk4"X@qx?̊xρ4OO/^I_5 v k[`uZ4tukӨ]G+Xo5 WSUFH~B֟yل x:Z!sfw^9 :T\SxNo0qIl,' 7!nQ{3#M!ߜkvbL+&td}[&˗PNter` S"z/Txo2@7]y/5dl8w'Fӳu ϓUxg-g8dY5Y~akLE)O%a ZKN=VWIJjX !s:яn4e':%324xbWXD@@  sc>',_]Z쯮2-#@@ p\SSY,մ 7}e촠nch9 ANvw- +q)aC! h(C'yY(ǣ 5_L_YɲDױ?{{:(ynkayP!}^:l2^W|!8&RѠ5_eV+XaEl=io}I: bOִ*~*!> &,U[h+זps/Afv8zzW%>DKG&Ei} Ƕ?ętz,濁Tr @Q1`6uxjN-MoQgڳݯRr2aF޲pF Qgbhr~z(Y뱟zDkX~h`V0ǿ>bKAk0Z9%s~\'!Z  M·e|%W6Cmi;I 8~aMltиyƝDig e*xIG`E$idc"9Q'޼Fтo伎ZGz ˆKNC&0=;N3=V5;YOP}|9.ސ5=]TV}tJl\×6> 42Mw9</>;(Y]&Y , .WwhWӦz¬Moǘw}wL,nB22^2%!cR*45,uoyQL9SݴR k^CsND}͸up`pN;O^|: ׹Mr5wR('@dC4ub΂50G`N.B if@dT"һ65y2H㟙hߏ6 6V ,B#PN=Pװ w}b!+-ZH'3#LLalN[;e}d`yV}:(P$~]]%2mJi"8U iAB8u| .*Vut• @tSKZ\oFry7% kgo}&彃+>&->ŋ~Md89'/9ݓ cR~SçIfN=` rbl";iH?}ʕt0A C&a楘]_lٿq"-Đ#1Ϗžb7Mp6k 膥]rl%Ǹ_8pa AΝ7@ o'!7>T G壅D q[V\?~y,1XN /ci^G:94g )fȾe:-)uW^lژ$8I˫'YZ\ VjgT4F5}ddw,>{4);%L6LX[WKT[,3'ۤ3sF?Y K1Sr-wٳLxϼ3y,"iSC\ƞY ts WF fXu >sVs(k4@@ 0PpVX 3чDQmV\slVuulϞGÊG78!ܯmʶBe+P]D%m8[;tOZt6tc(; r'`wku U?@~rULp JZ0iy 6i Cj%@zs$@/  NY$L^eS>,Kֻ*'X"Nry#Vc'|m8a4%MԥK濁Tr PňX`5m'?3ٲ7x9]0#of8=>[oB0T=Qx 0bc"|`}A~u:RjiC3mГ9-,Yg/Z EWo%yұ:J{uޖj?[ 50w~)pBsS  gtCe*/GvZL!c4 XHNԉ7o%"E :z}YW>ymIׯŞ?E'gm @rY<Ƹ^B#"q3 jz;nK&"ycŷW-$f/XǙ "'\;A| tS߰-Nc5&;?|@FWZl&rm(4AHɲA 'fz.u凅_cܥT @P_Φ-SL%!=@6ǖr̾E]ǁ_5F _'_X#L@`t",VtN*J"L,D閕ز/m}RqCh23d"̛(br**x$;`_}wP y)'PM"<{9X|O];2K~c;ѮMMYpߪ1= {L,nB22^2%!cR*4,Kݛd^T1:4SBnK)fkԃ'fuEXxܳ 5M9 %Q;k3_]6'ܠyfC' mwڄfSZz?5G*kQ*&qNLMդ~4`5zɺ_{' !gJ#kJqײ5Ǭ?qh )DoCef:%J̗(F)>֯ K,mY5-9!!myvBҚ1")Ŝk`?EH! ?0~ڬB#f:}?5ؕ` 6V1>S`ocEM"{]`dz ᵕN [?~Д?DO6}%'yV)r_;4\Fn(DB Hpc-Xxk >/T&ѧbVi* [;iCn\v{'8㮻~EBsͽţz"QI{+ UCu^u:217P6 W[D:0l+G]3OypF퉲ӕlDW{+!˅^E9(*xVBN6A iuOE#:TҟMYO?ͧeYrHHg0Y[ \bbGkJIra=55w+>$nB^lFyC NY<x=N q&C`ܦ(iueW?<kOde,V4yTuux ty+GNzr{xvJW06& dah Fp/7X]QѴ%+S}@H<;L‡ųN}ޏ7f&W`ʆdmOW :e`﯍X(B?GVVf/zcUW3`طh{1S^3Kߴ]JUxǔ+gV!ZwZ.{o)DH=PY{Jz<\=jW2_IN7V:_#͚)KԙB ُ .@e5@?rjPP-m#- bn)bp@`x"~~8nFhЬЅt Qq.2pall=H/0Nx'Y]!\`EvbNљ@@ 0PjD!ZsnҠsMG*50LD}dr)lڇ '%I :k1έzkt rese>^?r)o(!&@@ psltNΕK,S ~WՁN BegFjp||u O'&ÙG`$č6̈=kD(] zG܂@ HMu!]mը9 5׸f]_ǖ W:(y{CJqx{ks#LO>s_#?1>o? R =g]*:{椞Xy(b^? s")9/$ˊO9s(5-Sy9 e*xICa0ƒF+qoކKD|&u!W(鳞G )it)OZSt|v/ǜe#mu/m="ۨ͡9m@K1#wcFGNDF;Ƕoa[H ^ $ˣX.ҎµG@7 KAۙߢt<-_#oW dtUvl"oBtaΐy {\sg^-z3hk\ohO#?=:7U9ؿe3~It&W\jJ_|1hG # 79IW){,Ujڃ.$+LD=lZAμru dr_@;wK2PVo_-.~7d.* Qe%l.lFTP̿F &ػ X`.C^ T|,^FkSf鎌+һ_؎r+iSSFD=a&ܷcL;|^<>#KpLb%羌WLIȘ"36R&a|==Tcz걛69RZh>b+ꈨG؄WWa0ν)=nrMo8A]emmt)v7oC!ҬF%4?=-]H 1">sn4'!%uLT&v_"t]?LmV!kd?3wоm:))nH.pCs03=`7>Rt'В 3UύBHDk T$qsmudhu _~}2Yt4e6a|Z:Z/Wu6pHKKĕ,gHw@.iH[Mk4]iQ" ,Vp\Ii:}h_(L*9 (5r%sdN _}DEn4`7NOS.=cHJ @nC {zWʏaGI~| vTʬN8Zϣ}L=}7(D\gugFcrz6!ẃFqʍ? 9+P40t JE&2ӗI:N>O2+p4cgPDz؆3|NFr%nxP8<y)G$[`kL"s ?\ZwϡFZ0g,E.~zaJ~Xww3s„Ɉҷ2ٟW q\hΧ,M,k+( 揖,FzEMT?झZxh! $YG&&5}[epE${cg[9 g`i/,=4JT}\X|UDGiU .3b<>"ͽ#ihDǘݞJW)釱)nI:8(U'$\ _fB8 h*ۻuRa} Xrc(%QƊ" buDg'J +[:ʼPMj3j|k\@= ~mPGteBC8p(Kyrį2+? {,q֯x%>m8[;tOZt6{_}\h. ݚvBE\6: dZ}ނ4FD[-"Vbh+pbNձVoX^UO=wJ.YNzцcFSLD]jڍN(kH3r#u U VCvmhz: - IE 3m3^<]4G UD#2^;{_+_azDyZ k$FHKmvbGU<uNԕkk9VOv)٢1#v>1nZnohO#S~iZF7وU:ȥǦuȧ,Mث= oM^@`D |s͹+I=/tqaf")$ >ru dr_@;nW9티EqlEe5a!JĖ8|mx蓊@ȟ!aD{SQ#ceK9ʝo偾ثh{ʰx"ݑq%XzQv=mjʈ'ڄV}ǞW؋gdb wIܗq) ZWqYfY$"ѡbLO=v&g\J1]_ ͧT$>1sv,{Mmݨi,_huD#fl|sd̔CujMطo B2U8w."m7QYֈPrdpq/غ\ (I2#~~L`u?$TidM}Y)Z6G~!/Ŕm,L S g{Qqބt6Ґ^<;w!iiMȘ։b΂50"ĸ|D}w]?LmV!kd?3wоmڰ+R+ܐ(]M"`f"P{ 1n}.S^sO%5fS+\ Ou)\vocEM"{]`dz 5VzםH4M^3]—GDMKq|}k@ZZglFU P{v,W[ +6%BqsSΆ<^WY>/T'k^2J͹\dNQ̉|}#%*Ze`1.;n՝N@]{Ƅ(܆AgT-͏f*>>|ɩYVmUqٟG{znP72s (L:lB]C*}7t!+`Ϲk] 3f6X#ϕ3^ f3I,-Y2+us(V rVhaOy /0s6ogUX䏸#شdsr\'NV>.}YD#h]ѦX,z_,EJ pc>k%yy1/ xTu'{sK $ `bz/^n*b^[{ U/[˿ۊIA$!@"Đɒ,3gwvg7{ξ$>'3ggf{3guni4Kh7đAFv(5yq>{zY|_HyCJ^#8]yFk\,/s䭸ɋO7_"pi@4әvxϵgvP"^mpy : %w0--b ;)ӚSKɄD=}P8|\|ֲ3mɼ@Ҙ 8D9um"fL>v4IFI/`/>ODD"E8ک4SD..v uһ螹+/>fԯc`l5?'7:ZsGp$9uM'?=z"0@T# "V $s*WQ0.0Ď깒iW}xLE"Zֶ(GnQ_,=^Lӝfۏ{_*KϸǵyO>v|6_ n3ɇ@3Ow^2SR=;kLTc'$[&o{rr{^# MaBvG)vL7 p r~O)ǨNA-2~ /xL߸kdw$ cLV7ӧ>=?Y^y:#,7QtY 4.v)>sPv-l[f1&VN;޹KO&Npiq@݉?$$yкtCvz!} I2g:hȿM N2@T#O%y2 *W<^(LE"\X CDZRggwuy/Xǽ[md̴5Sj=C7&c 3"__gUid֙ϾasBPZw->+0#y`XQ<`&>줭70c:jA7NȺbl^Ϝ3$HzBl *10~;ƹ#|';v$"\M=~ T@,6ϢEKLs/kϴJHCɴ)˳hC~%8i ܜDӍr)-_i%94LFFbm7O F:=QGj sb{xhP:jQA4* D}0B,1~!W";˚#mH߰jZz2U/VY H78Eª)7Rj8)[J38zo~H'vNxv'[Bfmֻfɖ`y/X`-u ^tACzEΞn;lX ztR2f" 3.0Jw'<-a@څcӎW{µF;! WXq#^O򬇵<1#0#0 ?v fJfU3#0#D@(ܟЇR&S]OGk&as Rr#0#uIU1#0#EHb.c=ӥ\~2#0#M[LE7YvX=g#bF`F#s."ɺyW#6:A|Y׹_B/GѾ}(*vFG^9`-:(ڃ={`_Qy;].-AMY(t`F6u*뒁ơ8V{G\hdkk7ɵY%ua]FB$$FݐzM2&}(X O~{b$dDnA~#4+5Гy]po^y0kHP[t spݏn3|#Zys=6 =Fv{ΥqB? ;/\;t-mz˫$ґDب\ߗGGtd1At c711tCODG7q 1|:踗HG:;t|PBO|QF|RJ/|SN?Fू *ୂ +ൂ +ག ,ƒ+KG2qHn݋:Q ЌZ^Pqj V㡩Nd \e{oFz9"OgJ(qF[LGRѨv҃~k QTW -؋z`mqFu2wEӦa,z}l*.ߨЋغ}7Lwy*t2s^KziGpa?{ٚ5Z ii;#BL5؊5Kg~Si'|i9{Eт̚5Y/FՕ%OХooUœ`.G@SD,=Lr#qZw͸V' (||&Nql{EC% Hqּ)Ƚ'n5t!n|NڍG?\}Kj”wI{cNsr 隶<*uO}V&H#(_T_2_”c\~R+ |Mu QߵabۘɊq? ctJg^+䰼9(^9By غy3v]<GאCivSGwљAXTQzPIİ#|J.˒;I pc1n'[l eVwc9 P6C880@@t6H.rUXM 7|ɸ<`_U]rp-+x{Sgds݋oLယ5ԗci;dNɨ>GŇTz6Qhæc}9>,î{W[k^nn9Z{p6ц`D V|@@w9CxO{!ŽyȻْi ".r,ckIGm/ZݐyyN5rb_^'t2̲=gD]f.Qyxxsb:ǖx Hk-Aؖ`=}R ܡ`va&em{K'Su](۶ r3+a$ZQbEsȦ;^lL(EJj@d&jԎX`^sB&r_+gIɺD~4:=لIxymK'{Mmpl"d4aJqs'[پ(ڃO¬y7.jg]PGӃ|r88Ge4'ҩ|s7>^l,׉LC;9e@Yt\gAf0"!w-9 c?ka`^{NMXtc1|;ZA!5Dϖ`}ЪsɛݹUV/햂 v* Z`:k 4糱h]Ze4^0לtߔX>{`$sX Fک濌#)* W>!y򈴍h*-j72 eiX~"\XL]/ӂ>HH|XL醢5sbJ9G^^b*DH_ǡx*5? # ;luDiXLJ^Ē7Aۉ:Qu}]_r _`AU% ]*@:Y&\vJ6~}ӑkH +-wJs7!M{%^i;@n҃NjmS,Zܝ§#Ѕ.dcƌA|qDh$"-wT> mXMȴ<2ȓiS+b',n@dKo]*Ӝ'ƛ>916lەU.T^$'7dQ7AtYaTHKS"pׄ) }w_#vtOZ[iq6|Ji ;)ЛFmm)&XA\mm]/`{}cF`FHb.c=C\%zd=ZF [Z6JthiA%,/H}K(H/xu#C:Rpg[:bHr#|XPz{+ ~Lk񇣧R O2\5֔afO] #dKҾK?:OC\ ӌ뤺/T{GD(Xfa,"V|:c_ⶑ5Y|E,>R22!7šc1l.:KsA=OA#pWnYH>#0J 7:rCۣ0i`ԧZ.`na1-hiJJצ .B iv4*J39Ӓ\%:"̬x l1JϿq2h8Dj/`5<ҝH_IiYh@ڸ02tЮB:b4zi7aQҳhɌ|~.fF\tJ=Ib)uS l ꨚ,^+~тn>(!IYz+2:K㉀ןD%4el5$oG=8qeWK->oMI=384$0u ʼtJ#mp+M~9膠vY$}W&m޸1%kzfdYHj:D*ORtQZ/Z4ߑxzIenmxc0 nQ>v>d4cfR3Goo}2Ө,cFg" =F'1/Zd*6wxp[Y1Az]wVOή|+ 4 Za)>Ku3FVcL'edn#=WRx|]2&NM_Qy3-0Yp =Ergr OOo_}12zgGL}yRm֓M1v`P↓7,zqڂxLyV_i8[IAnlhRpߠdzrMpFǛcc%0Fd͋F ֮*VĬG\Mx*~Hc=z$\ {Plū蛊|3F` *Fi;; "H_%((<қ+o%##X1Y?۪u~ȕz-wdr2R0BIH²Y}穽Pp„|H w$I?o#/'-zz`JIut[$ܔ` 2*;Ow`)IeR@EGj'ڍL/^1zBSY@b& IO%k#;SSF v5zi˹\hmˁz W̑8zZ^ba~v# T-] UGB#uy,Ǻ#;-栭[.k#vJ_Q5/ꑒ9ڡԊM{cG: {BDZK"ILP[u&g`"knd/ot#3d,=ك1jPF"µ@yjz|xA ?}vIVz.gP^z6NlIK~J訏agguZ  >VDqaZ. 502}^;Fafd,F`:I%QW+UAXՋt`w%ViXR7"\XQDKDZRggwuyHƓwRI^'G}%ŧ'v$1%;h9LH :~ˇT܏S3 W}G@ ?17,b64/ǩ cM`-^_%0&XĦ33{u1#гO0vX .hz4|ԡ_t5ѮĢ<:2_FמOdHCɴ)K?72ϥח 4h%ыtLf8zq/N Ǣ &mS=qZ^WӢ }DD%UT`M7ImwуYw@m`F vUe,{'d*ya5@'LْH)C1Fu5@-&;udFw),ǏgF@qK橝"OXM3YՑV#Լ f渡a(l3QAϘEbF-?VɻlEKY,eo#0#0W-$ jZzd=ZF/67"^ 퍗p^ي/rWOI Ս(bkFG|K.LjJ|zcG/il†K-Vs@cMf~j0ǟnaMEJY!k.~iN>=rd|chPx0U53V0GGm$vMp4`2<(_nc3@,fF J\zfKyO!0itQrs АHjAKKR 6euL\x MkL58M[(>5y^=LY#݉fRZCZ6 (kkhH20Jl##0[ƢVk]͋t%E&[1>Â->E:& 75$0C.W!W#}4"wn}=K/:K㉀ןD%ʐi?pP|}HYC֩zgF_ǣEIo/(8.{+[oeNS }rͿ}ȌFT[2eH4WP} 4w&jݧitNK}xn 1ǡ'ᅩ㑛!' ?[i&+*nrr13=gϞĿ>G[?R67[zz#̡[QF ֕'FwsOM 3SiGC<`CX}U`WC[&#Wx1kFӇAZҎA!-x9`FpsQ$3n)z؝Š&HZp<ZBՆʆ46jsOqS,/< /qBpt BDW as _ZvԹ PǠ GcF['FN+8闃nlI@W&mޠ/P\jÿOq4ee#vԋo)RD]k hub@G.V'qvՎw0Dr1[HD} Ulr ^fw]| a݊D ]wL)7`&Ϭ!2f*w3MUA!@Z1sZqQ6kD4F9x*َ.ig'QY0@! Ho'I{!ҾG*m/96wxp[Y1Az]^==G#;(HlENXrn:\#aTC#IYD_㕃(_^+$|&K6QHҼr#I}Ȧ0YՇ_'U{/$b jqt Ff⒰/HÈX?6L荴0GU<$mĮm0$:'8z+@6eT6p;OW]$b~L:%i]89Iv#ӋWTH͐p^{4>zʘHoD7􄯧;⽉GQxkX~cHV*O»ކw˫p8ȕIڗf]^kgX2#0pBJ*[zDU/"4A[!6\Uy[(ZHQPjŦ1V=@D;V>HCku&g`"old5TJԤm8Kq& ]qx֞S Œ@ , {kb+ę;F|*t(^xw)o#3d,=ك1\-٩D"+\ Ǡ$j믯`鋀9 [n(Fcm'y{#Rx:v`'/1dxXJpJTXODnJF*}<|IJ퇉 |)I{aݪz4au;<rF`D\o#B%! Mɟ/n}s|p7^F&*'ޱԪXmS{wu4JkHD\_,?8|yTu| n |069UJZ6~7֛ l}Oܺ#<:MeG#tH`ĻkEȶՄI˥lghpt!$qI1U UNqܪl7Z`kz'3?rՒx=r%.(=-:v waڜwu΂~s) 09KVltkO-ts15C{aNn70N30#:*iq.cQ#k:<^L@2K&C<: ӂcIHKXL>N zAx&uR f=9_I^|z"`'HmNSSmTAq&$0 ˙Q}fLA^1M~B7^cSoZ _IXo)>N"`-X O 3a"Q8ZZvF`O>cǎEm,A;!e2Ŀ )ųo:7LgX'y2-ctE,D͉siJ˧4.:&3-3Tg,DɁvikîQ򱨂Ƀt)MӜnFˁ`F`FK[PɷԓKf2Y0 ^:O7 طw g1#0#Q!ߒwh)d=Hɏ\di+LL[Sր0#0#B!>2kփ@Drl6 X#0#0 $ߒXV'j:3 2A4X0#0# *iqHROi^#bF`F# ]vCe<c&8'F`F`Qd$B&=2kyLcmD?#0#DȷTR/Sb)dFssd?_}PTR ڍdaO.{}Kphك}Elwr6giFx:جR܆ԩp(ҮKFXZ3;py12sݼ'jge8RׅSuF FPIU2'a7`.<퉆@#tT̑-(/fLQ =icj:p轍qV* a 4EԘ^!Xs8nр;׬;p ~ߣ&T(ٵz0[f]9( qn>s#Эd\Ēy2_I]UL_Fq޿p*[COWO5[]b뛨x2CC.hgr`w'H:GzRI(NJi$]՘ȣ ZkWË~ۏ1+ZJŞu51SҍKxQX)hd]aL`̮ͼlAs> l(o3@wG@n1JO4.cqQ8.$Og6W<~5g%JZ{ ԇ"L]i#iוyΤ%Sm;Q6V>c[^y;0^ro([3s7M+)[f# %zVL%f"VI`] B?Hǡz91y*p덃d3&JG{=4VpCVG6,vœ緻(ſQ Vh!5"[ 5Qu`<J%N<ӿE 1X767Ү7='ڳOl[c4?a`g\.sIϵNס9 n;x,qf欁 ɪw>I63L",%ە߮u޳=oW>ޒ^C+v9jifD=W;j4۶vqs6:W/^=@WeE3Wu;D({\[UUb·:7͆`\C m1胜p ˭rhu 햷O;[iFqk+xෂ +x࿂ >,ʒ;ǑL~-at zphe^/8Uԁt'2=7DQ_=3A8KO&m#JbhTJb]A_f;wmA5بI*RHԟXQ| i_n":IOo?:ćYonG۳S{hL$e"d Q R[HX]L,~~'1hl Zex?v7%kq{QLXhxi=#D̺Kfջ_ǢiӰr>6P\oVElݾa;Vd:9~R%B#8𿰟lMIbdQoz^ߴ4Q]ܝqm!\{lE⚥3?ة4l>[J4rӜG="OUVhAf,?#CN^Kʒ?7Ћ7c*`OaDl`ٸ}:+s_[Gr1뵎D JϺ]*yZ}+HgkU8#g""G,x^tQk y}1R5o r9`G _8ߠSv׹Gߒn0]3\\Bro-Pao^kod7qkCW3[r̚ Rw_ɿno0|s"Ll~C0Y;^gwar`̔NkEÓ7ŋ–3G([7oN"(ؖ`}77n=:3<ˀ*JO*6v@i7eYs'ɡaն4t,foq d,J5>n,Gf zv&㡷0q^1m(;)w`4_0ݨ^K9fIe,nI®rZ{pHm[꒳ mPt_)ۛ=k'.^|c%ذ@=MԄѤ{Nѿ%;pHMF9*>ҳq4ZGL6aivݻآ]r`O%tޓ{|6=%Rng-"$ˉtHu,{ v|Ϋ@-ȖDpHS]Gy.r,>t%..@|8okľHNde{Z3-]֟19ɣ t=-GS[!-rչ{t~FC7M:ZO{`$sX F~^;A=ώD?~_?;Uo < `T,O[YcF" I%7UꅢkTO4zI&0xd?_䫇?y";"|!S˴R&2?:?#=Pf^LY7O 0\Le8^U&]'t$a13C w^‹X&rz"3v;Q'j϶:KNCrp65;ʴPV`K5hZ5 ăn6B0Ot:r >#?a^N[i&iﱄ 3;:z(0MiUlbњ||op~?v{3}[!9M!ّqp| Ǝx !nt)Btuu<~i4µ^,?Cu_ ѷQk`NN#xFȳ ճ.,\zE,^tMP>{5zSr+VsjFkZ|v Q!p`xv6.ڣP咐K/:,#c9vݲro#0#0Ie,\%xUXGɨ[ZQO F@yK8Rݍ[p.W7UYsL3"xOBӒ_Q;ޓԞGfN+x! #(Į+04`2<0zn F kԥɒ\%R.c5d!1#т69Iڔ4潐`jck}(&,p4*He3yӒ\7i_FBX?z\O[I6?}n_F߀g!ydA($җoRZCzd mBcvT&=Q*e2#{.TU.%2!sɺ)Mb|[v}ZutM Ouob_Iap5Zܭ18Z zVo^FXgRP83SaN!'CF8|m/Q"-00]~߽F_ D~=u0Μ_]7<-X5m2&8cCI-O S#7= M3ݚF rr13=gϞĿ>jKK:=z!hˁXOObD9^t˷h$~zw9zjMJ/0 v<˨+S鎉0SYLFc ֌1*h?_ȵ)f4<[9#0H-bIƁ9:=/aYK@aO O 髖v4ViD}u2>mѺM~9(]>Ic6 oWR}|^Si#YVf:aGPYy +|Ťx\gO*Du6_7na\nQ>v>dcF "X%\%j:P}eZػۮɢn{KMRKoZBDOyOxd${\"2 oQ%ʮbwi9BIeqߍMzXq=f$I?o#뽜m0$:'yD` 2*T+.1?JM4^P7㼟׬hkeQ/^1WH͐p^{4>zʘHoD7􄯧;J9>8/}#Gc XjU_,b;jT* f{t"o n~}U74zeJ{]á5eJ g-)qn.;r&&.fmvZ?m$s5aCRri#JkbB(B No2G Ir=8C?~rpCAsbG_S;դOk yaM뢃[\O&Y|Ti'2 6т遐XO&fDԵqٵ(p#D?3f ]I.!br/M[sq~RG,#k2O!dZƔX>2ϥ*j,$KvZ-E[u 5%d**d%SтOJC3LNF aw 0#me& ONq0#0#m$F9WIJԥt dش vf o#+lHo:CpaPrW0#01{.T.%2!ch 31kB-^#F`F`.@@nKM|'uUy{uA5!E伮 F`F`: "XV'ɻ<:2_ƪ[%u 8PQ{v 9P]ZjڳP4l# oN8@dhɺm “ߞiCx #[P_JF2E-%-ކ׋B/c63f`Z{|`oY񫃪4r G$4HQczlG`͵:zhDv\j5/~PY`Fg|.^yڴE,<.\{yAk1/)({w;NQe|IX5|z룫Ql}O`hȥ-:@.l'NTJ9EFx:yW5f3_jBBeUc Rgfx ŔtR,t/>4V9fF`:p|rv] B{1d{mʍ(X׎To^zMBIxfs{ر}>4g%JꤨKHwË(cRz.@Fu`]&-jRDyXMozx {˽ynR>Z9sՎ?v䕿I(ܑ~7h/|7}< ۏMvw:WG?xZ_693`̀0e3^jȧwщb}%F|Ǐ?>%ߙ>sMW;aͅ+=s|9HW/; }iW>}w;A7oJ_j=KoLwΓ=ޛ~0ԣ{gzcNo:jC)sģG}嗟K_t8FCKnJGtϦ>&Hɧק9=H:)حF;/HZ~x=3R3ܴC[u_2lLϒKޔ;dO'HsKWޔn}sOs2kqqmr|<*:O',Vv7ġ?;νa_sKG|BPiTjϝx5O={G7K8yKHk돻93iI0O])zU8o'nܝLsuU֗'I7+FJڲ\n}_h~X/IuCjJ_}ͣH7}3)]_3t$#wWc9W3`̀0cзZsQ b})'|%D2_v0=#?22fT;=_I{WSt@ Tz6|qvwק槅 ##(kjqKߞx:p=4s箿37d}dZϕ=WO0^9AkoIzcC駮#]p 9W3`̀01Ź:LQ\bn`5}|sx;;?r{/KV{8s"t`L֜kߚKoݩtK+'9S׬Z1]/|:sMW/}rQ#4jT_$~hY {=7ɭu7#g/岛ӛO9ӾKoyH ҟ~徔Bz+!~@ߒ~Ɣ~43+?/9KG眗R~꯭zcf 3`nlH|^ڑ XO_)}Wӟ5o.=ytӎ=[k[>~߯ ݮ W*jbEngߓn=sw9o%c[Mv/Liӟ-U}"}]W뼻XվA e/bwqm]Czyz,}~% M>\tKwySwJxVi>N?˟?o^zgtwq:>+?ssO:{ߔt1Dz8O:s$Gf 3`1&KN-.c܍fRv{/-_x1lujN%'\>5Mk]߾g'Zzˇ5+˝;J]1̟8Lz#Ωt,sz(/dUg9l{'Cii7pDz~y%' >{<=~4?l ˙ԧZ?0f W:+s=RSwygS׼e|.= @ >1˶]|.c3̑UZ,ؠx-Ho<]{nL78I3`̀ Tz5B-Pz\J6}^8k.OLc V0RN#kO 3`#hW\rʛz;stϚc8?dcG]mM3`6R|7gP},3`̀0f lgJQ|^ԗyZ/{2f 3``эȈ1uh-]=3`̀0f` 1PRcЭ| ; z0f 3`L Qԯ, bM8Zrf 3`̀0g ].ͷu>wI,[[mx`gX"$ڒ6sl̀0f 3p0w:sZ;߼KЫ6ڴa V.jvC)!! X{3`̀0f l!ݲo|H_tEͷNy5]٦Cjm7V#ghn;C{3f 3`̀0ʀa׻ޕ;tgu ߷o_koWlWӱzm4}l3g}n c̀0f 3 [뮻.M{=o8nFD/J8Y$gv5\O`C(N͔xQr;{7tӻ[wp 3`̀0f3p%4b[VG_w7^ߨ:W>KԒ)m>5/C}X{C=qСgsט3`̀0f l< AW7u9Nuԩr-Uj?hE}8@꯱^1w띛HNJRr_W^Z~Sߑ*{^"f 3`̀0$*p 䗘V^|~wȑrʉ&VAʞNɊߘl3g5Xꢕs˪.ƪ%ƯYP57> k=|N"*'O~/Kg;1-Cgi0f 3`U,Z4YiE8Sr闵-Y"ַ.,%+Vk(J{mABM+}ĺbK4+~pw0f 3`Q+G+ŽF=5[ mxn5Uߴbu(pWO+`Ǐ90_˘sJh8l̀0f 3`Z ?Z]tWW>洫bV́cUDpm6 DG^aZmbWde^ak1g 3`̀0@8)奻.1Ճɏqk>X &E7ULkX5/#;>v1WN,Ko _Zkęk5Xy^/X>- 0f 3`i,(WW.>>ѲW5w#/jBcW+C]# Iڧ +kѣ7Y]>kGaG,Mɩ&^̏X<[5%e|tO+Y`ђΊ>0f 3`i$V)./pi#K-43֖f>5\Eu8pb,u}b:zc˞UQpG>a -)Նb0f 3`?0RRdS%.s˲Ӣ&ƃ }jm8vb=nJbc%'v|v؛ZwjXVq"7:0f 3`1 }T.0xXڌ:b ^ØEOόE?o9vPۚ3`̀0f`@/qO1/da/ fɓz/嚦fDlCiĺ{(zJ[/+KoΣX>;3<sr`ANc-o~1f 3`2 Ī2rkpr]>5}br^ZmFo[~>X_7$:@!Ր55X#x} 7KLl)Um!G0f 3`/~ R4, \6ԔxG=Up-]Vr1ħg:L 禘^TU+pH5tqbjv 3`̀0ۋ&hmĥE*mSlm"?Fcjȕv|9o$U3LD◶y/L>)>׊B0O5kWSZ-l7I3`̀0f`bc*cp(k0.kE8𣕯yZXS0˼z!GbV~ǚ7>7JjG.^崄'Z|jb̜h{3`̀0f`1 U"~&|+Gc-9jcikF=Vu6KcU_Ee˩b-΢]BZթ 9XpY3`̀0f`Vb|aѪW-b5 z18߶ԿkZÕBSSb7FK}#^XrXX*/U:x%1f 3`̀X4_rob51W}XKk>p,9^vZk6QJj)E doTΧ+Cٗ8<؈RN ;F_r̀0f 3p2 ԶaU'?ZHE,m|j|sʱvb][׎Z'Ojh5!bH>R^`>j~\|0[3`̀0f ,M S Vduѯas1ͥM-sdpuQ7[%f9lucZ\V=)f@rAb<>.-3ʳ/5f 3`"1 WG ek>qi}¹^ef1W<VZ_Y3U6e\ٸ艘}3`̀0f ,hx_`o̚OmwŪW1>Vu6ڠYĺ;=r%%eO,K^gF3cCXy6I1V8;4䆀3`̀0f , M"1(Ƀac>db1rMj~_˼úYpHvtRr(e\z)}9s+'3˭͊qۚ3`̀0f`.Ob++sX>8\jkV,嵨Dkא)z_2D*V>F:aZ(W,\5Qԋ(TvTs&YfFVkW.7f 3`ڬv2Rer(8tV U*.WWK0X/Oc}J#eY"1vU~aZUU~ΌV=\Ï\_̀0f 3Z IJ奚I`uFZ]Z2׈EE|o;XoHBF_Xy|"OD˪V8~vyb>XŞĥU |/3`̀0f lw/tp2c5q~'GYaElf;oCD+x:|Y"XVj 밺Ä0p9ӊ}%SOl^'(ݝK5ib޾0#bܤV@D6jg/>,DLV-jè0f J#:F-9"n^^f 3`̀06j%6+#/Es_b8ʗ1=۩YNo8 Dfy5o{Ǚ_֑e[3`̀0f lJ6_(GۅSSZb-A4zіu\[u 76Z0"RC,[.+q+`._8Xz|Yۚ3`̀0f`;1ХCF"8ޘ-.qfcM̗\!Eb6#ҫ8/g+fn3jFL/f 3`̀X0Jxe,~8yq攵q6y2f~o?c0:UGa擣/D ?3s5ߕ+k3`̀0f`2%`r/(1`a1ό6gעvgzF,֛!2L%RX~sǚ'_´j=%7f 3`v`s|Ւke ukX-_UVW|SlYUgf>B6ue^}cVouX̤S=R15f 3`̀ 47G]i9S~ jXO,+6nXML"VizbCQ_5\G_51իF 0f 3`5Tb1X".- "Μ6,Ί>sI<ޤ (tŏ338kO 'ok̀0f EfM"pE\O8Z|jx߸#047 zSbEUi15mx܎,b-u50f 3`($bʗuX'Ɩ8u؈5/1P!ֹ-cksyҪͬ`23bߧql̀0f :Z9oK+eY£_03f\źfv<ӤWmqk~^Xqk8|̀0f 3`֋_q2 n71[;o5pbZI饲JTQ 1qy갱 3`̀0f`RfQ>R#+6yePf"֛ f^BcI%ƺ.0f 3`(5[芹6uʵm8jrb5$^ej~3}jZjm̀0f 3U裋jV_ K]́jjuoݰou1ϯ7%-wW]XW"F|j3`̀0f`2&pik蓗`ؾmfU*֛ s-ׅr1جŖ}e6O320f 3`@w{eOmyplS'7׿2 5o5MG<ەSӧ9f 3`̀ tޮu\镝o7Mk) ~8nkj٪O 5;km13`̀0f lDq/k8Ֆ, 7U7,)K]9kXCxa3`̀0f lSrgt57SkM818-ʵlk 3`̀0f'3=fd?i:POljqfKMh̞p̀0f 3'7f\~f>8;Us9vF\O-ؚ3`̀0f ̗b:lۧOM3ttFv[@[̀0f m@o H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 6 B\<IDAT8˅Kh]es1mA`jh[-E(FEaA!bIȐ*BX"؁4)NURZ!Mhjssm؋^-\gg ]o|Ҭ[346>zd ]#8Oݺt{5uIXN!I=@Vf=v1}e>;fvnvxaHrʪJF`D¹WZ]S%S)WAb |0K=So7D~\~q-˟\aMZ,S'*} F`Nnz674U H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 1;VIDAT8ukU?sg4h`G1 RQܸp%Bn"bЍXJ .4V iZ##T;m!4bP~7r>ιbwc;m;oӍAΆ ζZ^/|s{;yR=9(rtVoG1w#_ө{*E&!(LVuoᲵ‘D PG4 :&~*ݳreu: S-,U^E&JY[P!RB ŖޞʖR@_ȐdBfNvHf"2T]R j'B1ddAak/DIJD D2H&L`&L $Ex,6|~_\P $MH`I=@Z||ttvgcЕWTZ'3rje"ܵx9W> mb|byfFRx{w%DZC$wdցHmWnta(M<~;9]C/_;Տ#}o`zSڷ_>:;x컓?yݩ|}~wam-/7=0S5RP"*֯ IENDB`PK EA[{gtt#gaffer-0.4.4/_static/up-pressed.pngPNG  IHDRasRGBbKGDC pHYs B(xtIME ,ZeIDAT8͓jA*WKk-,By@- و/`cXYh!6jf GrOlXvvfk2!p!GOOԲ &zf 6|M~%`]* ΛM]K ZĆ1Er%ȶcm1`$gaffer-0.4.4/_static/ajax-loader.gifGIF89aU|NU|l!Created with ajaxload.info! ! NETSCAPE2.0,30Ikc:Nf E1º.`q-[9ݦ9 JkH! ,4N!  DqBQT`1 `LE[|ua C%$*! ,62#+AȐ̔V/cNIBap ̳ƨ+Y2d! ,3b%+2V_ ! 1DaFbR]=08,Ȥr9L! ,2r'+JdL &v`\bThYB)@<&,ȤR! ,3 9tڞ0!.BW1  sa50 m)J! ,2 ٜU]qp`a4AF0` @1Α! ,20IeBԜ) q10ʰPaVڥ ub[;PKDA"gaffer-0.4.4/_static/gaffer_ps.pngPNG  IHDRbmP iCCPICC ProfileX XeX]s9twwwHwI7" IA$T$D%DTR@RTDE$A_y?okf}Yk^S1)44A@Ppd6v9@(<"B,,L8?OOuS{zExYw ߆  |"C1|px0g|7J#H>rhX:`O`hP0V%y sĂBq(Ka@'s O~߃KP`l|%DXw>f1$=hg塑V__ehGj!&`:=t~DV 0,9WZ@$7+ GTx}^0 8 @h³78W|c8H K\7A+]!``u  !Z!QHR!=!7 @P-݁Ah z-@+h DtvBB lG>0D" (FT!!Z]!b@$9ɍG*!uH'72@! vd?)rAѢP(U! C%2Q%T ZGh6(Zmv@Ejt3=^D`0 A"c21eFL'f bX59Ħcca`DZM9'9q"\>nEFEOBfNIKCvll/W)b|?JNNCLnIGL^L~||' B!لN W"(@$:#ZbqIAK!AaDIDQJB1N񑒌Rҕ2*2***U"U)I jZjijs L:Ae,'M%7HZ^ZZih0ttFttg ӭЗߣg@20120dxΰΨxq+&SS#3sss+, EŒE>t7Yl"lVllmsgaQqcSӏ{.z.-@b^un6nC(Ja_<<<<N>3||dJ hXd4" i U =+  DE|EJEFD ~ecbh1e`*IqxxDDGI>I'<~])y@R/iSۥȈxȔ<%&ɶ~(7%O+o&J[~GAQ!\AaEOMRҀ2ZY[9ICJMOuˇy|IRm^KMB}^[QZWSZsIKX_Gm)pf:*: :H] a==[9}}zuyxNCaQѺqq ڤ䵩iia[́YY A0K RwVVǭiZYѶɱyi+demGibWk^>~A!aȑϱ dT\"#,R%vws&HF=t529?9"_/g3TEslsU_5+[]x7oVF^L{G|WĹT,ܱ2jkk> }Iud~ezܷ Adެ~kױmvN^^();@W7_y#pf1s`lB7Hz[T: "3pDFV0CcS9sևl/?sRpIr[f7 ][_$U J G'Ϫ($,"*~HTMX]P_[SMYNRgpc.õŖVgmls3N9pJtq ?}͋qWwOo_KΠ!SatQѤc1q=ILB)' NZ۞KƑ3Yn9yg >/Z+Fg)*ջT|DyI'UɮV_I-먟QJM[][[ۂGލw?AJsW{:zG r?o8nhXr'2M&LE~:c7k6мk7oޱ/1/3м\%]C~/%_ÿlo|>r3횝]==*O@#0VXAKy/..u/KQLT,O 6U5Or1նSpsdvpF8o,q;:Kq1k 9W ACއ~ "h"&e_<~=n@) 'ҖO?,fZȞyqUP(Tzδ|HIri𢡄/+_US[S7[:e@ &^o7Ǵ$s=َ{%Tw6uuv,>{0Ipȓїc?649Ea^a T\T}d>l5ɵş/}R[Fc?7I?mo2@!Ph.!LfW"$ST}i:hucadc~ͲnY5Ƚ‹cP4>*+*-/"yJ*S:_lUk 7o+*vCFfVv)Dp}'CS#5cISi!{MV56lN:;;9s?B@~'#,pwgtSQ QP:Q^z7mF%)8fN~0VavEuk~\\ܗxx9y_U{ Hlk!Q:5~ I )!irI FJ*(m՟ՑZ2چ:p+`ֈ``n g;\V:~pznŁ)wW٣an >O72Y_?A9! a 9lbbNjOLjIL}|rTiƌLݬTa9ҹr*ˈ+W=k]ѐ|mD\;w|7q8͉ɡm/kgK_e/$_4_X_/lJoqo-+eIІۏs{{_joo\lOPܗe<iTXtXML:com.adobe.xmp 691 354 T@IDATx} \E7ɾo$Y7}>P+x>0GGGGGGGG/fy}||||||||1_b#6=bS!h[MKd?yn/F۰;w[1|T`=ΝwGGGGGGDh,tbCp>>>>>> +f;xixGGGGGG(@GZq{D(`vz/GGGGGG tJ!fv? ömG²igtzU:^Ip-lbEl[ ж;nvFGGGGGG]6iV;nEaQ=f{t!8ȸuD'Pؾl'8NK@;"qa^k2SE;VfXȶVDv$'}}||||||Vx?uw=l't<&4?M~2=:x֊JWVx2d4㷗?E!-f;XȦ* S(/:LDŰ-Qtb&D?*ⵕy( CVfPȦ*;BO`)d@HUi-L29`-f94T^2z>>>>>>"ܱS^(tm%&줢\՗ 3Q̟тwyGivRѥ 4q;KoM/*>>>>>>GoW剦H<႒줳 OFͥgoɶ0٨#\1X.Od5ʊO+ ƖL'J\yqF U҅pqW_tA\':ifh \|e#*G+Ak~ #\ҍs“AW6.->ax7&Yɹ@KOЕCw_T&[ 8y֫WZ.dRDw]\iq٠CA.dBPh3(£< LǍ78s}!C,..Bd݀͞+]4מpL'%CQ5tu,+BdK6W ߕw DߕIYqpux&2Qdΰl]+B6ڍds2]LpמpL]tzW7g"I6 ;!dhK6+ߕw DߕIYqpux&2Qdΰl]+B6ڍds2]LpמpL]tzWUU۰aÜ'|oKN,ntd<&͵q&$:fᇺ wYL eN ;9K/K.Hh:$0/Y\E,qm&$ ]<ʹ!G8|q}-ѢC> t\pɺ6&92)q,Lf_4qш)7nsP?;V]>dύquFcx+.ߵҥCHjwgsiq,ԜCthtҤ9ym&tkQ.qNJKscK<.77Wm֬Y- d,YSonx1?6]F@t<+#`29s|eRPW\4'Mqv Ru_1ƄES2 ص&)wQނ@0nU4ʩғѩ'Yʐ.X];Qc01㦦Xs,\|ISޤKH:e )BGvD Rt.ǒډ֮]ԩSj`u;]<ϥg[*rx̛LvXQ;HYsqիLe6 ōK.K#lӖhǥEuŏ#sˆX64K6\ǹʱ/&yԕ}N-ImU5C\R_qINttiĵ~hO+C\sʮpi~s-l.NϿi#t63GysoQ6YqWמsI L1d\c6 t #F6S$><Ʃxd3Z٢chhk-_L$k,(Q \S'?.-C "I(\jVASr1/ȤQFc 4pN6ʰsL۔/ z_qƏ.$BcSOHSdC<9ǜc6N/y\C>i,h^eX<#]ͣuIl,+8U? S'3_Gsu.Nk(ϣάmE1Ҙ]?|ҵ^Rjjj`7 so>p E7Z`bL7|WKya`k qq*+C-*R2i%d\YX6\z*n2\Ʈ.;d/ZQИMiv'y4t\C?,3E9wHsɖh%G[j!dלDI'NYP4Ņ|ٖAH۲A>zUlSǎG^_f Rzi:ĥK(_,>]Ld\ixl֡v%Cp.AͩΧxrI4A[N}$ʘGͨڕd#mwY҇/\e`X| }v/xSԼ_8ќ:[hn6l\7-/> ߽x*;}g0nLPؔ_s* r~576'_IXi ?WlJG6+|#x;.N;Gu'_%G$C~8~.쫯hSX~CJ)ʍ9rqW[uIGM4aS8!lblo1R:خ; u IwiyGtMr%G\wJk.KӖ\5.qv6'(V30jj.ߥO]`tt) rnuvƌ/r.\|qhh;ٰ4F#dO<ؑ#/obwFR,+K|8 \{_إ+rztm^4tFCٶ޺Y\ q6w.!NH9I4Wg3Xn'"t[?Y`3+k$r-'+OcAœ_'1%/rw"< l7V5*Eצuk.>yNvc"V6X2rc*vj2qOXTq/et9d|MO٧]10[WbUxOp}Q}!f^XQ!zCh?y6Vi!^5&(ĺr5,g<4I,V y?4ُIn c?ŸU݉Ppȿ|ojgGxrƘ6X:AM4|Q6ƴKο3)8W7d1p`@F7US8S]S<8&.1u 7-|lSF4oHA$'[DB =ڣzuAl.g3(T֦صt1R>e+}S]+W\]K#vveis%8~p)P7SR"N4xy{b @⢋ƪMwTt)+m+PRVעx{ʄ/6f&EHq!YI_]~5ʰ'#yBEy^8%K.qFkGt _QdO2%1uk&:iC6cX?`C]wRsi㘲s\EM-d|,Ť}7[6ܴ?\pwq>Zf_1S_{𝟮ZLw=h܆?rfo[<|2כݘ qF5(fK,6o<;oSpͥSQj8¦vFally )(ǿ /MoC}ļ+8؄ ۦ53~=1J ~BaIofkt0c#{%0}nqq9)N^)G=⚇4]9ld'zMrNB8siIwMwlٖѦ]&#moXؒF=b@d.fၯ$n5 ۟ 7 8qGΞz Yw1G*P_u?]_o'uJm4ա|ѣuq_\vLtY{ћ_g̸罆$:kgG̏ F3~1>N|,-z|Gwᄆ/N5[g݉S{Ucq+FΎq}ƏʺcnK#gSнr)~d>ߎB|k/߁u ]?ɱU}͸櫸oW oi_}=?j_V:nlٛ4MX֏}׿:N= ?}";?cGv݋k{2e ϟg}_wojcl o3L$QR_-|m]A&Ŭ&ׂ8vqE#LՓ*.F;0|Sy!,G ʊQTR|Cb+fά]@a`Sju`ƏH#[-ENAq6ީh,S~j IxAQ.چ.1J PS`=?kwgy_N ]K(( ^7>qm*H煏/rzSaGa"-8'@A69/DW+G9ڡi\lhNҥ+|/lԕm(^,OkhOِ秾uq@yvZZU/aj8K@?DpV5׭ ,7g>uvۘQ ]pgɷJj? iѲO$V}n#;-lhϵrZ, }d |b8Hjc8ŮVA<S~=*Y9xh= YβWK jV {%{Í7Qۼ_^c"F酚j" X|-[ySN'1{t ƍkso$1Me9s0fg.tI-a`[OM{5zơG/u82Ͻ81FkrQZZ gFpc!~}!,[>s? {!BHNF:q\ȈIdilQ\ira?)kNwM.M6s{P`wX)E>ho p i;1[bH[ߔ{&U رgcqzL!PSlY۷b}b!rlW>s}u:&#>{w{\xݾaTUC=zgkqۏ_rg4:L7+ݾ3P9{wc>?·G`Okw}{300ڵ>4C"mƍxYbď]۰ YdٷunWi'/&go5u>^8oy٩F]?xa#߻wNӃ0`w|X _ٹو뵂xEԎb^B>yOƂF $f(F[3KUئbp:-2l];C:mQEtmQȎt#:Qu8r?e;[c(/G'׆pgX:,k^6sy)+QrFM:#^],hw̳m+,By7f?нsvg7dM=_^~̟U/[gލ,z(O[x͞ﭪk 45Ԡ֞lʷqq϶(mA᝝݀wٳ1ԐoEPP̚6EzIx75æ;Qcgz p'+J?kf="aSoD ϶3Zי?n+@EnZ:+t= 4>b/ m^}ѯM`CWcqϲ1lapWfv!A5i=lvoيfB2{PksL7mD3ށ+~0N;KN1a{׊y5\=yW⒓s5b]DwQ:i_Pf{CQnn9sie,_z0x`w]sg'Oz4fA!pbcclxeI8$Zආ6} MlZX&dK)b|lᮜpͬ7ēaf,K[|UٟE{ՃbvN}n(B<v6.]ka-*BdVKގ}Y1pȤn $mPr3w=&De[̖hpmVN`./iOQ'೥),9xrK6F_<1QP~̞'o̱_"9o(n~IOI ;< ?hܹ O/[1# 5w٧- e'*Mc{#/vŇOsw|ٵXh!6leY{BQ27suǣ@\rj  ڴYTw7ס??yOsIR:fsFn5ѝZ--; b9޷=re_ݶw;SSX[d뿸 6<(=ߪk6{v`7Iʷw/{ÿ x ۰݃5rMnlf%tI b_&q[DcٔM yk.R69V']']SPV4oy`c=6W |A#~7&RL 84Cp*僺*lGh.qȱX|n)f-$CpW `8|̀cӅ8uAq 8}1^Z v_-4svl^jQOE~iYxWYsK>k_^^wW?Mi/nٰ~!>t}4/E%u¢y0t`m6ݸξb 5']xn]y.sσb  vH}]Ɨ8zOOfBM==@ mHFkOc??J4hZ?%i_sƱI<ҘKWӘRǻ+er3Bv{ M&"\vǷMx;X|g~k(4\w-zmy8vzdxn8kpJxWwi >ޏ? >O3VZ |7|$ \+bmO!z+rC#Gj|rjwaE}i= kYC?gZԣzbާ\3oWw,Aᶿ{dWN)Nd ?g' F;f̷}7Ů ] p_)R9._oc}`{UCk1 : tp}xq>RĽ~7ڧ\e"! /vv< g 7cpQjÄ}k%wl nCב`z|g͢PRqF<8,{"{wm  BYo}[|d@1m_`/.ǖ5سwK_1p룐oclZ;mQF'i'%+y|ʺ &>ǒ<⻸x>/ҥ<}{˦i{Cv(C~DOYOwj:HtgBe#8uȣm*{Sd_0㔸ʘz};% qjKuג]Pz!:( z~ގaEӧw<۾*s1hO>X=n!P+ź{+Ǝu({6+ CFRQ] Oí0_'ri@߾Vcy/tue#yvn+ Qe_*4;q}8/;݊X7V5U6_A#@{> kB 핗 {?6ۂ&aAPVu=ĘUo[s1sߥ 蚅M5ַl{Ɵ{:f3~iy._Q56lAaPll^n.owZ`Cͮظ_GAXYekdcؾظ"8+6~ٔ?11͜Q#ߵC[lZC93ƦqL:eIsɗ W#\ %+}87ۑ^x!\/׶ў1`3^v6O6b c0\cs wM$-Ә<n'p1S.fuB ]F$'d4L]Y.mp#sSEd3N*Itlnm8ԡ-PMMg2m EWUU 2QO:[P8 e{<}g~ӑ͵RkL>g8/碌WQcv(GHG|4'f6Msh+:&sivڠnI'?Uhk:ɓߚtF]vٺg?O#,/;s|`3ۋZٺLey4e[Nc;mz8w&/'N n~`c&9Gn~;A ³=|îRG׶΋Ǘ o:wq e0Zokʋ,{:|B={G0R埾gu±bο} j9x4T^4Iz&W>>h._4>lǯC/͜sNM_駃57鿱:%Ϳ2ՕV ⼈p,:'yA]^ r(9ӶY Tbh wSC'驺dTJOh!+9Ҷ;3Z1"ʋ 'YHP6ĩCM˂42k#Fu(5g_veP<)0.eI;:ɣǴC=e5(ǵi8&i柶3!g_~mk-*ܵ!; s JI.Y0QV>yEH-YG;Tkio>rC_}^^e׹~~XuIO1sFQP4uK]5ElCP8e5(N-AAPŧHOiCPڨϹx]6n9w49i>.;ѭk0ּ|VK[If _[K Ϸrt\~m^[f1l5[&P1,DРŌ|AS.tk,\іM:no1ˉM4BIKF0*tr+A1 xL8/naM A:/,X /lS:xPt^TI'M8ќy )KڔEqdcڦMO]P@О%K}Ӷph<q6I&68# '(q$]4풑o|]\>`p08M2za2bꓧN{ҧ]YWE\E ᅈ-0!?-RVs'>>>>>>D!Ptc6Q˱dċŽZh b/hң88V,˫HVr'_<6Dmk,ڢyO&kL%Ӟ5Fc#1j#N.uƂ%kGa2T4@?^9FiGyѱBynl6qKSèٱ ¥^ҵe,Y`~eF5`¹;w./\}l^on5Kء^C%.\nI`寣[VK_:j;~sܚp!⬓؄gZgINXxh9+9vPp.`4-@Psh,( #Lգ p\-q'w`԰q3f;-/!p>cq¶hm'~o~:L~kJ7v<~ffÂuP '9$qH?N\Wyԡ~홊wP.9J^GGGH[#OV'I&2l_XPN5hPRIO _-Яϟ#tX2ܭ&åPTĚwhr$.sCq%chtu|foGGGȊ5r_-9~(_5 f£LG:Zhxno0ig0ہ0}\0Ղg]!5KqɞpN6|w7Lzqk/k5&nu_Y?&V4K~mxiu3)m֬/_w)fGUW6`+xL_>;xǯ_-Lg?`x|͞鳰.X>uV?~}"?^s 3܁ >^ϡzZCxus r8 a /˵7rVy? 'MWauZc9,maF~fζZkXoY!yp|>4Y/ v$he/wO$}||H]E]6podrh-'2Iv U}< Nb]X\Wݭzo}> C~G[kxO>O>iϴ>!X/[_V To*x'q׼MLf 2ձ%s6UT۸:uac[3gV^ȑW>'f7rc7?6sl{0? bߞ%-7Kljƛrbt6<'V]_{tٞ9Yװ%"eT*Wf|qzfvMٜw[!ERNuλtfĖo_i9TMy^EyڟvWu&]CZ?mmۧ]Da%-ND|CmAT͗K*KCOs UH:Urۻ}vο=:Vm9a̪{yb_}W{tGKDv$x@G":(^.b}:L묛X?b=ź,[Xc=ƺ4kXc=Ǻ<{Xd=ȺjaLo%'(7 oJV5Ŗti}(ߍy}j^ׂh{-JYc;n10%"49; _Nv5CVar Qڽo8!YMX}z4i4$]]fsp)o6+FIކk/݊*2HHk~:ͬ})g}LC_N9vCxJK'uxj q5w%lzcI?Kyw+YZ2û:0@'9)SQ)<4gghO=cK)GGGD9v"|c6J/t( IKTO*'Z̦St D2┕mӸ5l^+$տLjo?q}_c%q6f_V͹A:j5n7l)lyOtg[?$xt ,| OWT#HI ~J n,g7vkyG18o7ߴo&X5'} ^{S~3ѻ3znt|"y|"|(P76s'-vh.ßW#?QM 'T'N:ܦSNKτN3:9 \DGSx@G"H㎣x2~"z HgK[?~ɟ4w-3QO+EB.)㎉k%+;,N2IZ)iI~xp=c~h z_v\ypt/+\?L2YJRQQƻs7֠w^ڀ56)1=kq,=d1{#] 5A8gvJ={rsPVtNKg>iScqGڌҵxd`|S(bO~`8洳pٕ?}$yS޷NAe3c8|I?_ -Ѝx"x|S0Plam?[ůp$<?#ְ~vni̬~;*+P[ݺ,"Oypf[bnZ3@#ȭD#Tcѣ4]W?&^`g\ѣ4wL\vR$)t}jO=7_򀳰걙|rdvSt乘9& /R{/ׁ^ͷqx] wI w28J'λfc{?/@+>brjIBf@-ns_/ÀgaӜ0׾.yLq7b=ت/^$kH',Sso2 | ~bB\j$/x#K򫃇ϗĢ{:S17cL+,3$ R"-R{D¬G|||:+JylLHiYIF˱ CFv}=j});.ee'$dTnn<0. <{&VESx0k&~Ң[qMŃ]lVmfC6!9>Wo` g~Tj|zmrR(/ j+wo/.}ut7A\|A1ΆxGQ^ȞN>>>퉀qכ2,v>.x|| ǔ9&P]co'bVE[̪hUqTt w Wc1 ,U=c/N z%ddMNKZ.i~Tb0#w[(8c~/f/##G6'R*L ]U1c0 Yw,z  ZX[Yj $qB6ŧ9&OPƄnw.]X鴩5TM[ <s6‡Qjn6>4â0r(s?c~/e[/G਌߲UՅ*PSȗ[6y*Pc6QwfY 'T'O M6BvMɣ qݙ0)'=l웏*V` V1-ZHXwfE$Ef*Ŭ ]ƔgL, CX&kQ겥d]ICwKzȭ~x$s4Di>>lENNAuuDs<\ٷʼn W`ֶ([˶;J&l-ߍul"=J6_d[#|w͢4wm(\T/q{:՚ kǥEqpe|Gh+^/pdC9x/vd 1ҒX;axdgN#wdx=벃||fh(pNw}||2@ZHuS*k:K.k$ N$Lr£!~FԈ+r>-̷Z0׬y|M`l/m'qf|V6~z`lY Y=k v݇ͅȩ#P.5S'+7ŇL߈  qq/.'h)ik饷%/Kr8L.֍}yO|r+|5 077bEz/Bpq-#n]z}6ӓ=0Rj(&G+go?&+@ cc1`\|67`o>znN&wޏz MȂ7q>CE }y3lFc+њHcx¨;v]ۊ;\[U;U D&]ylVl1Λ [sh//ݔ 6e'[7bQ}64cUem߅V~ ~}|,ۄ:كV%U~ذa#^lRP҄ uߗFmx1g }e tN?y#G㾩pݰ-Xd6OWKk0kmَ+=uqx̉r^9f!}yIZXn uY9[*Wƞx>C'K-c=g)xc1yWWm>e4Ta}{kW^=e{._:۷Y<waܚI`&,;ޜًik]]I O‹o쓵^%T7.Sh 񱁹<"Z⸩JNYA +qvS-%/}{k.:]Snjy8):*[G 27}VM|AI~!zڍ} Zܙ lB[)] ^VW2\4(߶w.܋-l( |9PDK\/m7vtћ&G{µ#8/uVBWc*ovvYCOLK»oa< {'zMa/Y3_,ڵܹ=###hmhɠ^w:eGR(% D<#0#TBo:X>NaXU߄ 'L]+Ż5qS&$Us[V(;W1? N9xy3^ظw@qb."ؙ=C57n6I[hnnG4J'p԰Qxd`@YXؼ9JHG9f mƞ }cqUּ7cϡw2nމpMxбyd˻Ιe;ZҜш  Qjo$Z挴ZJ@$ZKXCE#u&^agt µ6;BA)ToWgOo/d(,,=pVZ%=yV650 /o)kff ^nY?|xf؁GcX¡Oڋۣ ۍ);v2 MK6Y]ۭ{qiȵ*SRRf-8/W6zs J5%= K r$NInma2 xT/~Tv؛lEmĚ52? _/l!1xVKt TGVy0j¼o~.h9V{sA3u߯{lWΟ4 o&>¦P?blی϶д###.@Z ŁEUw Y_5d ZzĄG,9+HpBEK#].PQ]Y7ADQ#$8IͿ;YiǤݿ8J2L5Id&t,21+$jk4V>P (T*>­g{y~ڵya, ?,˕7K/ᠵZ*̥3,M! 6ꊞf ! ] x~>*ܴh`ֹ*sJ[h6YاTʕV;?NTY[?h ) :_4~cAWz` N NВb%.aO6yYVs`L`` 57ғ +2ղ\ʯהqBT ,òO")Ep~l㩦g*]L8VOwǟźO5ҚОj&^(>C'kRU@U8K?z-#bpw[;ʡ d ߠE2И8rvYp`L`Pӝŋ8'`"ly-B.W,r/OYjo9ahB[ ;VZl֡ }ܖk.ng?>n`2KCVWaUZdò6/[9^又òOQweVt7]_Y_?)ۈQAzOmr_oz?kY<[@?&pAzɎ 0&`L Hٰэ[ׯcuMG]&6?0J}]!,6]\_˺4ND|X||)ӆY3&A -N/z:uCBjlǞguX:3sZjO-7ZhT6-3;3tJx)F`,:6Vu#(nRG (nrJX 3֎l||8b)h&ZSvk\əخ{X Xo\Ɠ5t9s<(Ϭ&tuexr&y4Qby09/S="RwKh_Wyme#ǻ/-2U+c`L t2k#r癓X"3ktgw4o2Mk^Ʌc: /*š!u]2 GC(0o ş{,_!]In~;GW)=_.u+>)Iə>? X~[h_20d:oo]]nGN/t̉3?OK'O<:~ bb/vZRE7Cej2&Bשr/=sOZLŲ"~rYQɑݙ1ȊlEb9[>. =/etZsd7Ҋ]vt,'b-7`tRQ9Z@k TәH5V9.R}k4i-np >r sEfT,B3F",^ &NeVܪp3A2;_/(kn8o Z)'`L 0H@p7{Cm55Rr%#(ZI7F|3$ixWC8GCZ.Y },Q޾ qiQ1\ΒlYܭzZ1Sb-Hr2)&<95~m Ny0͆ҋ7pB{e#`L`+ Fu"Y,䵬/cZ˯& x3._<*tB7:k>rĩAr zP)B*S)ZA.^S&L;mjPCڔLɗUx .K@nŻ7boX)"cL',O 2ɼ\$,6 -yj AGAXb hĸݤ;FL8|{EkD*v:5O(2sg+SL 0&08sVӝpvrZxV~D'|PLaImƋߗRA!t쫜+Hb~O:Qs5jQldk{{9j5ƋK:cor5Yj2H?%HkN5~pӛ[dYvuoTL 0qΝx, ?GaYN/G(L又òOQRZ8}'yR_s# P/ZXR|6@;-B,7Pm뾖5_F?tMo.2QL 0&0 %;&`L 0&0 2; `L 0& 2 0&`L``ev7 0&`LYL 0&`+vL 0&``L 0&XC gL 0&`e`L 0&%:n8`L 0&+<`L 0&,VfqÙ`L 0&X9`L 0&0` 2;``L 0&,&`L 0K;tp&`L 0Vfy0&`L XznkD1`?5Ҍ(Wo7ziz?\>`L 0&-np kݙ2/fkǧ./<7 O&]b*@ÈtXdadi Dx/Q!<ʧ% OKs^o-oJ~bL 0&B%WE tQFbt_8 $~ h%`˂0Y7HMJYȴ7!ʧ% ^")Z%s &`L@^ F(nHbr5W{!BY8kj.-ZPhC9d x҈u8KX-J!ϛ3> Dk}];.;63~?;Gjo4dEWQ2r;тn~MJ\ٌʎEhӒ).;qۢQK%KʏL 0&@7 Ze5J$3 8Hl^U0HZMlƎDJTY- #j(rȊ);U)?:[GIb fXiaF|{d|?A@eZk?;>fq_Z")7q&sE:XLFę%)kXVmMM5+Sa08*5ܢצd`وQ Iעnd_;!?M.BE+LXZ:9- e ђÏL 0&@:02MY I]3d CL4aE-RMNC6}H;TQxjʮU 7QЬj誤p9)R~UG{~T\såYꡔnQ6] ^V>-YљJ;XRe41d!0&`A Zel$47ڿ{_g$EՅu:?%G"3$Qt2GB&^(zRH$m8Mb\iɤQnv>*ßf?=hR%+aL 0&KX8!f$^Y*~dW,K6RE)^_zSƴdRzwK[mn:9P:&? &p8j'„7Oѣ7%xFtǫ`Ax2bݴ& z??*og4G𮔗6w$wk2剉x),LchS\iD*cd0 [wdf_&`L6sQOS/}Yʏㄯj ?,tY&r_NK} m2N KgE~+!Ö9] LjŗsRܤ@L0wE^EK"*H2i3-2OKvC9#`L 0'@jo֓Ļcr9,?WRqrX)JJ+|'8O {WN%f1&`L@AY ґS=F@ϷYO h{\"`L 0&0 jM{`L 0&pG2n5=1 vv:jtv;!4n(륖f4byTYhӒX|dQfJ8 0&# c~)ugK:_98hqQX}_6rVWU,;XEgL0"j8;!s7.]9 cT?ϴiBiGR-\::;F[f(r&`@_Mр]GǏ9PTN+^h跃rReLl{, 7HMJYȴ7r8!ʧ% Z0 I^kL 0&7AmXY%P(nw5W{!BY8kj.-ZPhC9d x҈u  xa%,W(|#M6$vl=\wltJ蟙d{5FbY9D' Eǿ$,B[[{?9q"GVwg8ٺ,qO>걛N}3,֋8'ʭo$7k E+!h5GJkX= Ot,{Ъ٩x&`^@K]٧&aqXӁv:[VE?F+M_е~b<7d-i-Ԓ%'r ;s+tl j)r6;|wk㴡|7hr:ګ'Kt2[M+iE*]<ц)x++c&MI(xق\9Tա󑫝v,F#MVtU*ua _97~g3*;梕OKҌ-:Np 0&P%0hY{%+0# )N\I*;0ٌZAFPSv2S~D7ؾu:Aj_NgB7<_D#)ΈoϚ'#7ؑZk?;>.v0⾴DRoseaRX g HpcY55ѺL`ƃ̖F(u6%&Q Iעnd(ɗDNFGSKЫqʧ%S4/hVu:9-~[x.)tf; G&`L[:ne:'@VH&ԭ,CL42ɢ袏!>n$XWP(<5B^> x&XAM75Kfb]v b̦:WI}T="Y7|VK(~<4R݊woDCڴgvb0VЫrʧ%S3B-w=`g-pamq)䉡YVY`L #0hY9WkBUv\ 8(qjt +2Z 1a>]̑︉[nb?o#%jZk+C'6-8,+O]Qt7LuUJ hաVl!؎+"eI5)}$i6_q1 Kw`.ZdR*c YOO'Z:0=G-ӷ[`L t漛~rCH6?=TWɔ!XlK(*')0QSৌiɤdNtZst:=s5L E"h=KǮ)RFᕙ-(Re &d2bc2=ML 0&@u˳O-=29jW~D'|P+La2r\/(]@gw8hcwN\:,x_qXOe'au̩EK:+֥ ?W(ϟ,7YR & JJʤ5ֶ[ʰV>-Ym2m cL 0ACԸRgOG#Pi, _#KY&Ga٧()Ep>H<)v/]h&P^RkRU \fis.`Ha4ozm nZd7 - 0&]DUMw`hBgm)OS zT. 0&`B^P:r*gN>οgXr)L 0&@oՐN. 0&`L t!ʬֈrkg[u f@ٍ:ଗ6hӒ۝(3ܶp>&`w^f3Na;_?q/oΝ{VWU,;XEgL0"j8;!dkqObX_0ByOKJJ 0&`%W@e4 Fq."-.G+MlY0f" ' Rd3}~2 x.Niɂct{eS0&ebeh X7FgUx|D!{e5 5yIC-oڜ 2zY] 59b"*]t6\hMqэ.L-Ւ%'r ;s+tl Ud-:ϹQ@^'Kt2[M+iER8PlK c&MI(xق\9TPoiGQ3 F#MVtUu$`ۆ_K+@IDAT?7!nAkFe"\iT6FSfG`L 0&T Ze5AI$3 8Hln WNL6cDD"%*C59dŔ̔ o|tsGڧ*i)d<\!ǓӈoϚ'#7I-g'FܗHJM A'Ng HbM˪ֵ`ej4 3Ge4F QkS20n{-VYO6X|ILlt4E |Z2Ecc^~U#`L 0&BS SI0XJc]3dݒeF&ݸ梏!>n$XWP(<5U 7Qc=S)T>^s vVkzLQ!]vYG~Q?WwݬU6(nŻ7!m3M{1PUhӒ:1,SɀL 0&@w AtlWkBUv\ 8(q¦t>R@a!&?rק9r273S>V<|T?"z:k:0=Gaub,/> ׯEAEM)OE}D‚?Y,buo -0F#E'`LJd iW\V>-TycL^`L 0&] 1#zP ^&S`Y/(DGM g҃2%ғs^Z :mwíF0X0 &dk2bMCoi)'.aiW*d ٻiM~T2hJ])/mVOe [Q7/OLģ'Nae*GC|rrʧ%E3F42&`a-:LS䰿/u"^r>B2#DXNirt᠍]Z):!s }a=8a2'2V)`\ x~>*dI5`&Zi#uD*%I[hma|Zpe`qΞX' ?GlaYN/G(L又òOQRZ8}'yR_޻LO 1!1GOP|N0䥪cPPњjzmJS.$/:M.4՝DS[m Y*h&+aaLuVaߙ_P%5hw8Zڐj| cǾ][CʨȎ ~ml;}{un, P%ӫm8}"蝹R3&@`eG0v1 zZCVn_K#YdF̆am[M?B yYG Xn.vj`OzgD˸ &&!h'nNRw qT:+LY_ ]eFqaMҽDص/^; 'U8fĢDәWħRB˰po#j5.y2\%pŠRuc-0kv'''tήbfbҗ6P}֍”o<+}EFX`k[(11z;S[6{_UX͕b!z_akMW6`ρ71rQGbBDn; ̉ G.Za.Ҥ <ᵉyC#F#}+x±ݿzOEi%<+=t2&@?&LM];?O;?⹌^+jiȎo ~M$$nANq0vg=ZͶR ߱`w`cuڲM|jbF@.Pף!uȦP,) ͤD;t?E[`sT1{Xy)Qs=ڨvF$-ǒ6u@װd/;˧Vw/{ΜZ)-oz_ĻA|J΃iүVCvMK;C72GXWxdWlو}6{YpYӄ$;_Fº1l9-yn4;o\o-6bLtGf;ÛgE 0&=msLo~ LLWɟL83"/KDVF$-[_XE3Æd,UY LJ?ԩ݈3& 0݀(aN$ZzQzCԵ=Tv̈H8iN, +[qA[Z >zd>F d#]59T>HӾ 2Ӗzwnw+e5~]2y#ҜWdO_jwqzԷ=KgK?FN!&xұʰU:bĈHQ\Q"7;JH8K{#G~ "Lk&@ho?oND lI~,WT.Ta}kk$ S$c8RD廹JC:8O8a>jH$40 uHaQ*"X;+o03k3K{⯝OZ4~bKX3Hz2Fv]ƯKSUw2Η`U:XtFfI߄]YI)_w֡.+FV%p~e?=c6ah,uK$bGZ>~fL 0D'uԯmI[Y R90L.%o dOarzY~%.U!%\Wo149LcG˥^Odsu8h6`% {Pu>mKÌorEzLIz׺T< NNGu•Sp%OmNя@-:k09ٌ # cN5/@mNhbGr]k G<7) JHw NW;ӛώM_an܆_7v P*8kw(̺_q,FјW܊$ՊHLj;Oh~nvvou6CL 0&0 Ze6"f Ǩ 1LJq"ZfdlEI2]2ҧeD%:MT:]HJS{=lVkǟ˞5({ rb=RkzGr|۹'ȚFK^S,yPo'FZ?2N \{y(.y)tڰY㧕 z,GJk"2ЦdcgbdǢ)˅+Gț6`ñ@,-eLn\~IY]dAcvvgmd 0&{nݺ{)a_VD#8}ěB!X(L4& J/JP "ivRP#]eiC}9w_˗=̅ZX霥1D7-4J\J;6\.3kqVp d!w.#;GS[+_D,"Ŀ.k#K?]4bW-5_\Z[ʿ;5̏L 0DԸR{Mr#^i, _#KY&Ga٧()Ep>H<){ HP%v;''`UvdWK'#>l~4(v ᎟V>-Yƺ8@Y,*,A X{/78rg]L 0&З2ۗ.am]H;rhN}"Yv5{Y<,w 0&F9z/smk6S[F~~j_ǻ5ϳnr`!$L 0&`Lo-U-.X~:HgL=wFH@q|w i 6%d]~a_p/!5~67 - 0&(Ap{)c{_z39:3swt_MVcgGS)UU*q/|г6y>rˡ_Enj j{Q@' 7mlur~[S)ʏKtݭ|R\mTMfzK?-1h*mDw`j}-m4P wj[˟3] <NY} OOsj s󖠧5~2]-m {ej2&x^i7b"L\A@P7rB*FX颁Н ^DzBoIȶ=0v:^R'0>#A ˢujL!ARB\DJouH@IQg N[31k˒6p:Eqf.D$z3&"~X'q>Qh\U tB@P5k ة2_i1r$?&׉XRsEklX{Z@ڔWp=՞W{L 0&pW@xWu3p"8e#Y `}|T߈XFɺ<5,YZF  -oӀZIRKQP\m׆3K? C-JEE \h=@I8],)^2, B/1Q:| iR^SRJ徖ҋ>_W'kթ+=;ԭ΅(K{O 3Hd_iUݝ/Z'r5G]bژtJo)S>1&%Mjv;-oyCȪ54-Q+ f"$u6VqF\]k58+^2_nTNK*䵇cidsW}GA]L,, 9K".r$.SXIN\*y ) ?k}ݫ`,^"VBjMK)Ox3ҏ3 aiσjقh_`L"1U]Θ%ggivϺCro®C&$XO/XVDv.޳p^j]T d/03U^XLDӼЊ8)q8@^1:<"8KZ/-)9uW7| ,\9$a2)ս`?/h3goDY--}!%>1,y,ZDڰ?|;6v%u^,m/ċ2EvL 0&0xU}:U+LvT]0MCL-"z).DW1tj3jK$FJF\)SCHD龏PhurTREB9WEM@7oUņsU"g;w mm7%k:IɗObgPץLΖo>{HH }-2Z[\evw8=`L x|tj9d &p^J< W!HG\8}ѱAtR>bb$Î?rEF{,>ELB^\޽ :r(UB$b1m:~$mzx`L()':]a {G2G}Z*}IRv9zV6 J7 Twv)ݴF<:jҍG*l:CnGײF 9 [?x<ƭL#C5|?Q|ҝ! SͥhDzL[3&!pϭ[4;KwޣH }&, _#!X|2r\/(]ӆV /Kh#%tDiͮةtJG}h#i|5Y"\ײpQn7z<[Yon[8`L 5Լ/Tr9,?WRqrX)qr'nhE~9ãBŪ7\XRd#Wobײp;N2W(L.25+d!`L WzɎ 0&`L HaF3&`L <`L 0&XC gL 0&`e`L 0&%:n8`L 0&+<`L 0&,VfqÙ`L 0&X9`L 0&0` 2;``L 0&,&`L 0K;tp&`L 0Vfy0&`L Xء3&`L 2s 0&`L``ev7 0&`LYL 0&`+ޡs5UZuZ]m6*\)AMm ܽ2mĦhӒiGkN1~V4 0&`&`̝W*oPFYG^ǥr])ɖVnڅ561̹H>OnFю}Lk(QVS[k?]2;XyĆN+L$dCcqj^sۼs;]fV-L 0&#1 ztݾ !49[C~ Ж;ӿju+)j,v<Y I5WCd{$jӒi{ (K!ѱ1rRdGNلGV~a8y-Z8Y`6vOGx瀳Kߺ>njXh: vTJhmpD%uV]K@.~BRXV?cef.D$vo3zF|dټ?Y.arӰ,)Cy {5,SNkp1쫭/5[s9YXcF+HdurkHYfxڦ+p!YQ|uYGQ{EX0=խh@~ZE5ц^Kp`.ZApddx:vJFI 1izicL 0&08 ZˬiȎo ~M$w %pzT;jfn&;lv$Жm%Y}@y_O \FĐj%EV(5v)A(BG˕:&_LK{:Ju.@ lJ8~W#/4Jv'%13f!&̝%.ܣXC1)S6u ƛW>Qsuq\ɺ` 6ڄz'CګgO 8Jii`.ZdAzOf*OصD ,|,x_]!PN`L`ʬvFy5rF$B4K(=Gt,{)ɰ) D 8SVUY L"*׊Sv#~}`%fgy|Y0 ?@re4gYNY[muEu,bBX<9. Uc!`L 0AD`*(@=\^Jy} +q"*ct> R@a.vGDdcnJ y6ضػILKj>RdUj,NTflltzsᢧug*NJjv$vZ(bxNg82͎ iQp TIshlKַlEhHBV>-T>*ц?*i-){zc݆zq ת?wh"UΎ 0&@$o엍FbŒ()DJ. q2l:DOtt%~&Ch<}`t T݃\n];_S•=e9_)ByEAjqU,h_~F p;,z%GR2hp=MS%BISh?CطwK٦cZ],o;E(XMg(Z6딋@\iDjR{CQ\-\W,q=cL 0&uKrH9jW~D'|OLa2r\/(]@j5T Y;)ƮYsﰿn%/{RB~$ g)>uL%-* cImL;X`L`PPPztL8@Ld҂-Z0Y{. 0&`J*ij5rV٧F 0&`=I2&`L 0>'ʬ)wsё_x\__@͕tVthӒitD{\4~V&1vL 0&@Wˤ*oAwµ5DW̎])ɡ-IPGζӆ5 Vu3vL5Xhm|j*mDw` gV>-| 2F&w6 2+ƴ :Y`L.#W]citiBOoW?ݾ ҹn:)$/>7INgvm Iu}/m: vY-~QpɳpUP|.\$V?cef.D$ 5S)_qs(Bbfbҗ~Ud_m}t./ƌW:$#גTO>2ī6]ـ=ޠcȑu#Q.[SR.=!eOKPMz]{ b' ʹݱDW樄c|e1RNdzR8cm)Q0…s{VzNl┍dE݂ծO\huB-W@//]jI!Uس ħ̥[JV>-J3:Uƨ)ȕcg(ڼ 7M1zάbL 0&<7i JNYjH.2Yih\xrz/kXFq" Bs,b&%PV:Su- 1F%HI])]VPh",,A@E&*GK;YFs&ϣ ,H6).6kn`Yv##<նh] d1YI Q9O nM.@JU֓.. P`L.& gwqC뚸cJRR:N2#EAVBRH)4d~IIS:5o$]PUfEʘy, @݉-ʌ85Z)M*J(Yg+`u*!۾k6͔m)zU.ZddqW?1r0kvBN `L 0&zjUcO`L 0&`"ʬPPs55{-n;f~=ZdqQ;Z[m)X`L e^2uVa;xҿG^%bvbLImIB(*}/%Q_tMۺE;^)85X1b=9&aW-,y!ʧ%Sm;m N;Xc(Xs 1j/ltt|< `L`+|p02҄2"uj\r$H ɅUY!zgTj,v<Y I5WCd{$NkӒi{ (K!ѱJ"WbmGC2&"eL 0&0( jlù?`W4qI w8+k_wN#|q̈E 3` Meb8 < Yu.P BV?cef.D$ o m+h!qΎ=kpښYK_)S ip1쫭/γ5_ uH)F]#nJnc,K mӕ s :YQ<eȻ0%ţVF=OpG]@?,<kgx-h-SZ?ddxs165u٘ap>Qh\oZ`L 0̺6KބWmriAD:3UD;jmk&YҿcFW$Жm%Y} ] Xϯ'#bHŒ"+T j=Ŵ1eЧDiICcOG?}Թ-9*=,_Y(ٹ Θe0wc1̡pŒbRdl$+7v}XDjmus zk{{=Փ!UC~|\4~梕OKtTt'VWn"{];_h%cb0OL 0&A:mgiW#gD"tHCtGt{)ɰ) D 8SVUY LJ*r8uj7#z7|Mf?0BE+,10>YNY[Cb'ؖ?r/f FdO=y-|2 >-G ,Lw>R2Xȱ1W/?u`v6uv(-|B /iu?Z uR1KbUW [y[ax_aͶ_=1[dl;$sg6m l<`vxSٌ84^bu>cOFt; JjvG/GL1 Āe {ˀac1?>1 bб1̉5s !/U:9ͮs=b;:k,YkOcK7k:.CY{DJ*/ؽ|N ]NjP+i_*(ƻ:ie~Şyuya;Ayab@ F2Ƹ~ow᏾i>93YK`F:@?DG6nJ-p3X>F cNf}wCA*/Mf;3GJSe\oϼX&!m`e)g0L1 Āb a@Bh;1|~!͕q[j:&|J 1 ĀY hbxC?#G gM ŀb@ 1@j[C11 Āb@ 1# h-ڱy{-|dw1WkVz-|{W9mwl{xD 1 Āʀ3(r lO}3oGpXu$dֹ7|#6w=4?|6ŰQc}Uö!y˦6_b6/> xזu^i=y 1 Āa4 tDFeʫ}i;['˺+|LSU[Ij{mTxYGP!{ sRx}je=? 3'oa'U%&./w%na&Z |m3mILJM$١WSϵ-klKuWL 1 @_`Oo] \a%o]C;C|۱.Ū&sm﮳G)ȁi;zv;Yc&{d3/,|g?>6۔S~f6V=5|UT6|.af/gkkSΚgUsxb/y5|À33PG,ܝ|/fO/ܙdٍcκ҆P[߈u|K?qó?-cG}muwyɫPgrj]_FQc٘M;ړs۴S?kl;9FVq"BĀb@ wm/d6ߴeycy򙩀wey=τ??6sE^*r>`$/6d4z|}nu? 4;|H ,d9yus_-|%3f4[k?;iعcI<*IJy1jxvsGQ+H166Cvj8Sr[3{dc3~;zw\Og3?aNm_ U羞v/>ilL9h|3* ~<^v;' hYn*Noj4$,3$|/pguUv>d1/7k65O>Bkal컲/6~ Yf-vب|[oZ*\1qmݯZ51 Ā؇|buXC{ヤ @p3cǤX.KIDATav;!#' _{unYjIpA?ͷ;~kolz!sZ78M\GnUviBsKϨfN)Xl]e󗎪c[vzx_S{k(a>X}Ͱ_fg06£ɕo(\á1t?`oWj(Āb``M ɟ_W6d8 -d?c#c/gQp\|wJR^^o]ozˇDNzA{<_'6{gdx85\1c+y:/yuyd݌Kl5Rx{ :dJӳ˯%-xuIgV|"I֋b@ >@| Ϝzb'ؖ?rƯnIFdO=y-|2 >a[ww>R2Xȱ1W/ +ϷVd# 7;lLe o]n˟ <5c'AҎ]z2E~wwݐȌp 6nꉏٺ$|gI 9;CiS.]` ÛftWC{ǟ~AlI_~6>Q&b@ 13=~A\Xs O :|tlcg 6sb͜9mKxN\0jKmVړMڧξPVEym)v|1s5HNo؉a+Imjq!utG" j w@ó5m3./p<|!puĉPT7[yUg>=aGx{QE~hG3./I@~Y) Āb`c }Lw 5Oq6;wfX?|z;uԪD 1 Āu2aNj/ u{޶{xp~QSַΌ) Āb fbb@ 1 Āb`w2a6klz6˖يկ/*(,ym+ln  +ؒ%Klيu]^X^]^mj꘻>V[bm4 5iׯ lynoՋ1 Āf@l2wv9Y1ұk1&ϛgۏ?$/V2'e_\d~ENnS'k3r}oDg|YH/C6qDͶf!am˛{Z޴꿆R1 Ā]0߿mf o[ևf7ٶ+헷^,/VNlՕ lC j<Mzv\e5u.}4{lfohsov {͕mͻ}tĆ%ڤ~ۯQsMob@ >@f[n83|k[5G7fMdזn-B|zsm]r|\iS=; w7b!{{dsUն vEv׮*'k:-δ7u3{kg͵k"[ݍ۶5kZe\wpkڤYmfmOt-+ao!|uXwϵsUW,:f{uɹϲ.*^M:L>C K~XX''ѵH!}<]{^2׫p^]2k]׺us5Ok.-wl^}_[h5KM󮳥-Y'K 1 @3LJ{wqfTZ^|vn{_mS'չ=ڳ/Cٶyw}kfܜk{-z6ymeQ>߻V=|mm/e;9n6mu<{qmtvquxwRkKoۿnys[tK?l'\ab]Ҧ~{SC>-T/~kmr\u;[{yo _ $wΞzZkq6򟔞eͺ~-αǷ ?'}m\|0oG%/βoomo?W/o|nvS)*ĀbH+r/,g, ˇ֗Ä <?a[8.G[x ق3y-y.%d =(9^Qc/>mX񐛿l5ԼڗlxhK~?6صL!,gC/׏0sט2k9 ]d`k6Fػ]E .t&;gțu61v,ξQ}0ęCJ-nsnE=zMaA)=1*snل،3lʘ^y?z%|d-|6>g"YRw?kLE \qO~fu:ofUl0clyc 0xʩSmQ@vRZѫb@ 2LJ;CSX܎u?o`?^?Wh6S@y_rw"G}=;x=̒i:jM\8'Ϩkn98. Iڌ}&V{B+᮴_. C[ }ͱ}IU狛;AoC>[삻؄%OSOMwsvuش_yeW5G7ȷZ6`XU4 9M*/ыح~>v+m¶mB ]q}%1 Ā>A3m [ nxz+wC0o!I;mj=R:FO|!u-y͆nZ:g7)WdžC;{|۳)|{pfhK-cB*YO4o?DLj3yv6n8mUס|k% EA[vϲĞvluߴ#?z]|?٢y+Kr}(%eNq۽Ƀm{n1A65G Ą_e1bέ.<3_ڦ-}l7JĀb@ }a}?/;۾b?..<5'ßfc~MrۊE=1gbz. o jjaLkqc~}oה g}'kd<𒯌!ɛlm?; '[A:J{d ^J[^l]ׄ5WOn\JoZsݲ`;O5+?c775l9Zl#1&O j|%HĀb@ 1Ч\NO|t |83@ bP k!|!Oc-dܷ8Put7|5T&Āb@ hɛkyg,=Nr3^;~Ol'j_SmĀb@ 1/0Pvp.EeY&3MWZ~Ϯ]Fb@ 1 Ā؃و;4bծwoV|> n]p ;w|/>nڴiGzIŀb@ 13oO<`.Zp>Kgg&ԥл]0 'DۿыޠE|ȁOm`|f.kq4}446As?ļ~C#QMLZ 1 Ā{ 7`G>] oSKFKvޙ&jظJuÉeρ >7Кu`O8`K4JJ< NAb@ 1 0Q8x͙%~i:4Ky<_{zY,ꇶ IHBI=W<'#zՈ1 Ā~5jQM.sPC;M$浯xl#a bSj6X'q>F kӎ}bU2&-Āb@ ZmaK>}qj8k<1oj⺪f DPur1cghN1%b@ 1 Ā?KvQ |HZ?́텸hǹp{jI tZDlcFc;/ׁsab@ 1 ,Bu<ЯeeMid~c4z5\jf viY'q41jbx-:O5_[@:峎qc5ۍf`y;-6syи~xyafk8ܲx\8{<b@ 131CP%G^g>:9IIean04<؃6Ouĩ}. To(} ĈCCZ`Oӫb@ 1W3mlby:mMbvt/>]LuK* T}nca cxsї>\xyyp-xzb@ 1 *A(m8X}ڌ#jlz͵BøǪ4XpM :0rm $6:BẂ_8Чͽ1 Ā}?Ц++GjCR?AX}Ӧtg{IUC?cގ@*4$F 8l1N61i1 Āb1m`8f]ZүGsOiYcf~X07&҆7n1GymyqaCkjRoQ >ydiq}i1 Ābo22@ :> яuf80E9/D 1o3uxպì_ G}j^3u|>}ڌ16NbG=}hx ce1 Āb1!b@}.̃Ma{=qi1 Ābo1yg}@}- xf^:hCCXRT|a]0qvSu\O<}-l0|@}<+5b<CƠc$1 ĀbO2fsna>Pp4cަOXuZnڬxaK`~sĘs@0!6'zĽ'l4Y5^Z 1 Ā `>Nx 3 }h4ġ͜>5c<p4bOwwfj<)Zj8cI4rpx}}?9ppCW1 Āb@ 35cy1c>8cY0^}0~e~wY6&9юu㈡m4 1B6ގcC!xC $1 ĀbO2B8lOi60̇8c}jm_GkcIB/fY`؃5}N:G./lC,0 jg6đK9gaKĀb@ 139y 0،>bǶe>ވAk|j0vb$9fhCshe=c!T^7oPe.PÃ8PxU 1 Ā `olKӾڕ;KPR0Ń1nq8̉1i6. u8{kQ3F -5pKmhH h1b@ 1 ~vOSjiC:XZm?5$X{f~Ua7YCcԨ6}hbl8tccı?N$zjÆ .xyLyb@ 1 f,ay9c<,Ảek} FNMM뼘˴f/HfY5:x,bqZXcQ IOI1 Ā}oO,&͹0y6& O;ǘ:դ1wV[P0[eC,al^ӘG\_+G/fiX,iuqN]~ٴM`F^8c b!zC\8CC-F}b@ 1 ]vρ=h3C&#-G: +dkwYla1mЌcoAFcK#ufP͸b@ 1𳏷}ho#9'ѧFmh_qb?vþ̥QOA¼x'RU0[!Fц6AH"V#5rhe}ܬ#=A״~-|l1 Āb`fKY144gMq2¦\!iqj%//4ƻ >6L}6z0MVq6r!XsQu&MABIP/b@ 1 Āp Y65Cr<&'smgqxM)zZwwZmsÈ 㬇TiC#8`Rb'kA&X#lb@ 1 *1 ڱ#WяylY﵏æ mb֍fɴ| *IG.1wdiCW{D|> },Itŀb@ gii>\QGm/Ƒ^V5ٍf!x~qfNډϧ "Z:53Y1AA+b@ 1 @ D?MC/i1yNÉ75qj`XW;*om楝A*Xo1h9,c/}<qxݙ%K 1 Ā pFFv>u1|_mƩE K6k}:7qN pg>{"6\qڬC8$m<1 Ā}=fF-m`8f*YëǙKiCCؿuxwf` skHLK<`ӧF@ /BBX>%|b@ 1 @a m;gqX4o5pA҇x_#hҨ;by85)mՐRz602Yf-|b@ 1 ai6X70☏ },ӆRe fFNyY~s]֥i1x ÞG8gqq8rYG1|Ǔ$1 Ā}x6 1}q qh>O{3N,{cU$ipc,yMcm>>ۋ8X+_ 1 Āwbk0c1^ q쑥}o~1"f&]? $;YcK?eωq40HZM'qb@ 1 ĀGV/{M|RMaq=Y8b@ 1 @f kѦ&c#FkI^#1@ IꞝMRiS#5c9Y_9Ԭ91s1&-Āb@ j$kmj@,? g87zb=2&?&m#5CY~#cY6R<1i1 Āb@ @`eqC,/6{bEh.Py񀘆yaOƈ~\8sуvIӬMsŀb@ }j憴*j<;ugKXvWjO&Āb@ d geqo36e.]Y,Уl@<2-Œmp ~FY\OMY9b@ 1 @b mD@\q}V8cMZ>c RSg0˵|mG{[΋USM=H1 ĀbyCa^}r|۬nɻ~ӽ6b*$oYc+/KU4Āb@ }3>Ή}VVVԳY,ثl`4dHްCxt T,C 1 Ā d  Vyzc̈́I/v7QMLp5,)G 1 Ā64̒ i1 Āb@ {ݎa1 Āb@ 1Pd@ì~Āb@ 1 Z4Nb@ 1 Ā01 Āb@  {ŀb@ 1 4g@ 1 Āb`e@^{q1 Āb@ \>>#gaffer-0.4.4/_static/searchtools.js/* * searchtools.js_t * ~~~~~~~~~~~~~~~~ * * Sphinx JavaScript utilties for the full-text search. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /** * helper function to return a node containing the * search summary for a given text. keywords is a list * of stemmed words, hlwords is the list of normal, unstemmed * words. the first one is used to find the occurance, the * latter for highlighting it. */ jQuery.makeSearchSummary = function(text, keywords, hlwords) { var textLower = text.toLowerCase(); var start = 0; $.each(keywords, function() { var i = textLower.indexOf(this.toLowerCase()); if (i > -1) start = i; }); start = Math.max(start - 120, 0); var excerpt = ((start > 0) ? '...' : '') + $.trim(text.substr(start, 240)) + ((start + 240 - text.length) ? '...' : ''); var rv = $('
').text(excerpt); $.each(hlwords, function() { rv = rv.highlightText(this, 'highlighted'); }); return rv; } /** * Porter Stemmer */ var Stemmer = function() { var step2list = { ational: 'ate', tional: 'tion', enci: 'ence', anci: 'ance', izer: 'ize', bli: 'ble', alli: 'al', entli: 'ent', eli: 'e', ousli: 'ous', ization: 'ize', ation: 'ate', ator: 'ate', alism: 'al', iveness: 'ive', fulness: 'ful', ousness: 'ous', aliti: 'al', iviti: 'ive', biliti: 'ble', logi: 'log' }; var step3list = { icate: 'ic', ative: '', alize: 'al', iciti: 'ic', ical: 'ic', ful: '', ness: '' }; var c = "[^aeiou]"; // consonant var v = "[aeiouy]"; // vowel var C = c + "[^aeiouy]*"; // consonant sequence var V = v + "[aeiou]*"; // vowel sequence var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 var s_v = "^(" + C + ")?" + v; // vowel in stem this.stemWord = function (w) { var stem; var suffix; var firstch; var origword = w; if (w.length < 3) return w; var re; var re2; var re3; var re4; firstch = w.substr(0,1); if (firstch == "y") w = firstch.toUpperCase() + w.substr(1); // Step 1a re = /^(.+?)(ss|i)es$/; re2 = /^(.+?)([^s])s$/; if (re.test(w)) w = w.replace(re,"$1$2"); else if (re2.test(w)) w = w.replace(re2,"$1$2"); // Step 1b re = /^(.+?)eed$/; re2 = /^(.+?)(ed|ing)$/; if (re.test(w)) { var fp = re.exec(w); re = new RegExp(mgr0); if (re.test(fp[1])) { re = /.$/; w = w.replace(re,""); } } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1]; re2 = new RegExp(s_v); if (re2.test(stem)) { w = stem; re2 = /(at|bl|iz)$/; re3 = new RegExp("([^aeiouylsz])\\1$"); re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re2.test(w)) w = w + "e"; else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); } else if (re4.test(w)) w = w + "e"; } } // Step 1c re = /^(.+?)y$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(s_v); if (re.test(stem)) w = stem + "i"; } // Step 2 re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step2list[suffix]; } // Step 3 re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step3list[suffix]; } // Step 4 re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; re2 = /^(.+?)(s|t)(ion)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); if (re.test(stem)) w = stem; } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1] + fp[2]; re2 = new RegExp(mgr1); if (re2.test(stem)) w = stem; } // Step 5 re = /^(.+?)e$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); re2 = new RegExp(meq1); re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) w = stem; } re = /ll$/; re2 = new RegExp(mgr1); if (re.test(w) && re2.test(w)) { re = /.$/; w = w.replace(re,""); } // and turn initial Y back to y if (firstch == "y") w = firstch.toLowerCase() + w.substr(1); return w; } } /** * Search Module */ var Search = { _index : null, _queued_query : null, _pulse_status : -1, init : function() { var params = $.getQueryParameters(); if (params.q) { var query = params.q[0]; $('input[name="q"]')[0].value = query; this.performSearch(query); } }, loadIndex : function(url) { $.ajax({type: "GET", url: url, data: null, success: null, dataType: "script", cache: true}); }, setIndex : function(index) { var q; this._index = index; if ((q = this._queued_query) !== null) { this._queued_query = null; Search.query(q); } }, hasIndex : function() { return this._index !== null; }, deferQuery : function(query) { this._queued_query = query; }, stopPulse : function() { this._pulse_status = 0; }, startPulse : function() { if (this._pulse_status >= 0) return; function pulse() { Search._pulse_status = (Search._pulse_status + 1) % 4; var dotString = ''; for (var i = 0; i < Search._pulse_status; i++) dotString += '.'; Search.dots.text(dotString); if (Search._pulse_status > -1) window.setTimeout(pulse, 500); }; pulse(); }, /** * perform a search for something */ performSearch : function(query) { // create the required interface elements this.out = $('#search-results'); this.title = $('

' + _('Searching') + '

').appendTo(this.out); this.dots = $('').appendTo(this.title); this.status = $('

').appendTo(this.out); this.output = $('