extended commit (see note)

- Slight overhaul of init() system; added ``edbob.init_modules()`` function.

- Added ``read_service()`` method to ``AppConfigParser`` class, for use with
  Windows services.

- Added generic ``Service`` class to ``edbob.win32`` module.  (File monitor now
  inherits from it.)

- Tweaked ``edbob.db`` initialization somewhat.  (``Base.metadata`` no longer
  binds to ``edbob.db.engine``.)

- Fixed guest role bug in ``edbob.db.auth.has_permission()`` function.

- Added "automagical" enumeration support for database extensions.

- Added ``EMAIL_PREFERENCE`` enum to ``contact`` database extension.

- Tweaked ``edbob.pyramid.includeme()``.

- Tweaked ``people`` Pyramid views.
This commit is contained in:
Lance Edgar 2012-09-17 11:43:13 -07:00
parent 526ff5dd6b
commit c7e0bfef6a
18 changed files with 457 additions and 248 deletions

View file

@ -34,6 +34,3 @@ from edbob.files import *
from edbob.modules import *
from edbob.configuration import *
from edbob.initialization import *
inited = False

View file

@ -289,6 +289,25 @@ class AppConfigParser(ConfigParser.SafeConfigParser):
self.paths_loaded.append(path)
log.info("Read config file: %s" % path)
def read_service(self, service, paths):
"""
"Special" version of :meth:`read()` which will first inspect the
file(s) for a service-specific directive, the presence of which
indicates the *true* config file to be used for the service.
This method is pretty much a hack to get around certain limitations of
Windows service implementations; it is not used otherwise.
"""
config = ConfigParser.SafeConfigParser()
config.read(paths)
if (config.has_section('edbob.service_config')
and config.has_option('edbob.service_config', service)):
paths = eval(config.get('edbob.service_config', service))
self.read(paths, recurse=True)
def require(self, section, option, msg=None):
"""
Convenience method which will raise an exception if the given option

View file

@ -37,7 +37,6 @@ import edbob
__all__ = ['engines', 'engine', 'Session', 'get_setting', 'save_setting']
inited = False
engines = None
engine = None
Session = sessionmaker()
@ -71,36 +70,36 @@ def init(config):
from edbob.db import enum
from edbob.db.extensions import extend_framework
global inited, engines, engine
global engines, engine
keys = config.get('edbob.db', 'sqlalchemy.keys')
keys = config.get('edbob.db', 'keys')
if keys:
keys = keys.split()
keys = keys.split(',')
else:
keys = ['default']
engines = {}
cfg = config.get_dict('edbob.db')
for key in keys:
key = key.strip()
try:
engines[key] = engine_from_config(cfg, 'sqlalchemy.%s.' % key)
engines[key] = engine_from_config(cfg, '%s.' % key)
except KeyError:
if key == 'default':
try:
engines[key] = engine_from_config(cfg)
engines[key] = engine_from_config(cfg, 'sqlalchemy.')
except KeyError:
pass
engine = engines.get('default')
if engine:
Base.metadata.bind = engine
Session.configure(bind=engine)
extend_framework()
edbob.graft(edbob, edbob.db)
edbob.graft(edbob, model)
edbob.graft(edbob, enum)
inited = True
def get_setting(name, session=None):

View file

@ -85,12 +85,12 @@ def guest_role(session):
"""
uuid = 'f8a27c98965a11dfaff7001143047286'
admin = session.query(edbob.Role).get(uuid)
if admin:
return admin
admin = edbob.Role(uuid=uuid, name='Guest')
session.add(admin)
return admin
guest = session.query(edbob.Role).get(uuid)
if guest:
return guest
guest = edbob.Role(uuid=uuid, name='Guest')
session.add(guest)
return guest
def grant_permission(role, permission, session=None):
@ -114,7 +114,7 @@ def has_permission(obj, perm, session=None):
"""
if isinstance(obj, edbob.User):
roles = obj.roles
roles = list(obj.roles)
elif isinstance(obj, edbob.Role):
roles = [obj]
elif obj is None:

View file

@ -76,6 +76,11 @@ class Extension(edbob.Object):
# :meth:`Extension.get_models_module()` for more info).
model_module = ''
# You can set this to any dotted module path you like. If unset a default
# will be assumed, of the form ``<path.to.extension>.enum`` (see
# :meth:`Extension.get_enum_module()` for more info).
enum_module = ''
# @property
# @requires_impl(is_property=True)
# def name(self):
@ -119,6 +124,9 @@ class Extension(edbob.Object):
"""
edbob.graft(edbob, self.get_model_module())
enum = self.get_enum_module()
if enum:
edbob.graft(edbob, enum)
# def extend_mappers(self, metadata):
# """
@ -144,6 +152,26 @@ class Extension(edbob.Object):
self.populate_metadata(meta, recurse)
return meta
def get_enum_module(self):
"""
Imports and returns a reference to the Python module providing
enumeration data for the extension (if one exists).
:attr:`Extension.enum_module` is first consulted to determine the
dotted module path. If nothing is found there, a default path is
constructed by appending ``'.enum'`` to the extension module's own
dotted path.
"""
if self.enum_module:
module = self.enum_module
else:
module = str(self.__class__.__module__) + '.enum'
try:
return import_module_path(module)
except ImportError:
return None
def get_model_module(self):
"""
Imports and returns a reference to the Python module providing schema

View file

@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
################################################################################
#
# edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar
#
# This file is part of edbob.
#
# edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
``edbob.db.extensions.contact.enum`` -- Enumerations
"""
EMAIL_PREFERENCE_NONE = 0
EMAIL_PREFERENCE_TEXT = 1
EMAIL_PREFERENCE_HTML = 2
EMAIL_PREFERENCE_MOBILE = 3
EMAIL_PREFERENCE = {
EMAIL_PREFERENCE_NONE : "No Emails",
EMAIL_PREFERENCE_TEXT : "Text",
EMAIL_PREFERENCE_HTML : "HTML",
EMAIL_PREFERENCE_MOBILE : "Mobile",
}

View file

@ -35,7 +35,7 @@ import threading
import edbob
from edbob.errors import email_exception
from edbob.filemon import get_monitor_profiles
from edbob.win32 import file_is_free
from edbob.win32 import Service, file_is_free
if sys.platform == 'win32': # docs should build for everyone
import win32api
@ -50,36 +50,23 @@ if sys.platform == 'win32': # docs should build for everyone
log = logging.getLogger(__name__)
class FileMonitorService(win32serviceutil.ServiceFramework):
class FileMonitorService(Service):
"""
Implements edbob's file monitor Windows service.
"""
_svc_name_ = "Edbob File Monitor"
_svc_name_ = 'EdbobFileMonitor'
_svc_display_name_ = "Edbob : File Monitoring Service"
_svc_description_ = ("Monitors one or more folders for incoming files, "
"and performs configured actions as new files arrive.")
appname = 'edbob'
def __init__(self, args):
"""
Constructor.
"""
# super(FileMonitorService, self).__init__(args)
win32serviceutil.ServiceFramework.__init__(self, args)
# Create "wait stop" event, for main worker loop.
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def Initialize(self):
"""
Service initialization.
"""
# Read configuration file(s).
edbob.init(self.appname)
if not Service.Initialize(self):
return False
# Read monitor profile(s) from config.
self.monitored = get_monitor_profiles(self.appname)
@ -118,50 +105,6 @@ class FileMonitorService(win32serviceutil.ServiceFramework):
return True
def SvcDoRun(self):
"""
This method is invoked when the service starts.
"""
import servicemanager
# Write start occurrence to Windows Event Log.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
# Figure out what we're supposed to be doing.
if self.Initialize():
# Wait infinitely for stop request, while threads do their thing.
log.info("SvcDoRun: All threads started; waiting for stop request.")
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
log.info("SvcDoRun: Stop request received.")
else: # Nothing to be done...
msg = "Nothing to do! No valid monitor profiles found in config."
servicemanager.LogWarningMsg(msg)
log.warning("SvcDoRun: %s" % msg)
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# Write stop occurrence to Windows Event Log.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
def SvcStop(self):
"""
This method is invoked when the service is requested to stop itself.
"""
# Let the SCM know we're trying to stop.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# Let worker loop know its job is done.
win32event.SetEvent(self.hWaitStop)
def monitor_files(queue, path, profile):
"""

View file

@ -38,7 +38,9 @@ from edbob.configuration import (
from edbob.exceptions import InitError
__all__ = ['init']
__all__ = ['init', 'init_modules', 'inited']
inited = []
log = logging.getLogger(__name__)
@ -80,22 +82,40 @@ def init(appname='edbob', *args, **kwargs):
else:
config_paths = default_system_paths(appname) + default_user_paths(appname)
shell = bool(kwargs.get('shell'))
for paths in config_paths:
config.read(paths, recurse=not shell)
service = kwargs.get('service')
if service:
config.read_service(service, config_paths)
else:
shell = kwargs.get('shell', False)
for paths in config_paths:
config.read(paths, recurse=not shell)
config.configure_logging()
default_modules = 'edbob.time'
modules = config.get('edbob', 'init', default=default_modules)
if modules:
for name in modules.split(','):
name = name.strip()
modules = modules.split(',')
init_modules(modules, config)
edbob.graft(edbob, locals(), 'config')
inited.append('edbob')
def init_modules(names, config=None):
"""
Initialize the given modules. ``names`` should be a sequence of strings,
each of which should be a dotted module name. If ``config`` is not
specified, :attr:`edbob.config` is assumed.
"""
if config is None:
config = edbob.config
for name in names:
name = name.strip()
if name not in inited:
module = __import__(name, globals(), locals(), fromlist=['init'])
if not hasattr(module, 'init'):
raise InitError(module)
getattr(module, 'init')(config)
# config.inited.append(name)
# config.inited.append('edbob')
edbob.graft(edbob, locals(), 'config')
edbob.inited = True
inited.append(name)

View file

@ -29,6 +29,8 @@
from sqlalchemy.orm import sessionmaker, scoped_session
from zope.sqlalchemy import ZopeTransactionExtension
import edbob
__all__ = ['Session']
@ -56,6 +58,7 @@ def includeme(config):
config.include('pyramid_tm')
# Configure SQLAlchemy session.
Session.configure(bind=edbob.engine)
Session.configure(extension=ZopeTransactionExtension())
# Configure user authentication / authorization.
@ -69,3 +72,6 @@ def includeme(config):
# Add static views.
config.include('edbob.pyramid.static')
# Add subscriber hooks.
config.include('edbob.pyramid.subscribers')

View file

@ -74,12 +74,10 @@ def EnumFieldRenderer(enum):
return ''
if value in enum:
return enum[value]
return value
return str(value)
def render(self, **kwargs):
opts = []
for value in sorted(enum):
opts.append((enum[value], value))
opts = [(enum[x], x) for x in sorted(enum)]
return formalchemy.fields.SelectFieldRenderer.render(self, opts, **kwargs)
return Renderer

View file

@ -1,5 +1,5 @@
<%inherit file="/form.mako" />
<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+str(form.fieldset.model)}</%def>
<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+h.literal(str(form.fieldset.model))}</%def>
${parent.body()}

View file

@ -1,2 +0,0 @@
<%inherit file="/base.mako" />
${parent.body()}

View file

@ -0,0 +1,28 @@
<%inherit file="/crud.mako" />
<%def name="crud_name()">Person</%def>
<%def name="context_menu_items()">
<li>${h.link_to("Back to People", url('people'))}</li>
</%def>
${parent.body()}
## % if fieldset.edit:
## <h2>User Info</h2>
## % if user:
## ${user.render()|n}
## <div class="buttons">
## <button type="button" onclick="location.href = '${url('user.edit', uuid=user.model.uuid)}';">Edit User</button>
## </div>
## % else:
## <p>This person does not have a user account.</p>
## ${h.form(url('user.new'))}
## ${h.hidden('User--person_uuid', value=fieldset.model.uuid)}
## ${h.hidden('User--username')}
## <div class="buttons">
## ${h.submit('submit', "Create User")}
## </div>
## ${h.end_form()}
## % endif
## % endif

View file

@ -1,12 +1,11 @@
<%inherit file="/people/base.mako" />
<%inherit file="/index.mako" />
<%inherit file="/grid.mako" />
<%def name="title()">People</%def>
<%def name="context_menu_items()">
% if request.has_perm('people.create'):
<li>${h.link_to("Create a new Person", url('person.new'))}</li>
% endif
## % if request.has_perm('people.create'):
## <li>${h.link_to("Create a new Person", url('person.new'))}</li>
## % endif
</%def>
${parent.body()}

View file

@ -1,29 +0,0 @@
<%inherit file="/people/base.mako" />
<%inherit file="/crud.mako" />
<%def name="crud_name()">Person</%def>
<%def name="menu()">
<p>${h.link_to("Back to People", url('people.list'))}</p>
</%def>
${parent.body()}
% if fieldset.edit:
<h2>User Info</h2>
% if user:
${user.render()|n}
<div class="buttons">
<button type="button" onclick="location.href = '${url('user.edit', uuid=user.model.uuid)}';">Edit User</button>
</div>
% else:
<p>This person does not have a user account.</p>
${h.form(url('user.new'))}
${h.hidden('User--person_uuid', value=fieldset.model.uuid)}
${h.hidden('User--username')}
<div class="buttons">
${h.submit('submit', "Create User")}
</div>
${h.end_form()}
% endif
% endif

View file

@ -26,155 +26,239 @@
``edbob.pyramid.views.people`` -- Person Views
"""
import transaction
from pyramid.httpexceptions import HTTPFound
from sqlalchemy import and_
from formalchemy import Field
# import transaction
# from pyramid.httpexceptions import HTTPFound
# from formalchemy import Field
import edbob
from edbob.pyramid import filters
from edbob.pyramid import forms
from edbob.pyramid import grids
from edbob.pyramid import Session
# from edbob.pyramid import filters
# from edbob.pyramid import forms
# from edbob.pyramid import grids
# from edbob.pyramid import Session
from edbob.pyramid.views import SearchableAlchemyGridView, CrudView
def filter_map():
return filters.get_filter_map(
edbob.Person,
ilike=['first_name', 'last_name', 'display_name'])
class PeopleGrid(SearchableAlchemyGridView):
def search_config(request, fmap):
return filters.get_search_config(
'people.list', request, fmap,
include_filter_display_name=True,
filter_type_display_name='lk')
mapped_class = edbob.Person
config_prefix = 'people'
sort = 'first_name'
def search_form(config):
return filters.get_search_form(config)
def join_map(self):
return {
'email':
lambda q: q.outerjoin(edbob.PersonEmailAddress, and_(
edbob.PersonEmailAddress.parent_uuid == edbob.Person.uuid,
edbob.PersonEmailAddress.preference == 1)),
'phone':
lambda q: q.outerjoin(edbob.PersonPhoneNumber, and_(
edbob.PersonPhoneNumber.parent_uuid == edbob.Person.uuid,
edbob.PersonPhoneNumber.preference == 1)),
}
def grid_config(request, search, fmap):
return grids.get_grid_config(
'people.list', request, search,
filter_map=fmap, sort='display_name')
def filter_map(self):
return self.make_filter_map(
ilike=['first_name', 'last_name'],
email=self.filter_ilike(edbob.PersonEmailAddress.address),
phone=self.filter_ilike(edbob.PersonPhoneNumber.number))
def sort_map():
return grids.get_sort_map(
edbob.Person,
['first_name', 'last_name', 'display_name'])
def filter_config(self):
return self.make_filter_config(
include_filter_first_name=True,
filter_type_first_name='lk',
include_filter_last_name=True,
filter_type_last_name='lk',
filter_label_phone="Phone Number",
filter_label_email="Email Address")
def query(config):
smap = sort_map()
q = Session.query(edbob.Person)
q = filters.filter_query(q, config)
q = grids.sort_query(q, config, smap)
return q
def sort_map(self):
return self.make_sort_map(
'first_name', 'last_name',
email=self.sorter(edbob.PersonEmailAddress.address),
phone=self.sorter(edbob.PersonPhoneNumber.number))
def grid(self):
g = self.make_grid()
g.configure(
include=[
g.first_name,
g.last_name,
g.phone.label("Phone Number"),
g.email.label("Email Address"),
],
readonly=True)
g.clickable = True
g.click_route_name = 'person.read'
return g
def people(context, request):
class PersonCrud(CrudView):
fmap = filter_map()
config = search_config(request, fmap)
search = search_form(config)
config = grid_config(request, search, fmap)
people = grids.get_pager(query, config)
mapped_class = edbob.Person
home_route = 'people'
g = forms.AlchemyGrid(
edbob.Person, people, config,
gridurl=request.route_url('people.list'),
objurl='person.edit')
g.configure(
include=[
g.first_name,
g.last_name,
g.display_name,
],
readonly=True)
grid = g.render(class_='clickable people')
return grids.render_grid(request, grid, search)
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure(
include=[
fs.first_name,
fs.last_name,
fs.phone.label("Phone Number"),
fs.email.label("Email Address"),
])
return fs
def person_fieldset(person, request):
fs = forms.make_fieldset(person, url=request.route_url,
url_action=request.current_route_url(),
route_name='people.list')
fs.configure(
include=[
fs.first_name,
fs.last_name,
fs.display_name,
])
return fs
# def filter_map():
# return filters.get_filter_map(
# edbob.Person,
# ilike=['first_name', 'last_name', 'display_name'])
# def search_config(request, fmap):
# return filters.get_search_config(
# 'people.list', request, fmap,
# include_filter_display_name=True,
# filter_type_display_name='lk')
# def search_form(config):
# return filters.get_search_form(config)
# def grid_config(request, search, fmap):
# return grids.get_grid_config(
# 'people.list', request, search,
# filter_map=fmap, sort='display_name')
# def sort_map():
# return grids.get_sort_map(
# edbob.Person,
# ['first_name', 'last_name', 'display_name'])
# def query(config):
# smap = sort_map()
# q = Session.query(edbob.Person)
# q = filters.filter_query(q, config)
# q = grids.sort_query(q, config, smap)
# return q
def new_person(context, request):
# def people(context, request):
fs = person_fieldset(edbob.Person, request)
if not fs.readonly and request.POST:
fs.rebind(data=request.params)
if fs.validate():
# fmap = filter_map()
# config = search_config(request, fmap)
# search = search_form(config)
# config = grid_config(request, search, fmap)
# people = grids.get_pager(query, config)
with transaction.manager:
fs.sync()
Session.add(fs.model)
Session.flush()
request.session.flash("%s \"%s\" has been %s." % (
fs.crud_title, fs.get_display_text(),
'updated' if fs.edit else 'created'))
# g = forms.AlchemyGrid(
# edbob.Person, people, config,
# gridurl=request.route_url('people.list'),
# objurl='person.edit')
return HTTPFound(location=request.route_url('people.list'))
# g.configure(
# include=[
# g.first_name,
# g.last_name,
# g.display_name,
# ],
# readonly=True)
return {'fieldset': fs, 'crud': True}
# grid = g.render(class_='clickable people')
# return grids.render_grid(request, grid, search)
def edit_person(request):
"""
View for editing a :class:`edbob.Person` instance.
"""
# def person_fieldset(person, request):
# fs = forms.make_fieldset(person, url=request.route_url,
# url_action=request.current_route_url(),
# route_name='people.list')
# fs.configure(
# include=[
# fs.first_name,
# fs.last_name,
# fs.display_name,
# ])
# return fs
from edbob.pyramid.views.users import user_fieldset
uuid = request.matchdict['uuid']
person = Session.query(edbob.Person).get(uuid) if uuid else None
assert person
# def new_person(context, request):
fs = person_fieldset(person, request)
if request.POST:
fs.rebind(data=request.params)
if fs.validate():
# fs = person_fieldset(edbob.Person, request)
# if not fs.readonly and request.POST:
# fs.rebind(data=request.params)
# if fs.validate():
with transaction.manager:
fs.sync()
fs.model = Session.merge(fs.model)
request.session.flash("%s \"%s\" has been %s." % (
fs.crud_title, fs.get_display_text(),
'updated' if fs.edit else 'created'))
home = request.route_url('people.list')
# with transaction.manager:
# fs.sync()
# Session.add(fs.model)
# Session.flush()
# request.session.flash("%s \"%s\" has been %s." % (
# fs.crud_title, fs.get_display_text(),
# 'updated' if fs.edit else 'created'))
return HTTPFound(location=home)
# return HTTPFound(location=request.route_url('people.list'))
user = fs.model.user
if user:
user = user_fieldset(user, request)
user.readonly = True
del user.person
del user.password
del user.confirm_password
# return {'fieldset': fs, 'crud': True}
return {'fieldset': fs, 'crud': True, 'user': user}
# def edit_person(request):
# """
# View for editing a :class:`edbob.Person` instance.
# """
# from edbob.pyramid.views.users import user_fieldset
# uuid = request.matchdict['uuid']
# person = Session.query(edbob.Person).get(uuid) if uuid else None
# assert person
# fs = person_fieldset(person, request)
# if request.POST:
# fs.rebind(data=request.params)
# if fs.validate():
# with transaction.manager:
# fs.sync()
# fs.model = Session.merge(fs.model)
# request.session.flash("%s \"%s\" has been %s." % (
# fs.crud_title, fs.get_display_text(),
# 'updated' if fs.edit else 'created'))
# home = request.route_url('people.list')
# return HTTPFound(location=home)
# user = fs.model.user
# if user:
# user = user_fieldset(user, request)
# user.readonly = True
# del user.person
# del user.password
# del user.confirm_password
# return {'fieldset': fs, 'crud': True, 'user': user}
def includeme(config):
config.add_route('people.list', '/people')
config.add_view(people, route_name='people.list', renderer='/people/index.mako',
permission='people.list', http_cache=0)
config.add_route('people', '/people')
config.add_view(PeopleGrid, route_name='people',
renderer='/people/index.mako',
permission='people.list')
config.add_route('person.new', '/people/new')
config.add_view(new_person, route_name='person.new', renderer='/people/person.mako',
permission='people.create', http_cache=0)
# config.add_route('people.list', '/people')
# config.add_view(people, route_name='people.list', renderer='/people/index.mako',
# permission='people.list', http_cache=0)
config.add_route('person.edit', '/people/{uuid}/edit')
config.add_view(edit_person, route_name='person.edit', renderer='/people/person.mako',
permission='people.edit', http_cache=0)
config.add_route('person.read', '/people/{uuid}')
config.add_view(PersonCrud, attr='read', route_name='person.read',
renderer='/people/crud.mako',
permission='people.read')
# config.add_route('person.new', '/people/new')
# config.add_view(new_person, route_name='person.new', renderer='/people/person.mako',
# permission='people.create', http_cache=0)
# config.add_route('person.edit', '/people/{uuid}/edit')
# config.add_view(edit_person, route_name='person.edit', renderer='/people/person.mako',
# permission='people.edit', http_cache=0)

View file

@ -69,7 +69,6 @@ setup(
],
install_requires = requires,
tests_require = requires,
packages = find_packages(),
include_package_data = True,

View file

@ -28,16 +28,96 @@
import sys
import subprocess
import logging
if sys.platform == 'win32': # docs should build for everyone
import pywintypes
import win32api
import win32con
import win32event
import win32file
import win32print
import win32service
import win32serviceutil
import winerror
import edbob
log = logging.getLogger(__name__)
class Service(win32serviceutil.ServiceFramework):
"""
Base class for Windows service implementations.
"""
appname = 'edbob'
def __init__(self, args):
"""
Constructor.
"""
win32serviceutil.ServiceFramework.__init__(self, args)
# Create "wait stop" event, for main worker loop.
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def Initialize(self):
"""
Service initialization.
"""
# Read configuration file(s).
edbob.init(self.appname, service=self._svc_name_)
return True
def SvcDoRun(self):
"""
This method is invoked when the service starts.
"""
import servicemanager
# Write start occurrence to Windows Event Log.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
# Figure out what we're supposed to be doing.
if self.Initialize():
# Wait infinitely for stop request, while threads do their thing.
log.info("SvcDoRun: All threads started; waiting for stop request.")
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
log.info("SvcDoRun: Stop request received.")
else: # Nothing to be done...
msg = "Nothing to do! (Initialization failed.)"
servicemanager.LogWarningMsg(msg)
log.warning("SvcDoRun: %s" % msg)
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# Write stop occurrence to Windows Event Log.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
def SvcStop(self):
"""
This method is invoked when the service is requested to stop itself.
"""
# Let the SCM know we're trying to stop.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# Let worker loop know its job is done.
win32event.SetEvent(self.hWaitStop)
def RegDeleteTree(key, subkey):
"""