moksha.common-1.2.3/0000755000175000017500000000000012326216031016176 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha.common.egg-info/0000755000175000017500000000000012326216031022441 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha.common.egg-info/PKG-INFO0000644000175000017500000000140112326216031023532 0ustar threebeanthreebean00000000000000Metadata-Version: 1.1 Name: moksha.common Version: 1.2.3 Summary: Common components for Moksha Home-page: http://moksha.fedorahosted.org Author: Luke Macken, John (J5) Palmieri, Mairin Duffy, and Ralph Bean Author-email: UNKNOWN License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 moksha.common-1.2.3/moksha.common.egg-info/SOURCES.txt0000644000175000017500000000157312326216031024333 0ustar threebeanthreebean00000000000000AUTHORS COPYING MANIFEST.in README setup.py moksha/__init__.py moksha.common.egg-info/PKG-INFO moksha.common.egg-info/SOURCES.txt moksha.common.egg-info/dependency_links.txt moksha.common.egg-info/entry_points.txt moksha.common.egg-info/namespace_packages.txt moksha.common.egg-info/requires.txt moksha.common.egg-info/top_level.txt moksha/common/__init__.py moksha/common/config.py moksha/common/commands/__init__.py moksha/common/commands/cli.py moksha/common/exc/__init__.py moksha/common/lib/__init__.py moksha/common/lib/cache.py moksha/common/lib/converters.py moksha/common/lib/dates.py moksha/common/lib/helpers.py moksha/common/lib/reflect.py moksha/common/tests/__init__.py moksha/common/tests/test_config.ini moksha/common/tests/test_config.py moksha/common/tests/test_helpers.py moksha/common/testtools/__init__.py moksha/common/testtools/utils.py moksha/common/utils/__init__.pymoksha.common-1.2.3/moksha.common.egg-info/dependency_links.txt0000644000175000017500000000000112326216031026507 0ustar threebeanthreebean00000000000000 moksha.common-1.2.3/moksha.common.egg-info/requires.txt0000644000175000017500000000003212326216031025034 0ustar threebeanthreebean00000000000000six decorator pytz kitchenmoksha.common-1.2.3/moksha.common.egg-info/top_level.txt0000644000175000017500000000000712326216031025170 0ustar threebeanthreebean00000000000000moksha moksha.common-1.2.3/moksha.common.egg-info/namespace_packages.txt0000644000175000017500000000000712326216031026771 0ustar threebeanthreebean00000000000000moksha moksha.common-1.2.3/moksha.common.egg-info/entry_points.txt0000644000175000017500000000011012326216031025727 0ustar threebeanthreebean00000000000000 [console_scripts] moksha = moksha.common.commands.cli:main moksha.common-1.2.3/PKG-INFO0000644000175000017500000000140112326216031017267 0ustar threebeanthreebean00000000000000Metadata-Version: 1.1 Name: moksha.common Version: 1.2.3 Summary: Common components for Moksha Home-page: http://moksha.fedorahosted.org Author: Luke Macken, John (J5) Palmieri, Mairin Duffy, and Ralph Bean Author-email: UNKNOWN License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 moksha.common-1.2.3/setup.cfg0000644000175000017500000000007312326216031020017 0ustar threebeanthreebean00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 moksha.common-1.2.3/README0000644000175000017500000000016012320571512017055 0ustar threebeanthreebean00000000000000====== Moksha ====== http://mokshaproject.net Documentation ------------- http://mokshaproject.net/apps/docs moksha.common-1.2.3/COPYING0000644000175000017500000002613612320571512017243 0ustar threebeanthreebean00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. moksha.common-1.2.3/setup.py0000644000175000017500000000407212326216021017712 0ustar threebeanthreebean00000000000000# This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from setuptools import setup, find_packages import sys # This is required (oddly) to fix a python 2.7 bug with nose tests. try: import multiprocessing import logging except Exception: pass tests_require = [ 'nose', 'mock', ] if sys.version_info[0] == 2 and sys.version_info[1] <= 6: tests_require.extend([ 'unittest2', ]) setup( name='moksha.common', version='1.2.3', description='Common components for Moksha', author='Luke Macken, John (J5) Palmieri, Mairin Duffy, and Ralph Bean', author_email='', url='http://moksha.fedorahosted.org', install_requires=[ "six", "decorator", "pytz", "kitchen", ], classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", ], packages=find_packages(exclude=['ez_setup']), include_package_data=True, test_suite='nose.collector', namespace_packages=['moksha'], tests_require=tests_require, entry_points=""" [console_scripts] moksha = moksha.common.commands.cli:main """, ) moksha.common-1.2.3/moksha/0000755000175000017500000000000012326216031017460 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/__init__.py0000644000175000017500000000007012320571512021570 0ustar threebeanthreebean00000000000000__import__('pkg_resources').declare_namespace(__name__) moksha.common-1.2.3/moksha/common/0000755000175000017500000000000012326216031020750 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/exc/0000755000175000017500000000000012326216031021527 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/exc/__init__.py0000644000175000017500000000153212320571512023643 0ustar threebeanthreebean00000000000000# This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Exceptions used with Moksha """ class MokshaException(Exception): pass class ApplicationNotFound(MokshaException): pass class WidgetNotFound(MokshaException): pass class CacheBackendException(MokshaException): pass moksha.common-1.2.3/moksha/common/utils/0000755000175000017500000000000012326216031022110 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/utils/__init__.py0000644000175000017500000000234512320571512024227 0ustar threebeanthreebean00000000000000# This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # The root controller class root = None # All loaded moksha applications _apps = None # All loaded ToscaWidgets _widgets = None # All WSGI applications wsgiapps = None # All loaded moksha menus menus = None def get_widget(name): """ Get a widget instance by name """ if _widgets: return _widgets[name]['widget'] def get_widgets(): """ Return a dictionary of all widgets """ from pkg_resources import iter_entry_points return _widgets or [widget.load() for widget in iter_entry_points('moksha.widget')] def get_app(name): """ Get an app controller by name """ return _apps[name]['controller'] moksha.common-1.2.3/moksha/common/config.py0000644000175000017500000000440312326215751022600 0ustar threebeanthreebean00000000000000import os try: import configparser except ImportError: import ConfigParser as configparser class EnvironmentConfigParser(configparser.ConfigParser): """ConfigParser which is able to substitute environment variables. """ def get(self, section, option, raw=0, vars=None): try: val = configparser.ConfigParser.get( self, section, option, raw=raw, vars=vars) except Exception: val = configparser.ConfigParser.get( self, section, option, raw=1, vars=vars) return self._interpolate(section, option, val, vars) def _interpolate(self, section, option, rawval, vars): """Adds the additional key-value pairs to the interpolation. Also allows to use default values in %(KEY:-DEFAULT)s""" if not vars: vars = {} for k, v in os.environ.items(): if not k in vars.keys(): vars[k] = v value = rawval depth = configparser.MAX_INTERPOLATION_DEPTH while depth: # Loop through this until it's done depth -= 1 start = value.find("%(") if start > -1: end = value.find(")s", start) if end <= start: raise ValueError( "configparser: no \")s\" found " "after \"%(\" : " + rawval) rawkey = value[start + 2:end] default = None key = rawkey sep = rawkey.find(':-') if sep > -1: default = rawkey[sep + 2:] key = key[:sep] if key in vars: value = value.replace("%(" + rawkey + ")s", vars[key], 1) else: if default: value = value.replace("%(" + rawkey + ")s", default, 1) else: raise ValueError( "configparser: Key %s not found in: %s" % ( rawval, key)) else: break if value.find("%(") != -1: raise ValueError( "configparser: Interpolation Depth error: %s" % rawval) return value moksha.common-1.2.3/moksha/common/commands/0000755000175000017500000000000012326216031022551 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/commands/__init__.py0000644000175000017500000000000012320571512024652 0ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/commands/cli.py0000644000175000017500000001057612320571512023705 0ustar threebeanthreebean00000000000000""" The Moksha Command-line Interface """ from __future__ import print_function import os import sys import signal import logging import pkg_resources from optparse import OptionParser from twisted.internet import protocol from twisted.internet import reactor log = logging.getLogger(__name__) pids = [] class MokshaProcessProtocol(protocol.ProcessProtocol): def __init__(self, name): self.name = name def connectionMade(self): pass def outReceived(self, data): sys.stdout.write(data) def errReceived(self, data): sys.stderr.write(data) def inConnectionLost(self): pass def outConnectionLost(self): pass def errConnectionLost(self): pass def processEnded(self, status_object): print("Process %r quit with status %d" % ( self.name, status_object.value.exitCode)) reactor.stop() for pid in pids: try: os.kill(pid, signal.SIGTERM) except OSError: pass class MokshaCLI(object): def _exec(self, process, *args, **kw): args = args and [process] + list(args) or [process] print("Running %s" % ' '.join(args)) pp = MokshaProcessProtocol(name=process) process = reactor.spawnProcess(pp, process, args, env={'PYTHONPATH': os.getcwd()}, **kw) pids.append(process.pid) def start(self): """ Start all of the Moksha components """ from moksha.lib.helpers import get_moksha_config_path orbited = ['orbited'] if os.path.exists('/etc/moksha/orbited.cfg'): orbited += ['-c', '/etc/moksha/orbited.cfg'] self._exec(*orbited) self._exec('paster', 'serve', get_moksha_config_path()) self._exec('moksha-hub', '-v') def list(self): """ List all available apps, widgets, producers and consumers """ entry_points = ('root', 'widget', 'application', 'wsgiapp', 'producer', 'consumer') for entry in entry_points: print("[moksha.%s]" % entry) for obj_entry in pkg_resources.iter_entry_points('moksha.' + entry): print(" * %s" % obj_entry.name) print() def install(self): """ Install a Moksha component """ def uninstall(self): """ Uninstall a Moksha component """ def quickstart(self): """ Create a new Moksha component """ # If no arguments given, run `paster moksha --help` def send(self, topic, message): """ Send a message to a topic """ from moksha.hub.api import MokshaHub, reactor hub = MokshaHub() print("send_message(%s, %s)" % (topic, message)) hub.send_message(topic, {'msg': message}) def stop_reactor(): hub.close() reactor.stop() reactor.callLater(0.2, stop_reactor) reactor.run() def get_parser(): usage = 'usage: %prog [command]' parser = OptionParser(usage, description=__doc__) parser.add_option('', '--start', action='store_true', dest='start', help='Start Moksha') parser.add_option('', '--list', action='store_true', dest='list', help='List all installed Moksha components') parser.add_option('', '--send', action='store_true', dest='send', help='Send a message to a given topic. Usage: send ') return parser def main(): parser = get_parser() opts, args = parser.parse_args() pkg_resources.working_set.add_entry(os.getcwd()) moksha = MokshaCLI() logging.basicConfig(level=logging.INFO, format= '%(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s') stdout = logging.StreamHandler(sys.stdout) stdout.setFormatter(logging.Formatter('%(message)s')) log.addHandler(stdout) if opts.start or 'start' in args: print("Starting Moksha...") moksha.start() try: reactor.run() except Exception as e: print("Caught exception: %s" % str(e)) moksha.stop() elif opts.list or 'list' in args: moksha.list() elif opts.send or 'send' in args: if len(sys.argv) != 4: log.error('Usage: moksha send ') sys.exit(-1) moksha.send(sys.argv[2], sys.argv[3]) else: parser.print_help() if __name__ == '__main__': main() moksha.common-1.2.3/moksha/common/__init__.py0000644000175000017500000000000012320571512023051 0ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/lib/0000755000175000017500000000000012326216031021516 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/lib/cache.py0000644000175000017500000000304012320571512023132 0ustar threebeanthreebean00000000000000# This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging log = logging.getLogger(__name__) from moksha.exc import CacheBackendException class Cache(object): """ A memcached-specific caching interface """ def __init__(self, url, timeout=None, prefix=None): try: import memcache except ImportError: log.warning('Cannot import the `memcache` module. Install the ' 'python-memcached package to enable Mokshas memcached ' 'integration.') return self.mc = memcache.Client([url]) self.timeout = timeout or 300 self.prefix = prefix if not self.mc.set('x', 'x', 1): raise CacheBackendException("Cannot connect to Memcached") def get(self, key): return self.mc.get(key) def set(self, key, value, timeout=None): if self.prefix: key = self.prefix + key self.mc.set(key, value, timeout or self.timeout) moksha.common-1.2.3/moksha/common/lib/helpers.py0000644000175000017500000000554212326215764023554 0ustar threebeanthreebean00000000000000from __future__ import print_function # This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import moksha.common.utils import moksha.common.config import re import os import logging import warnings try: import configparser except ImportError: import ConfigParser as configparser log = logging.getLogger(__name__) scrub_filter = re.compile('[^_a-zA-Z0-9-]') def get_moksha_config_path(): """ :returns: The path to Moksha's configuration file. """ for config_path in ('.', '/etc/moksha/', __file__ + '/../../../'): for config_file in ('production.ini', 'development.ini'): cfg = os.path.join(os.path.abspath(config_path), config_file) if os.path.isfile(cfg): return cfg log.warn('No moksha configuration file found, make sure the ' 'controlling app is fully configured') return None def get_moksha_dev_config(): fname = 'development.ini' cfgs = [ os.path.join(os.path.abspath(__file__ + '/../../../'), fname), os.path.join(os.path.abspath(__file__ + '/../../../../'), fname), os.path.join(os.getcwd(), fname), '/etc/moksha/%s' % fname, ] for cfg in cfgs: if os.path.isfile(cfg): return cfg log.warn("Cannot find configuration in %r" % cfgs) def get_moksha_appconfig(): """ Return the appconfig of Moksha """ return appconfig('config:' + get_moksha_config_path()) def appconfig(config_path): """ Our own reimplementation of paste.deploy.appconfig """ if config_path.startswith('config:'): config_path = config_path[7:] here = os.path.abspath(os.path.dirname(config_path)) parser = moksha.common.config.EnvironmentConfigParser({"here": here}) parser.read(filenames=[config_path]) try: return dict(parser.items('app:main')) except configparser.NoSectionError: for section in parser.sections(): if section.startswith('app:'): print("Using %r" % section) return dict(parser.items(section)) raise configparser.NoSectionError("Couldn't find app: section.") def create_app_engine(app, config): """ Create a new SQLAlchemy engine for a given app """ from sqlalchemy import create_engine return create_engine(config.get('app_db', 'sqlite:///%s.db' % app)) moksha.common-1.2.3/moksha/common/lib/converters.py0000644000175000017500000000176712326012501024271 0ustar threebeanthreebean00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste # (http://pythonpaste.org) # Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php import six def asbool(obj): if isinstance(obj, six.string_types): obj = obj.strip().lower() if obj in ['true', 'yes', 'on', 'y', 't', '1']: return True elif obj in ['false', 'no', 'off', 'n', 'f', '0']: return False else: raise ValueError("String is not true/false: %r" % obj) return bool(obj) def asint(obj): try: return int(obj) except (TypeError, ValueError): raise ValueError("Bad integer value: %r" % obj) def aslist(obj, sep=None, strip=True): if isinstance(obj, six.string_types): lst = obj.split(sep) if strip: lst = [v.strip() for v in lst] return lst elif isinstance(obj, (list, tuple)): return obj elif obj is None: return [] else: return [obj] moksha.common-1.2.3/moksha/common/lib/dates.py0000644000175000017500000002724512320571512023204 0ustar threebeanthreebean00000000000000# This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time import datetime def utc_offset(tz): """ Return the UTC offset for a given timezone. >>> utc_offset('US/Eastern') '-4' """ utc_offset = '' now = datetime.now(utc) now = now.astimezone(timezone(tz)) offset = now.strftime('%z') if offset.startswith('-'): offset = offset[1:] utc_offset += '-' hours = int(offset[:2]) utc_offset += str(hours) # FIXME: account for minutes? #minutes = int(offset[2:]) #if minutes: # utc_offset += '.%d' % ... return utc_offset class DateTimeDisplay(object): """ DateTimeDisplay is an object which takes any number of datetime objects and process them for display:: >>> from datetime import datetime >>> now = datetime(2009, 5, 12) >>> later = datetime(2009, 5, 13) >>> d = DateTimeDisplay(now) >>> print d 2009-05-12 00:00:00 >>> d.age(later) '1 day' >>> d.age(datetime(2010, 7, 10, 10, 10), granularity='minute') '1 year, 1 month, 29 days, 10 hours and 10 minutes' >>> d.age(datetime(2010, 7, 10, 10, 10), tz='Europe/Amsterdam') '1 year, 1 month, 29 days and 10 hours' >>> d = DateTimeDisplay(datetime(2009, 5, 12, 12, 0, 0)) >>> d.timestamp datetime.datetime(2009, 5, 12, 12, 0) >>> d.astimezone('Europe/Amsterdam') datetime.datetime(2009, 5, 12, 14, 0, tzinfo=) """ def __init__(self, timestamp, format='%Y-%m-%d %H:%M:%S'): if isinstance(timestamp, basestring) and '.' in timestamp: timestamp = timestamp.split('.')[0] self.timestamp = timestamp if isinstance(timestamp, datetime.datetime): self.datetime = timestamp elif isinstance(timestamp, time.struct_time): self.datetime = datetime.datetime(*timestamp[:-2]) elif isinstance(timestamp, basestring): if hasattr(datetime, 'strptime'): # Python 2.5+ self.datetime = datetime.datetime.strptime(timestamp, format) else: # Python 2.4 self.datetime = datetime.datetime(*time.strptime(timestamp, format)[:-2]) else: raise Exception("You must provide either a datetime object or a" "string, not %s" % type(timestamp)) def astimezone(self, tz): """ Return `self.datetime` as a different timezone """ timestamp = self.datetime.replace(tzinfo=utc) zone = timezone(tz) return zone.normalize(timestamp.astimezone(zone)) def age(self, end=None, tz=None, granularity='hour', general=False): """ Return the distance of time in words from `self.datetime` to `end`. >>> start = datetime(1984, 11, 02) >>> now = datetime(2009, 5, 22, 12, 11, 10) >>> DateTimeDisplay(start).age(now) '2 decades, 4 years, 6 months, 20 days and 12 hours' >>> DateTimeDisplay(start).age(now, general=True) '2 decades' """ start = self.datetime if not end: end = datetime.datetime.utcnow() else: if isinstance(end, DateTimeDisplay): end = end.datetime if tz: zone = timezone(tz) end = end.replace(tzinfo=utc) end = zone.normalize(end.astimezone(zone)) start = self.astimezone(tz) age = distance_of_time_in_words(start, end, granularity=granularity) if general: if not age.startswith('less than'): age = age.split('and')[0].split(',')[0] return age def __str__(self): return self.datetime.strftime('%Y-%m-%d %H:%M:%S %Z%z') def __repr__(self): return "" % self.datetime # The following functions were all copied *wholesale* from webhelpers.date def _process_carryover(deltas, carry_over): """A helper function to process negative deltas based on the deltas and the list of tuples that contain the carry over values""" for smaller, larger, amount in carry_over: if deltas[smaller] < 0: deltas[larger] -= 1 deltas[smaller] += amount def _pluralize_granularity(granularity): """Pluralize the given granularity""" if 'century' == granularity: return "centuries" return granularity + "s" def _delta_string(delta, granularity): """Return the string to use for the given delta and ordinality""" if 1 == delta: return "1 " + granularity elif delta > 1: return str(delta) + " " + _pluralize_granularity(granularity) def _is_leap_year(year): if year % 4 == 0 and year % 400 != 0: return True return False def distance_of_time_in_words(from_time, to_time=0, granularity="second", round=False): """ Return the absolute time-distance string for two datetime objects, ints or any combination you can dream of. If times are integers, they are interpreted as seconds from now. ``granularity`` dictates where the string calculation is stopped. If set to seconds (default) you will receive the full string. If another accuracy is supplied you will receive an approximation. Available granularities are: 'century', 'decade', 'year', 'month', 'day', 'hour', 'minute', 'second' Setting ``round`` to true will increase the result by 1 if the fractional value is greater than 50% of the granularity unit. Examples: >>> distance_of_time_in_words(86399, round=True, granularity='day') '1 day' >>> distance_of_time_in_words(86399, granularity='day') 'less than 1 day' >>> distance_of_time_in_words(86399) '23 hours, 59 minutes and 59 seconds' >>> distance_of_time_in_words(datetime(2008,3,21, 16,34), ... datetime(2008,2,6,9,45)) '1 month, 15 days, 6 hours and 49 minutes' >>> distance_of_time_in_words(datetime(2008,3,21, 16,34), ... datetime(2008,2,6,9,45), granularity='decade') 'less than 1 decade' >>> distance_of_time_in_words(datetime(2008,3,21, 16,34), ... datetime(2008,2,6,9,45), granularity='second') '1 month, 15 days, 6 hours and 49 minutes' """ granularities = ['century', 'decade', 'year', 'month', 'day', 'hour', 'minute', 'second'] # 15 days in the month is a gross approximation, but this # value is only used if rounding to the nearest month granularity_size = {'century': 10, 'decade': 10, 'year': 10, 'month': 12, 'day': 15, 'hour': 24, 'minute': 60, 'second': 60 } if granularity not in granularities: raise ValueError("Please provide a valid granularity: %s" % (granularities)) # Get everything into datetimes if isinstance(from_time, int): from_time = datetime.datetime.fromtimestamp(time.time()+from_time) if isinstance(to_time, int): to_time = datetime.datetime.fromtimestamp(time.time()+to_time) # Ensure that the to_time is the larger if from_time > to_time: s = from_time from_time = to_time to_time = s # Stop if the tiems are equal elif from_time == to_time: return "0 " + _pluralize_granularity(granularity) # Collect up all the differences deltas = {'century': 0, 'decade': 0, 'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 0, 'second' : 0} # Collect the easy deltas for field in ['month', 'hour', 'day', 'minute', 'second']: deltas[field] = getattr(to_time,field) - getattr(from_time,field) # deal with year, century and decade delta_year = to_time.year - from_time.year if delta_year >= 100: deltas['century'] = delta_year // 100 if delta_year % 100 >= 10: deltas['decade'] = delta_year // 10 - deltas['century'] * 10 if delta_year % 10: deltas['year'] = delta_year % 10 # Now we need to deal with the negative deltas, as we move from # the smallest granularity to the largest when we encounter a negative # we will 'borrow' from the next highest value. Because to_time is # the larger of the two, carry_over = [('second', 'minute', granularity_size['second']), ('minute', 'hour', granularity_size['minute']), ('hour', 'day', granularity_size['hour'])] _process_carryover(deltas, carry_over) # Day is its own special animal. We need to deal with negative days # differently depending on what months we are spanning across. We need to # look up the from_time.month value in order to bring the number of days # to the end of the month. month_carry = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] if deltas['day'] < 0: deltas['month'] -= 1 # Deal with leap years if (from_time.month) == 2 and _is_leap_year(from_time.year): deltas['day'] += 29 else: deltas['day'] += month_carry[from_time.month] carry_over = [('month', 'year', granularity_size['month']), ('year', 'decade', granularity_size['year']), ('decade', 'century', granularity_size['decade'])] _process_carryover(deltas, carry_over) # Display the differences we care about, at this point we should only have # positive deltas return_strings = [] for g in granularities: delta = deltas[g] # This is the finest granularity we will display if g == granularity: # We can only use rounding if the granularity is higher than # seconds if round and g != 'second': i = granularities.index(g) # Get the next finest granularity and it's delta g_p = granularities[i + 1] delta_p = deltas[g_p] # Determine if we should round up if delta_p > granularity_size[g_p] / 2: delta += 1 if delta != 0: return_strings.append(_delta_string(delta, g)) if not return_strings: return "less than 1 " + granularity break else: if delta != 0: return_strings.append(_delta_string(delta, g)) # We're not rounding, check to see if we have encountered # any deltas to display, if not our time difference # is less than our finest granularity if not return_strings: return "less than 1 " + granularity break # Read the value and continue else: if delta != 0: return_strings.append(_delta_string(delta, g)) if len(return_strings) == 1: return return_strings[0] return ", ".join(return_strings[:-1]) + " and " + return_strings[-1] def time_ago_in_words(from_time, granularity="second", round=False): """ Return approximate-time-distance string for ``from_time`` till now. Same as ``distance_of_time_in_words`` but the endpoint is now. """ return distance_of_time_in_words(from_time, datetime.datetime.now(), granularity, round) moksha.common-1.2.3/moksha/common/lib/__init__.py0000644000175000017500000000000012320571512023617 0ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/lib/reflect.py0000644000175000017500000001014412320571512023516 0ustar threebeanthreebean00000000000000# This is a copy/paste from twisted.python.reflect. class _NoModuleFound(Exception): """ No module was found because none exists. """ class InvalidName(ValueError): """ The given name is not a dot-separated list of Python objects. """ class ModuleNotFound(InvalidName): """ The module associated with the given name doesn't exist and it can't be imported. """ class ObjectNotFound(InvalidName): """ The object associated with the given name doesn't exist and it can't be imported. """ def _importAndCheckStack(importName): """ Import the given name as a module, then walk the stack to determine whether the failure was the module not existing, or some code in the module (for example a dependent import) failing. This can be helpful to determine whether any actual application code was run. For example, to distiguish administrative error (entering the wrong module name), from programmer error (writing buggy code in a module that fails to import). @raise Exception: if something bad happens. This can be any type of exception, since nobody knows what loading some arbitrary code might do. @raise _NoModuleFound: if no module was found. """ try: try: return __import__(importName) except ImportError: excType, excValue, excTraceback = sys.exc_info() while excTraceback: execName = excTraceback.tb_frame.f_globals["__name__"] if (execName is None or # python 2.4+, post-cleanup execName == importName): # python 2.3, no cleanup raise excType, excValue, excTraceback excTraceback = excTraceback.tb_next raise _NoModuleFound() except: # Necessary for cleaning up modules in 2.3. sys.modules.pop(importName, None) raise def namedAny(name): """ Retrieve a Python object by its fully qualified name from the global Python module namespace. The first part of the name, that describes a module, will be discovered and imported. Each subsequent part of the name is treated as the name of an attribute of the object specified by all of the name which came before it. For example, the fully-qualified name of this object is 'twisted.python.reflect.namedAny'. @type name: L{str} @param name: The name of the object to return. @raise InvalidName: If the name is an empty string, starts or ends with a '.', or is otherwise syntactically incorrect. @raise ModuleNotFound: If the name is syntactically correct but the module it specifies cannot be imported because it does not appear to exist. @raise ObjectNotFound: If the name is syntactically correct, includes at least one '.', but the module it specifies cannot be imported because it does not appear to exist. @raise AttributeError: If an attribute of an object along the way cannot be accessed, or a module along the way is not found. @return: the Python object identified by 'name'. """ if not name: raise InvalidName('Empty module name') names = name.split('.') # if the name starts or ends with a '.' or contains '..', the __import__ # will raise an 'Empty module name' error. This will provide a better error # message. if '' in names: raise InvalidName( "name must be a string giving a '.'-separated list of Python " "identifiers, not %r" % (name,)) topLevelPackage = None moduleNames = names[:] while not topLevelPackage: if moduleNames: trialname = '.'.join(moduleNames) try: topLevelPackage = _importAndCheckStack(trialname) except _NoModuleFound: moduleNames.pop() else: if len(names) == 1: raise ModuleNotFound("No module named %r" % (name,)) else: raise ObjectNotFound('%r does not name an object' % (name,)) obj = topLevelPackage for n in names[1:]: obj = getattr(obj, n) return obj moksha.common-1.2.3/moksha/common/testtools/0000755000175000017500000000000012326216031023010 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/testtools/utils.py0000644000175000017500000001046712320571512024534 0ustar threebeanthreebean00000000000000# This file is part of Moksha. # Copyright (C) 2008-2010 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Utilities to make writing tests easier """ import functools import socket old_school = False try: # For python-2.6 import unittest2 as unittest old_school = True except ImportError: import unittest from moksha.common.lib.helpers import get_moksha_appconfig def crosstest(method): @functools.wraps(method) def wrapper(self): for setup, name in self._setUp(): def _inner(name): if not old_school: setup() else: try: setup() except unittest.SkipTest: # For python-2.6 # nose doesn't know how to catch this, so we just # return. The test "passes" which is incorrect. It # should be skipped. return try: return method(self) finally: self._tearDown() yield _inner, name return wrapper config_sets = { 'stomp': { "moksha.livesocket": "True", "moksha.livesocket.backend": "stomp", "orbited_host": "localhost", "orbited_port": "9000", "orbited_scheme": "http", "stomp_broker": "localhost", "stomp_port": "61613", "stomp_user": "guest", "stomp_pass": "guest", }, 'amqp': { "moksha.livesocket": "True", "moksha.livesocket.backend": "amqp", "orbited_host": "localhost", "orbited_port": "9000", "orbited_scheme": "http", "amqp_broker": "guest/guest@localhost", "amqp_broker_host": "localhost", "amqp_broker_port": "5672", "amqp_broker_user": "guest", "amqp_broker_pass": "guest", "amqp_broker_ssl": "False", }, 'zeromq': { "moksha.livesocket": "True", "moksha.livesocket.backend": "websocket", "moksha.livesocket.websocket.port": "9998", "zmq_enabled": "True", "zmq_publish_endpoints": "tcp://*:6543", "zmq_subscribe_endpoints": "tcp://127.0.0.1:6543", "zmq_strict": "True", }, } flash_keys = [] for name, config_set in config_sets.items(): flash_keys.extend(config_set.keys()) flash_keys = list(set(flash_keys)) def should_skip_config_set(name, config_set): if name == 'stomp': address = (config_set['stomp_broker'], config_set['stomp_port']) # If we can connect, then run tests. If we can't, then don't. try: s = socket.create_connection(address) s.close() return False except: return True elif name == 'amqp': address = (config_set['amqp_broker_host'], config_set['amqp_broker_port']) # If we can't import qpid, then bail... but also: # If we can connect, then run tests. If we can't, then don't. try: import qpid s = socket.create_connection(address) s.close() return False except: return True elif name == 'zeromq': return False else: raise ValueError("Unknown config set name.") def make_setup_functions(kernel): for name, config_set in config_sets.items(): def __setup(): if should_skip_config_set(name, config_set): raise unittest.SkipTest("%s is not available." % name) config = get_moksha_appconfig() for key in flash_keys: if key in config: del config[key] for key, value in config_set.items(): config[key] = value kernel(config) yield __setup, name moksha.common-1.2.3/moksha/common/testtools/__init__.py0000644000175000017500000000000012320571512025111 0ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/tests/0000755000175000017500000000000012326216031022112 5ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/tests/test_config.py0000644000175000017500000000162112320571512024772 0ustar threebeanthreebean00000000000000import os import moksha.common.config from nose.tools import eq_, raises def load_config(filename='/test_config.ini'): p = moksha.common.config.EnvironmentConfigParser() filename = '/'.join(__file__.split('/')[:-1] + [filename]) p.read(filenames=[filename]) return p def test_config(): expected = 'test_value 123' os.environ['test_variable'] = expected p = load_config() actual = p.get('test', 'test') eq_(actual, expected) @raises(ValueError) def test_invalid_config(): expected = 'test_value 123' os.environ['test_variable'] = expected p = load_config() p.get('test_invalid_config', 'test') def test_default_config_value(): p = load_config('/test_config.ini') eq_(p.get('test_default', 'test'), 'bar') @raises(ValueError) def test_missing_config_variable(): p = load_config('/test_config.ini') p.get('test_missing_variable', 'test') moksha.common-1.2.3/moksha/common/tests/__init__.py0000644000175000017500000000000012320571512024213 0ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/tests/test_helpers.py0000644000175000017500000000000012320571512025155 0ustar threebeanthreebean00000000000000moksha.common-1.2.3/moksha/common/tests/test_config.ini0000644000175000017500000000022312320571512025116 0ustar threebeanthreebean00000000000000[test] test=%(test_variable)s [test_default] test=%(foo:-bar)s [test_missing_variable] test=%(biz)s [test_invalid_config] test=%(test_variable) moksha.common-1.2.3/AUTHORS0000644000175000017500000000027712320571512017256 0ustar threebeanthreebean00000000000000Luke Macken John (J5) Palmieri Mairin Duffy Ralph Bean Logo design by Michael Langlie moksha.common-1.2.3/MANIFEST.in0000644000175000017500000000012312320571512017732 0ustar threebeanthreebean00000000000000include LICENSE README AUTHORS COPYING include moksha/common/tests/test_config.ini