move psu from loops.common to loops.server; provide server.main for easy starting as server

This commit is contained in:
Helmut Merz 2024-10-06 09:36:40 +02:00
parent 2a689a871b
commit dc87e32a63
6 changed files with 319 additions and 3 deletions

View file

@ -5,8 +5,12 @@ from os import getenv
load_dotenv() load_dotenv()
zope_conf = getenv('ZOPE_CONF', 'zope.conf')
server_port = getenv('SERVER_PORT', '8099') server_port = getenv('SERVER_PORT', '8099')
shell_pw = (getenv('SHELL_PW', 'dummy'))
# storage settings # storage settings
from scopes.storage.db.postgres import StorageFactory from scopes.storage.db.postgres import StorageFactory
dbengine = 'postgresql+psycopg' dbengine = 'postgresql+psycopg'

7
inst/loops/runserver.sh Executable file
View file

@ -0,0 +1,7 @@
# inst/loops/runserver.sh
# use environment variables for instance-specific configuration:
#ZOPE_CONF=zope.conf
#SERVER_PORT=8099
python -c "from loops.server.main import main; main()"

8
inst/loops/zshell.sh Executable file
View file

@ -0,0 +1,8 @@
# inst/loops/zshell.sh
# use environment variables for instance-specific configuration:
#ZOPE_CONF=zope.conf
#LOOPS_PATH=sites/mysite
python -ic "from loops.server import psu; psu.setup()"

1
loops/server/__init__.py Normal file
View file

@ -0,0 +1 @@
# loops.server

View file

@ -1,6 +1,10 @@
# loops/inst/loops/main.py # loops.server.main
# call main() for starting a loops server process
import sys import sys
# module aliases - should be moved to loops.server.aliases
from zope.securitypolicy import securitymap from zope.securitypolicy import securitymap
sys.modules['zope.app.securitypolicy.securitymap'] = securitymap sys.modules['zope.app.securitypolicy.securitymap'] = securitymap
@ -12,8 +16,11 @@ def run(app, config):
#print(f'Serving on port {port}.') #print(f'Serving on port {port}.')
waitress.serve(app, port=port) waitress.serve(app, port=port)
def main():
if __name__ == '__main__':
import config import config
zope_conf = getattr(config, 'zope_conf', 'zope.conf')
app = getWSGIApplication('zope.conf') app = getWSGIApplication('zope.conf')
run(app, config) run(app, config)
if __name__ == '__main__':
main()

289
loops/server/psu.py Normal file
View file

@ -0,0 +1,289 @@
# loops.server.psu - python shell utilities
# use this from a Python command line (in a loops / bluebrem virtual environment).
#
# then:
#
# from loops.server import psu
# import config
# psu.setup('path/to/loopsRoot', 'zope-0.conf')
# obj = psu.byuid('578457950')
#
import atexit
import os
from transaction import commit, abort
from zope.app import wsgi
from zope.app.authentication.principalfolder import Principal
from zope.cachedescriptors.property import Lazy
from zope.catalog.interfaces import ICatalog
from zope.component.hooks import setSite
from zope.container.contained import ObjectAddedEvent, ObjectRemovedEvent
from zope.copypastemove.interfaces import IContainerItemRenamer
from zope import component
from zope.event import notify
from zope.exceptions.interfaces import DuplicationError
from zope.interface import Interface
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.publisher.browser import TestRequest as BaseTestRequest
from zope.security.management import getInteraction, newInteraction, endInteraction
#from zope.pluggableauth.plugins.principalfolder import PrincipalInfo
from cybertools.util.date import date2TimeStamp, strptime
from cybertools.util.jeep import Jeep
from loops.common import adapted, baseObject
from loops.util import getObjectForUid, getUidForObject, getCatalog, reindex
os.environ['NLS_LANG'] = 'German_Germany.UTF8'
try:
import config
except ImportError:
print('*** config file could not be loaded!')
config = None
sc = Jeep() # shortcuts
conn = None
def closeConnection():
if conn is not None:
print('closing ZODB connection...')
conn.close()
def setup(zope_conf=None, loopsRootPath='sites/loops', config=config):
if zope_conf is None:
zope_conf = getattr(config, 'zope_conf', 'zope.conf')
if loopsRootPath is None:
loopsRootPath = getattr(config, 'loops_path', None)
global conn, root, sm, smdefault, intids, pau, loopsRoot, sc
print(f'opening ZODB connection... - conf: {zope_conf}, path: {loopsRootPath}')
conn = wsgi.config(zope_conf).open()
atexit.register(closeConnection)
root = conn.root()['Application']
setSite(root)
sm = component.getSiteManager(root)
smdefault = sm['default']
intids = smdefault['IntIds']
pau = smdefault['PluggableAuthentication']
user = getattr(config, 'shell_user', 'zope.manager')
password = (getattr(config, 'shell_pw', None) or
input('Enter manager password: '))
login(Principal(user, password, u'Manager'))
loopsRoot = root
if loopsRootPath is not None:
for name in loopsRootPath.split('/'):
if name:
loopsRoot = loopsRoot[name]
sc.concepts = loopsRoot['concepts']
for name in ('standard', 'hasType',):
sc[name] = sc.concepts[name]
def byuid(uid):
return getObjectForUid(uid)
def uid(obj):
return getUidForObject(obj)
def notifyModification(obj):
obj = baseObject(obj)
notify(ObjectModifiedEvent(obj))
def save(obj):
notifyModification(obj)
commit()
def notifyAdded(obj):
obj = baseObject(obj)
notify(ObjectAddedEvent(obj))
def notifyRemoved(obj):
obj = baseObject(obj)
notify(ObjectRemovedEvent(obj))
def delete(container, name, docommit=True):
obj = container.get(name)
if obj is None:
print('*** Object', name, 'not found!')
return
notifyRemoved(obj)
del container[name]
if docommit:
commit()
def rename(container, old, new, docommit=True):
obj = container.get(old)
if obj is None:
print('*** Object', old, 'not found!')
return
renamer = IContainerItemRenamer(container)
if new != old:
try:
renamer.renameItem(old, new)
except DuplicationError:
print('*** Object', new, 'already exists!')
# container[new] = obj
# notifyAdded(obj)
notifyModification(obj)
if docommit:
commit()
def move(source, target, name):
obj = source.get(name)
if obj is None:
print('*** Object', name, 'not found!')
return
#notifyRemoved(obj)
#del source[name]
target[name] = obj
#notifyAdded(obj)
notifyModification(obj)
commit()
def get(container, obj):
if isinstance(obj, basestring):
name = obj
obj = container.get(name)
if obj is None:
print('*** Object', name, 'not found!')
return None
return adapted(obj)
# startup, loop, finish...
def startup(msg, **kw):
print('***', msg)
step = kw.pop('step', 10)
return Jeep(count=0, step=step, message=msg, **kw)
def update(fct, obj, info):
info.count += 1
start = info.get('start')
if start and info.count < start:
return
if info.count % info.step == 0:
try:
objInfo = obj.__name__
except:
try:
objInfo = obj.context.__name__
except:
objInfo = obj
print('*** Processing object # %i: %s' % (info.count, objInfo))
if info.get('updated'):
print('*** updated: %i.' % info.updated)
if info.get('errors'):
print('*** errors: %i.' % info.error)
commit()
return fct(obj, info)
def finish(info):
print('*** count: %i.' % info.count)
if info.get('updated'):
print('*** updated: %i.' % info.updated)
if info.get('errors'):
print('*** errors: %i.' % info.error)
commit()
def stop_condition(info):
stop = info.get('stop')
return stop is not None and info.count > stop
def loop(message, objects, fct, **kw):
def _fct(obj, info):
params = info.get('fctparams', {})
fct(obj, info, **params)
info = startup(message, **kw)
for obj in objects:
update(_fct, obj, info)
if stop_condition(info):
break
finish(info)
# indexing
def reindex_objects(objs, **kw):
catalog = getCatalog(objs[0])
def do_reindex(obj, info):
util.reindex(obj, catalog)
loop('reindex %s objects' % len(objs), objs, do_reindex, **kw)
def reindex_all(root, step=1000):
catalog = getCatalog(root)
def _updateObj(params, info):
uid, obj = params
for index in catalog.values():
index.index_doc(uid, obj)
info = startup('Indexing all objects', step=step)
for uid, obj in catalog._visitSublocations():
update(_updateObj, (uid, obj), info)
finish(info)
# some common repair tasks
def get_type_instances(name):
return sc.concepts[name].getChildren([sc.hasType])
def notify_modification(c, info):
notifyModification(c)
def update_type_instances(**kw):
objs = get_type_instances(kw.pop('type'))
loop('Notify Type Instances', objs, notify_modification, **kw)
def update_type_instances_title_from_adapted(**kw):
def update_type_title_from_adapted(c, info):
c.title = adapted(c).title
notifyModification(c)
objs = get_type_instances(kw.pop('type'))
loop('Update Type Instances Title', objs, update_type_title_from_adapted, **kw)
def removeRecords(container, **kw):
"""Remove records from container selected by the criteria given."""
def remove(obj, info):
notifyRemoved(obj)
del container[obj.__name__]
info = startup('Remove records', **kw)
date = kw.pop('date', None)
if date:
kw['timeFromTo'] = (
date2TimeStamp(strptime(date + ' 00:00:00')),
date2TimeStamp(strptime(date + ' 23:59:59')))
loop('Remove records', container.query(**kw), remove, **kw)
# helper functions and classes
def login(principal):
endInteraction()
newInteraction(Participation(principal))
class TestRequest(BaseTestRequest):
basePrincipal = BaseTestRequest.principal
@Lazy
def principal(self):
interaction = getInteraction()
if interaction is not None:
parts = interaction.participations
if parts:
prin = parts[0].principal
if prin is not None:
return prin
return self.basePrincipal
class Participation(object):
""" Dummy Participation class for testing.
"""
interaction = None
def __init__(self, principal):
self.principal = principal