"""General helper functions for WAeUP. """ import os import re import sys import shutil import grok from zope.component.interfaces import IFactory from zope.interface import implementedBy def removeFileOrDirectory(filepath): """Remove a file or directory. """ filepath = os.path.abspath(filepath) if not os.path.exists(filepath): return if os.path.isdir(filepath): shutil.rmtree(filepath) else: os.unlink(filepath) return def copyFileSystemTree(src, dst, overwrite=False, del_old=False): """Copy contents of directory src to directory dst. Both directories must exists. If `overwrite` is true, any same named objects will be overwritten. Otherwise these files will not be touched. If `del_old` is true, copied files and directories will be removed from the src directory. This functions returns a list of non-copied files. Unix hidden files and directories (starting with '.') are not processed by this function. """ if not os.path.exists(src): raise ValueError('source path does not exist: %s' % src) if not os.path.exists(dst): raise ValueError('destination path does not exist: %s' % dst) if not os.path.isdir(src): raise ValueError('source path is not a directory: %s' % src) if not os.path.isdir(dst): raise ValueError('destination path is not a directory: %s' % dst) not_copied = [] for item in os.listdir(src): if item.startswith('.'): continue # We do not copy hidden stuff... itemsrc = os.path.join(src, item) itemdst = os.path.join(dst, item) if os.path.exists(itemdst): if overwrite is True: removeFileOrDirectory(itemdst) else: not_copied.append(item) continue if os.path.isdir(itemsrc): shutil.copytree(itemsrc, itemdst) else: shutil.copy2(itemsrc, itemdst) if del_old: removeFileOrDirectory(itemsrc) return not_copied def getInnerHTMLPart(html_code): """Return the 'inner' part of a complete HTML snippet. If there is a form part, get this. If there is no form part, try to return the body part contents. If there is no body, return as-is. """ try: result = re.match('^.+(]*>.*).+$', html_code, re.DOTALL).groups()[0] return result except AttributeError: # No
part included try: result = re.match('^.+]*>(.*).*$', html_code, re.DOTALL).groups()[0] return result except AttributeError: # No and no tag... pass return html_code def getName(context): """Construct a name out of an object with prefix and title. The `context` has to provide `title_prefix` and `title` attributes. >>> from waeup.sirp.utils.helpers import getName >>> class FakeObject(object): ... title_prefix = 'department' ... title = 'Strange Things' >>> getName(FakeObject()) 'Department of Strange Things' As we can see in the result the `title_prefix` is rendered uppercase. """ prefix = context.title_prefix prefix = prefix[0].upper() + prefix[1:] return '%s of %s' % (prefix, context.title) class FactoryBase(grok.GlobalUtility): """A factory for things. This is a baseclass for easier creation of factories. Factories are utilities that are registered under a certain name and return instances of certain classes when called. In :mod:`waeup.sirp` we use factories extensively for batching. While processing a batch some importer looks up a factory to create real-world instances that then get filled with data from imported CSV files. To get rid of reimplementing the same stuff over and over again, most notably the methods defined here, we offer this base class (which will *not* be registered as a factory itself). Real factories can then be created like this: >>> import grok >>> from waeup.sirp.utils.helpers import FactoryBase >>> class MyObject(object): ... # Some class we want to get instances of. ... pass >>> class MyObjectFactory(FactoryBase): ... # This is the factory for MyObject instances ... grok.name(u'waeup.sirp.factory.MyObject') ... factory = MyObject That's it. It is essential to set the ``factory`` attribute, which will determine the class of which instances should be created when called. The given name must even be unique amongst all utilities registered during runtime. While you can pick any name you like you might want to prepend ``waeup.sirp.factory.`` to the name string to make sure it does not clash with names of other utilities one day. Before all this works we have to grok the baseclass once and our freshly defined factory. This executes all the component registration stuff we don't want to do ourselves. In daily use this is done automatically on startup of a :mod:`waeup.sirp` system. >>> grok.testing.grok('waeup.sirp.utils.helpers') >>> grok.testing.grok_component( ... 'MyObjectFactory', MyObjectFactory ... ) True After grokking we (and importers) can create objects without knowing about the location of the real class definition, just by the factory name: >>> from zope.component import createObject >>> obj = createObject('waeup.sirp.factory.MyObject') >>> isinstance(obj, MyObject) True We can also use the regular utility lookups to find our new factory: >>> from zope.component import getUtility >>> from zope.component.interfaces import IFactory >>> factory = getUtility( ... IFactory, name='waeup.sirp.factory.MyObject' ... ) >>> isinstance(factory, MyObjectFactory) True And this factory generates `MyObject` instances: >>> obj = factory() >>> isinstance(obj, MyObject) True """ grok.baseclass() # Do not grok this class, do not register us. grok.implements(IFactory) # You can override any of the following attributes in derived # classes. The `grok.name` setting *must* even be set to some # unique value. grok.name(u'waeup.Factory') title = u"Create instances of ``factory``.", description = u"This factory instantiates new applicant instances." factory = None def __call__(self, *args, **kw): """The main factory function. Returns an instance of the requested object. """ return self.factory() def getInterfaces(self): # Required by IFactory return implementedBy(self.factory)