commit d0b8fdecba2cb56faf43a86e0c8fd91a84031748 Author: cecilkorik Date: Mon Jul 16 18:02:45 2012 -0600 initial add of pycecil diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..2c9154d --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +syntax: glob +*.pyc diff --git a/buildnum b/buildnum new file mode 100644 index 0000000..295c01a --- /dev/null +++ b/buildnum @@ -0,0 +1,4 @@ +(S'1.0.0-build' +p1 +I10 +t. \ No newline at end of file diff --git a/cecil/__init__.py b/cecil/__init__.py new file mode 100644 index 0000000..95b1af6 --- /dev/null +++ b/cecil/__init__.py @@ -0,0 +1,11 @@ + +__doc__ = """ +Cecil's Python Modules + +Full of awesome little tools that do magic and stuff. +""".lstrip() + +__all__ = ["core"] + +import preconfig + diff --git a/cecil/core/__init__.py b/cecil/core/__init__.py new file mode 100644 index 0000000..0f31680 --- /dev/null +++ b/cecil/core/__init__.py @@ -0,0 +1,8 @@ + +__doc__ = """ +Core modules from Cecil's library +""".lstrip() + +__all__ = ["objproxy", "config"] + +import objproxy, config diff --git a/cecil/core/config.py b/cecil/core/config.py new file mode 100644 index 0000000..6cedd0f --- /dev/null +++ b/cecil/core/config.py @@ -0,0 +1,119 @@ +import os, sys +import inifile +import objproxy +from cecil import preconfig + + +config = objproxy.Proxy({}) +paths = [] +filenames = [] + +def init(appname=None): + global paths, filenames + paths = [] + filenames = [] + if appname != None: + if os.name == 'posix': + if 'HOME' in os.environ: + paths.append(os.path.join(os.environ['HOME'], appname)) + paths.append(os.path.join('/etc', appname)) + filenames.extend([x % (appname) for x in ('%s.conf', '%s.ini', '%s.cfg', '%s.cnf')]) + + elif os.name == 'nt': + if 'APPDATA' in os.environ: + paths.append(os.path.join(os.environ['APPDATA'], appname)) + if 'LOCALAPPDATA' in os.environ: + paths.append(os.path.join(os.environ['LOCALAPPDATA'], appname)) + filenames.extend([x % (appname) for x in ('%s.ini', '%s.conf', '%s.cfg', '%s.cnf')]) + + + if os.name == 'posix': + if 'HOME' in os.environ: + paths.append(os.environ['HOME']) + paths.append('/etc') + + try: + paths.append(os.path.abspath(os.path.dirname(__file__))) + except: + pass + + if os.name == 'nt': + if 'USERPROFILE' in os.environ: + paths.append(os.environ['USERPROFILE']) + if 'WINDIR' in os.environ: + paths.append(os.environ['WINDIR']) + if 'SYSTEMDRIVE' in os.environ: + paths.append('%s\\' % (os.environ['SYSTEMDRIVE']),) + + try: + paths.append(os.path.abspath(os.path.split(os.path.dirname(__file__))[0])) + paths.append(os.path.abspath(os.path.dirname(sys.argv[0]))) + except: + pass + + if os.name == 'posix': + filenames.extend(['settings.conf', 'settings.ini', 'settings.cfg', 'settings.cnf']) + else: + filenames.extend(['settings.ini', 'settings.conf', 'settings.cfg', 'settings.cnf']) + + load_config() + +def locate_config(paths, filenames): + fd = None + for p in paths: + for f in filenames: + fp = os.path.join(p, f) + if os.path.exists(fp): + try: + fd = inifile.inifile(fp) + except None: + fd = None + return fd + +def load_config(fname=None): + global paths, filenames + if fname != None: + fp, fn = os.path.split(fname) + else: + fp, fn = None, None + + if fp: + pp = [fp] + else: + pp = paths[:] + + if fn: + ff = [fn] + else: + ff = filenames[:] + + fd = locate_config(pp, ff) + if fd != None: + config._setobj(fd) + +def save_config(original_name=True, make_path=True): + if original_name: + fp = config.fname + else: + saved = False + for f in filenames: + for p in paths: + if make_path: + try: + os.makedirs(p) + except OSError: + pass + if os.path.exists(p): + fp = os.path.join(p, f) + config.fname = fp + try: + config.write() + except OSError: + continue + saved = True + break + if not saved: + raise OSError, "No writable path to save configuration" + +if preconfig.load_config: + init(preconfig.appname) diff --git a/cecil/core/inifile.py b/cecil/core/inifile.py new file mode 100644 index 0000000..f435052 --- /dev/null +++ b/cecil/core/inifile.py @@ -0,0 +1,226 @@ +# ini reader/writer +__doc__ = """ +reads and writes config files using the microsoft windows 'ini' format. +(author's note: I don't think there IS any official 'ini' format. Some +artistic licence may have been used) + +Provides class 'inifile', which is a dict-like object that can be read +from a file or written out to a file (or both!) +""" + +__license__ = """ +Copyright (c) 2006-2007 Bradley Lawrence + +This is licensed under the Modified BSD License, as follows: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + + +def sanitize_for_subst(key): + """ + replaces open and close brackets with tokens, to ensure the brackets + do not interfere with the %(dict_key)s string formatting syntax. + + '=' is used as a token delimiter because we know an equals can never + appear in a keyname, since it is interpreted by this reader as the + end of the key and the start of the value. + """ + return key.replace('(', '=o=').replace(')', '=c=') + + + + +class inifilesec(dict): + def __init__(self, name, owner): + self.owner = owner + self.name = name + self.verbatim_hdr = "[%(sec_title)s]\n" + self.verbatim = ["%(=new_data=)s"] + self.verbatim_ftr = "\n" + self.verbatim_fields = [] + self.new_fields = [] + self.initializing = True + + def __getitem__(self, key): + lk = key.lower() + if lk in self: + return dict.__getitem__(self, key.lower()) + else: + raise KeyError, "Section '%s' has no key '%s'" % (self.name, key) + + def __setitem__(self, key, value): + if not self.has_key(key.lower()): + if self.initializing: + self.verbatim_fields += [key] + else: + self.new_fields += [key] + dict.__setitem__(self, key.lower(), value) + + def __delitem__(self, key): + dict.__delitem__(self, key.lower()) + + + +class inifile(dict): + def __init__(self, fname, path=None, autoread=True): + dict.__init__(self) + + self.fname = fname + self.cur_section = None + self.verbatim_hdr = [] + self.section_order = [] + if autoread: + self.read() + + def set_section(self, section): + self.cur_section = section.lower() + + def unset_section(self): + self.cur_section = None + + def get_current_file(self): + return + + def read(self): + fd = file(self.fname, 'r') + + act_section = 'default' + section_init = False + for line in fd: + verbatimline = line + keyvalue_line = False + line = line.lstrip() + if not line: + # blank line, ignore + pass + elif line[0] == ';': + # comment! ignore + pass + elif line[0] == '[': + # section header + line = line.rstrip() + if line[-1] == ']': + act_section = line[1:-1] + section_init = True + if not self.has_key(act_section.lower()): + "add specified section" + actsec = inifilesec(act_section, self) + dict.__setitem__(self, act_section.lower(), actsec) + #self[act_section.lower()] = actsec + actsec.verbatim_hdr = verbatimline + actsec.verbatim_ftr = "" + verbatimline = None + self.section_order += [act_section] + else: + # garbage line, don't understand it + pass + else: + peq = line.find('=') + if peq == -1: + # garbage line, don't understand it + pass + else: + k = line[:peq] + v = line[peq+1:].rstrip('\n\r') + if not self.has_key(act_section.lower()): + "add default section" + assert not section_init + + actsec = inifilesec('default', self) + self[act_section.lower()] = actsec + + + self[act_section.lower()][k] = v + verbatimline = "".join([k, '=%(', sanitize_for_subst(k.lower()), ')s\n']) + keyvalue_line = True + + if verbatimline: + if section_init or keyvalue_line: + "keyvalue lines always go under the default section, regardless of initialized state" + actsec = self[act_section.lower()] + actsec.verbatim += [verbatimline] + else: + self.verbatim_hdr += [verbatimline] + + + if section_init: + actsec = self[act_section.lower()] + actsec.verbatim += ["\n"] + else: + self.verbatim_hdr += ["\n"] + + if self.has_key('default'): + "has a default section. make sure it's added to the section_order" + try: + self.section_order.index('default') + except: + self.section_order += ['default'] + + for secdict in self.values(): + secdict.initializing = False + + def write(self): + fd = file(self.fname, 'w') + first = True + + fd.write("".join(self.verbatim_hdr)) + + for sec in self.section_order: + secdict = self[sec.lower()] + + new_lines = [] + for k in secdict.new_fields: + new_lines += ["%s=%s\n" % (k, secdict[k])] + new_lines = "".join(new_lines) + verbatim_dict = {'=new_data=': new_lines} + for k in secdict.verbatim_fields: + verbatim_dict[sanitize_for_subst(k.lower())] = secdict[k] + + fd.write(secdict.verbatim_hdr % {'sec_title': secdict.name}) + fd.write("".join(secdict.verbatim) % verbatim_dict) + fd.write(secdict.verbatim_ftr) + + def add_section(self, key): + if not self.has_key(key): + self[key.lower()] = inifilesec(key, self) + self.section_order += [key] + + + def __getitem__(self, key): + if type(key) != str: + raise TypeError, "Ini files can only contain string keys" + + if self.cur_section != None: + if dict.__getitem__(self, self.cur_section).has_key(key.lower()): + return dict.__getitem__(self, self.cur_section)[key.lower()] + else: + raise KeyError, "Section '%s' does not contain a key named '%s'" % (self.cur_section, key) + else: + #if not self.has_key(key.lower()): + # self[key.lower()] = inifilesec(key, self) + return dict.__getitem__(self, key.lower()) + def __setitem__(self, key, value): + if self.cur_section != None: + if not self.has_key(self.cur_section): + raise KeyError, "Section '%s' does not exist" % (self.cur_section) + self[self.cur_section][key.lower()] = value + else: + raise ValueError, "Cannot set the value of a section header" + diff --git a/cecil/core/objproxy.py b/cecil/core/objproxy.py new file mode 100644 index 0000000..9ce88c0 --- /dev/null +++ b/cecil/core/objproxy.py @@ -0,0 +1,99 @@ + +## {{{ http://code.activestate.com/recipes/496741/ (r1) +class Proxy(object): + __slots__ = ["_obj", "__weakref__"] + _local = { + '_setobj':1, + '_getobj':1 + } + def __init__(self, obj): + object.__setattr__(self, "_obj", obj) + + # + # proxying (special cases) + # + def __getattribute__(self, name): + if name in Proxy._local: + return object.__getattribute__(self, name) + return getattr(object.__getattribute__(self, "_obj"), name) + def __delattr__(self, name): + delattr(object.__getattribute__(self, "_obj"), name) + def __setattr__(self, name, value): + setattr(object.__getattribute__(self, "_obj"), name, value) + + def __nonzero__(self): + return bool(object.__getattribute__(self, "_obj")) + def __str__(self): + return str(object.__getattribute__(self, "_obj")) + def __repr__(self): + return repr(object.__getattribute__(self, "_obj")) + def __call__(self, *args, **kwargs): + return object.__getattribute__(self, "_obj")(*args, **kwargs) + + def _setobj(self, newobj): + newclass = Proxy._get_proxy(newobj) + object.__setattr__(self, "_obj", newobj) + object.__setattr__(self, "__class__", newclass) + + def _getobj(self): + return object.__getattribute__(self, "_obj") + + # + # factories + # + _special_names = [ + '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', + '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', + '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', + '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', + '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', + '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', + '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', + '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', + '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', + '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', + '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', + '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', + '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__', + '__truediv__', '__xor__', 'next' + ] + + @classmethod + def _get_proxy(cls, obj): + try: + cache = cls.__dict__["_class_proxy_cache"] + except KeyError: + cls._class_proxy_cache = cache = {} + try: + theclass = cache[obj.__class__] + except KeyError: + cache[obj.__class__] = theclass = cls._create_class_proxy(obj.__class__) + return theclass + + @classmethod + def _create_class_proxy(cls, theclass): + """creates a proxy for the given class""" + + def make_method(name): + def method(self, *args, **kw): + return getattr(object.__getattribute__(self, "_obj"), name)(*args, **kw) + return method + + namespace = {} + for name in cls._special_names: + if hasattr(theclass, name) and not hasattr(cls, name): + namespace[name] = make_method(name) + return type("%s(%s)" % (cls.__name__, theclass.__name__), (cls,), namespace) + + def __new__(cls, obj, *args, **kwargs): + """ + creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are + passed to this class' __init__, so deriving classes can define an + __init__ method of their own. + note: _class_proxy_cache is unique per deriving class (each deriving + class must hold its own cache) + """ + theclass = cls._get_proxy(obj) + ins = object.__new__(theclass) + #theclass.__init__(ins, obj, *args, **kwargs) + return ins diff --git a/cecil/preconfig.py b/cecil/preconfig.py new file mode 100644 index 0000000..5179443 --- /dev/null +++ b/cecil/preconfig.py @@ -0,0 +1,5 @@ + +load_config = True +appname = None +db_pool_class = None + diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..6bae21d --- /dev/null +++ b/setup.py @@ -0,0 +1,45 @@ +#!/usr/bin/python +from distutils.core import setup +import os +import sys +import shutil +import distutils.sysconfig +try: + from cPickle import load, dump +except ImportError: + from pickle import load, dump + +packages=['cecil', 'cecil.core'] + +if sys.argv[1] == 'clean': + for dir in packages: + dir_to_remove = os.path.join(distutils.sysconfig.get_python_lib(), dir) + assert len(dir_to_remove) > (len(distutils.sysconfig.get_python_lib()) + 1) + shutil.rmtree(dir_to_remove) +else: + majorversion = '1.0.0-build' + buildnumber = 0 + if os.path.exists('buildnum'): + fd = open('buildnum', 'rb') + try: + vinfo = load(fd) + mv, bn = vinfo + if mv == majorversion: + buildnumber = bn + except: + pass + fd.close() + buildnumber += 1 + fd = open('buildnum', 'wb') + dump((majorversion, buildnumber), fd) + fd.close() + + ver = '%s%d' % (majorversion, buildnumber) + print "Building version %s" % (ver,) + setup( + name='pycecil', + version=ver, + packages=packages + ) + if os.path.exists('setup.py'): + shutil.rmtree("build")