Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

updated sources from: http://trac.pylucid.net/browser/branches/0.9/py…

  • Loading branch information...
commit 236dad766628782ad6d3c09d15000701fe9db37d 0 parents
Jens Diemer authored
16 AUTHORS
@@ -0,0 +1,16 @@
+
+PRIMARY AUTHORS are and/or have been (alphabetic order):
+
+* Diemer, Jens
+ Main Developer since the first code line.
+ ohloh.net profile: <http://www.ohloh.net/accounts/4179/>
+ Homepage: <http://www.jensdiemer.de/>
+
+
+CONTRIBUTORS are and/or have been (alphabetic order):
+ -
+
+
+last SVN commit info:
+$LastChangedDate: 2008-11-14 12:13:00 +0100 (Fr, 14 Nov 2008) $
+$Rev: 1796 $
19 LICENSE
@@ -0,0 +1,19 @@
+All rights reserved.
+
+
+django-dbpreferences is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3 or later as published
+by the Free Software Foundation.
+
+complete GNU General Public License version 3:
+ http://www.gnu.org/licenses/gpl-3.0.txt
+
+German translation:
+ http://www.gnu.de/documents/gpl.de.html
+
+
+copyleft 2009 by the django-dbpreferences team, see AUTHORS for more details.
+
+SVN info:
+$LastChangedDate: 2008-06-05 16:33:09 +0200 (Do, 05 Jun 2008) $
+$Rev: 1635 $
24 README
@@ -0,0 +1,24 @@
+
+=============
+ description
+=============
+
+With django-dbpreferences you can store some app preferences into the database.
+
+A django app defines a form with initial values. The form cleaned data dict would
+be stored serialized into the database. The app can easy get the current preference
+dict and the user can easy edit the values in the django admin panel.
+
+
+=======
+ links
+=======
+
+:homepage:
+ http://code.google.com/p/django-dbpreferences/
+
+:SVN:
+ http://django-dbpreferences.googlecode.com/svn/trunk/
+
+
+*(Readme text $Rev$)*
9 dbpreferences/__init__.py
@@ -0,0 +1,9 @@
+
+
+__version__ = (0, 1, 0, 'beta')
+
+# for setuptools
+# - Only use . as a separator
+# - No spaces: (0, 1, 0, 'beta') -> "0.1.0beta"
+# http://peak.telecommunity.com/DevCenter/setuptools#specifying-your-project-s-version
+VERSION_STRING = "%s.%s.%s%s" % __version__
101 dbpreferences/admin.py
@@ -0,0 +1,101 @@
+# coding: utf-8
+
+"""
+ dbpreferences.admin
+ ~~~~~~~~~~~~~~~~~~~
+
+
+ Last commit info:
+ ~~~~~~~~~~~~~~~~~
+ $LastChangedDate$
+ $Rev$
+ $Author$
+
+ :copyleft: 2008 by the PyLucid team, see AUTHORS for more details.
+ :license: GNU GPL v3 or above, see LICENSE for more details.
+"""
+from django.contrib import admin
+from django.template import RequestContext
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext as _
+from django.conf.urls.defaults import patterns, url
+from django.template.loader import render_to_string
+from django.shortcuts import get_object_or_404, render_to_response
+
+from dbpreferences.models import Preference
+from dbpreferences.tools import forms_utils
+
+#------------------------------------------------------------------------------
+
+class PreferenceAdmin(admin.ModelAdmin):
+ actions = None # Disable actions
+ list_display = ("site", "app_label", "form_name", "lastupdatetime", "lastupdateby", "edit_link")
+ list_display_links = ("form_name",)
+ list_filter = ("site", "app_label",)
+ search_fields = ("site", "app_label", "form_name",)
+
+ def edit_link(self, instance):
+ """ For adding a edit link into django admin interface """
+ context = {
+ "instance": instance,
+ }
+ return render_to_string('dbpreferences/model_admin_edit_link.html', context)
+ edit_link.allow_tags = True
+
+ def edit_form(self, request, pk):
+ """ edit a preference entry using the associated form """
+
+ obj = get_object_or_404(Preference, pk=pk)
+ form_class = obj.get_form_class()
+
+ if request.method == 'POST':
+ form = form_class(request.POST)
+ if form.is_valid():
+ # save new preferences
+ obj.preferences = form.cleaned_data
+ obj.lastupdateby = request.user
+ obj.save()
+
+ msg = "Preferences %s updated." % obj
+ self.log_change(request, obj, msg)
+
+ if request.POST.has_key("_continue"):
+ msg += ' ' + _("You may edit it again below.")
+
+ if request.REQUEST.has_key('_popup'):
+ next_url = request.path + "?_popup=1"
+ else:
+ next_url = request.path
+ else:
+ next_url = reverse("admin_dbpreferences_preference_changelist")
+
+ self.message_user(request, msg)
+ return HttpResponseRedirect(next_url)
+ else:
+ form = form_class(obj.preferences)
+
+ # Append initial form values into all field help_text
+ forms_utils.setup_help_text(form)
+
+ context = {
+ "title": _('Change %s') % obj,
+ "obj": obj,
+ "form_url": reverse("admin_dbpref_edit_form", kwargs={"pk": pk}),
+ "form": form,
+ }
+ return render_to_response("dbpreferences/edit_form.html", context,
+ context_instance=RequestContext(request))
+
+ def get_urls(self):
+ """ add own edit view into urls """
+ urls = super(PreferenceAdmin, self).get_urls()
+ my_urls = patterns('',
+ url(r'^(?P<pk>\d+)/edit_form/$', self.admin_site.admin_view(self.edit_form),
+ name="admin_dbpref_edit_form")
+ )
+ return my_urls + urls
+
+
+
+admin.site.register(Preference, PreferenceAdmin)
28 dbpreferences/forms.py
@@ -0,0 +1,28 @@
+# coding: utf-8
+
+from django import forms
+from django.contrib.sites.models import Site
+
+from dbpreferences.models import Preference
+from dbpreferences.tools import forms_utils, easy_import
+
+class DBPreferencesBaseForm(forms.Form):
+ def __init__(self, *args, **kwargs):
+ assert(isinstance(self.Meta.app_label, basestring))
+ super(DBPreferencesBaseForm, self).__init__(*args, **kwargs)
+
+ def get_preferences(self):
+ current_site = Site.objects.get_current()
+ app_label = self.Meta.app_label
+ form_name = self.__class__.__name__
+
+ try:
+ db_entry = Preference.objects.get(site=current_site, app_label=app_label, form_name=form_name)
+ except Preference.DoesNotExist:
+ # Save initial form values into database
+ form_dict = Preference.objects.save_form_init(
+ form=self, site=current_site, app_label=app_label, form_name=form_name)
+ else:
+ form_dict = db_entry.preferences
+
+ return form_dict
183 dbpreferences/models.py
@@ -0,0 +1,183 @@
+# coding: utf-8
+
+"""
+ DBPreferences - models
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Last commit info:
+ ~~~~~~~~~~~~~~~~~
+ $LastChangedDate: $
+ $Rev: $
+ $Author: $
+
+ :copyleft: 2009 by the PyLucid team, see AUTHORS for more details.
+ :license: GNU GPL v3 or above, see LICENSE for more details.
+"""
+
+if __name__ == "__main__":
+ # For doctest only
+ import os
+ os.environ["DJANGO_SETTINGS_MODULE"] = "django.conf.global_settings"
+
+import pprint
+
+from django import forms
+from django.db import models
+from django.contrib.sites.models import Site
+from django.utils.translation import ugettext as _
+from django.contrib.auth.models import User, Group
+
+from dbpreferences.tools import forms_utils, easy_import, data_eval
+
+# The filename in witch the form should be stored:
+PREF_FORM_FILENAME = "preference_forms"
+
+
+def serialize(data):
+ return pprint.pformat(data)
+
+def deserialize(stream):
+ return data_eval.data_eval(stream)
+
+
+class PreferencesManager(models.Manager):
+ """ Manager class for Preference model """
+ def save_form_init(self, form, site, app_label, form_name):
+ """ save the initial form values as the preferences into the database """
+ form_dict = forms_utils.get_init_dict(form)
+ new_entry = Preference(
+ site = site,
+ app_label = app_label,
+ form_name = form_name,
+ preferences = form_dict,
+ )
+ new_entry.save()
+ return form_dict
+
+# def get_pref(self, form):
+# """
+# returns the preferences for the given form
+# stores the preferences into the database, if not exist.
+# """
+# assert isinstance(form, forms.Form), ("You must give a form instance and not only the class!")
+#
+# current_site = Site.objects.get_current()
+# app_label = form.Meta.app_label
+# form_name = form.__class__.__name__
+#
+# try:
+# db_entry = self.get(site=current_site, app_label=app_label, form_name=form_name)
+# except Preference.DoesNotExist:
+# # Save initial form values into database
+# form_dict = self.save_form_init(form, current_site, app_label, form_name)
+# else:
+# form_dict = db_entry.preferences
+#
+# return form_dict
+
+
+
+class DictFormWidget(forms.Textarea):
+ """ form widget for preferences dict """
+ def render(self, name, value, attrs=None):
+ """
+ FIXME: Can we get the original non-serialized db value here?
+ """
+ value = serialize(value)
+ return super(DictFormWidget, self).render(name, value, attrs)
+
+
+class DictFormField(forms.CharField):
+ """ form field for preferences dict """
+ widget = DictFormWidget
+
+ def clean(self, value):
+ """
+ validate the form data
+ FIXME: How can we get the pref form class for validating???
+ """
+ value = super(DictFormField, self).clean(value)
+ try:
+ return deserialize(value)
+ except Exception, err:
+ raise forms.ValidationError("Can't deserialize: %s" % err)
+
+
+class DictField(models.TextField):
+ """
+ A dict field.
+ Stores a python dict into a text field.
+ """
+ __metaclass__ = models.SubfieldBase
+
+ def to_python(self, value):
+ """ decode the data dict using simplejson.loads() """
+ if isinstance(value, dict):
+ return value
+ return deserialize(value)
+
+ def get_db_prep_save(self, value):
+ "Returns field's value prepared for saving into a database."
+ assert isinstance(value, dict)
+ return serialize(value)
+#
+ def formfield(self, **kwargs):
+ # Always use own form field and widget:
+ kwargs['form_class'] = DictFormField
+ kwargs['widget'] = DictFormWidget
+ return super(DictField, self).formfield(**kwargs)
+
+
+
+class Preference(models.Model):
+ """
+ Plugin preferences
+ """
+ objects = PreferencesManager()
+
+ id = models.AutoField(primary_key=True, help_text="The id of this preference entry, used for lucidTag")
+
+ site = models.ForeignKey(Site, editable=False, verbose_name=_('Site'))
+ app_label = models.CharField(max_length=128, editable=False,
+ help_text="app lable, must set via form.Meta.app_label")
+ form_name = models.CharField(max_length=128, editable=False,
+ help_text="preference form class name")
+
+ preferences = DictField(null=False, blank=False, #editable=False,
+ help_text="serialized preference form data dictionary")
+
+ createtime = models.DateTimeField(auto_now_add=True, help_text="Create time",)
+ lastupdatetime = models.DateTimeField(auto_now=True, help_text="Time of the last change.",)
+ lastupdateby = models.ForeignKey(User, editable=False, null=True, blank=True,
+ related_name="%(class)s_lastupdateby", help_text="User as last edit the current page.",)
+
+ #__________________________________________________________________________
+
+ def get_form_class(self):
+ """ returns the form class for this preferences item """
+ from_name = "%s.%s" % (self.app_label, PREF_FORM_FILENAME)
+ form = easy_import.import3(from_name, self.form_name)
+ return form
+
+ #__________________________________________________________________________
+
+ def __unicode__(self):
+ return u"Preferences for %s.%s.%s" % (self.site, self.app_label, self.form_name)
+
+ class Meta:
+ unique_together = ("site", "app_label", "form_name")
+ permissions = (("can_change_preferences", "Can change preferences"),)
+ ordering = ("site", "app_label", "form_name")
+ verbose_name = verbose_name_plural = "preferences"
+# db_table = 'PyLucid_preference'
+# app_label = 'PyLucid'
+
+
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod(
+# verbose=True
+ verbose=False
+ )
+ print "DocTest end."
46 dbpreferences/templates/dbpreferences/edit_form.html
@@ -0,0 +1,46 @@
+{% extends "admin/base_site.html" %}
+{% load i18n admin_modify adminmedia %}
+
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
+
+{% block bodyclass %}change-form{% endblock %}
+
+{% block breadcrumbs %}{% if not is_popup %}
+<div class="breadcrumbs">
+ <a href="../../../../">Home</a> ›
+ <a href="../../../">Dbpreferences</a> ›
+ <a href="../../">Preferences</a> ›
+ {{ obj }}
+</div>
+{% endif %}{% endblock %}
+
+{% block content %}
+<div id="content-main">
+<form action="{{ form_url }}" method="post">
+<fieldset class="module aligned">
+ {% for field in form %}
+ <div class="form-row{% if field.errors %} errors{% endif %} {% for field in form %}{{ field.field.name }} {% endfor %} ">
+ {{ field.errors }}
+ <div class="field-box">
+ {% if field.is_checkbox %}
+ {{ field }}{{ field.label_tag }}
+ {% else %}
+ {{ field.label_tag }}{{ field }}
+ {% endif %}
+ {% if field.help_text %}<p class="help">{{ field.help_text|safe }}</p>{% endif %}
+ </div>
+ </div>
+ {% endfor %}
+</fieldset>
+
+<div class="submit-row">
+ <input type="submit" name="_save" class="default" value="Save"/>
+ <!-- TODO:
+ <p class="deletelink-box"><a class="deletelink" href="reset/">Reset to default values</a></p>
+ -->
+ <input type="submit" name="_continue" value="Save and continue editing"/>
+</div>
+
+</form>
+</div>
+{% endblock %}
5 dbpreferences/templates/dbpreferences/model_admin_edit_link.html
@@ -0,0 +1,5 @@
+<strong>
+<a href="{% url admin_dbpref_edit_form instance.pk %}" title="edit form: {{ instance.site }} - {{ instance.app_label }} - {{ instance.form_name }}">
+ edit preferences
+</a>
+</strong>
77 dbpreferences/tests.py
@@ -0,0 +1,77 @@
+# coding: utf-8
+"""
+ Unittest for DBpreferences
+
+ INFO: dbpreferences should be exist in python path!
+"""
+
+if __name__ == "__main__":
+ # run unittest directly
+ import os
+ os.environ["DJANGO_SETTINGS_MODULE"] = "django.conf.global_settings"
+ from django.conf import global_settings
+ global_settings.INSTALLED_APPS += (
+ 'django.contrib.sites',
+ 'dbpreferences',
+ )
+ global_settings.DATABASE_ENGINE = "sqlite3"
+ global_settings.DATABASE_NAME = ":memory:"
+ global_settings.SITE_ID = 1
+
+from django import forms
+from django.test import TestCase
+
+from dbpreferences.models import Preference
+from dbpreferences.forms import DBPreferencesBaseForm
+
+
+class FormWithoutMeta(DBPreferencesBaseForm):
+ pass
+
+class FormMetaWithoutAppLabel(DBPreferencesBaseForm):
+ class Meta:
+ pass
+
+class UnittestForm(DBPreferencesBaseForm):
+ """ preferences test form for the unittest """
+ subject = forms.CharField(initial="foobar", help_text="Some foo text")
+ foo_bool = forms.BooleanField(initial=True, required=False, help_text="Yes or No?")
+ count = forms.IntegerField(initial=10, min_value=1, help_text="A max number")
+ font_size = forms.FloatField(initial=0.7, min_value=0.1, help_text="font size")
+
+ class Meta:
+ app_label = 'dbpreferences'
+
+
+
+class TestDBPref(TestCase):
+ def test_form_without_meta(self):
+ self.failUnlessRaises(AttributeError, FormWithoutMeta)
+
+ def test_form_meta_without_app_label(self):
+ self.failUnlessRaises(AttributeError, FormMetaWithoutAppLabel)
+
+ def test(self):
+ form = UnittestForm()
+ # Frist time, the data would be inserted into the database
+ self.failUnless(Preference.objects.count() == 0)
+ pref_data = form.get_preferences()
+ self.failUnless(Preference.objects.count() == 1)
+ self.failUnless(isinstance(pref_data, dict),
+ "It's not dict, it's: %s - %r" % (type(pref_data), pref_data))
+ self.failUnlessEqual(pref_data,
+ {'count': 10, 'foo_bool': True, 'font_size': 0.7, 'subject': 'foobar'})
+
+ form = UnittestForm()
+ self.failUnless(Preference.objects.count() == 1)
+ pref_data = form.get_preferences()
+ self.failUnless(Preference.objects.count() == 1)
+ self.failUnless(isinstance(pref_data, dict),
+ "It's not dict, it's: %s - %r" % (type(pref_data), pref_data))
+ self.failUnlessEqual(pref_data,
+ {'count': 10, 'foo_bool': True, 'font_size': 0.7, 'subject': 'foobar'})
+
+if __name__ == "__main__":
+ # Run this unittest directly
+ from django.core import management
+ management.call_command('test', 'dbpreferences')
0  dbpreferences/tools/__init__.py
No changes.
246 dbpreferences/tools/data_eval.py
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+
+"""
+ Data eval
+ ~~~~~~~~~
+
+ Evaluate a Python expression string, but only Python data type objects:
+ - Constants, Dicts, Lists, Tuples
+ - from datetime: datetime and timedelta
+
+ Error class hierarchy:
+
+ DataEvalError
+ +-- EvalSyntaxError (compiler SyntaxError)
+ +-- UnsafeSourceError (errors from the AST walker)
+
+ Note
+ ~~~~
+ Based on "Safe" Eval by Michael Spencer
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/364469
+
+ Last commit info:
+ ~~~~~~~~~~~~~~~~~
+ $LastChangedDate$
+ $Rev$
+ $Author: JensDiemer $
+
+ :copyleft: 2008 by the PyLucid team, see AUTHORS for more details.
+ :license: GNU GPL v3 or above, see LICENSE for more details.
+"""
+
+import compiler
+
+
+# For visitName()
+NAME_MAP = {"None": None, "True": True, "False": False}
+
+# For visitGetattr(): key is the callable name and value is the module name
+ALLOWED_CALLABLES = {
+ "datetime" : "datetime",
+ "timedelta": "datetime",
+}
+
+
+class SafeEval(object):
+ """
+ walk to compiler AST objects and evaluate only data type objects. If other
+ objects found, raised a UnsafeSourceError
+ """
+ def visit(self, node, **kw):
+ node_type = node.__class__.__name__
+ method_name = "visit" + node_type
+ method = getattr(self, method_name, self.unsupported)
+ result = method(node, **kw)
+ return result
+
+ def visitExpression(self, node, **kw):
+ for child in node.getChildNodes():
+ return self.visit(child, **kw)
+
+ #_________________________________________________________________________
+ # Errors
+
+ def unsupported(self, node, **kw):
+ raise UnsafeSourceError(
+ "Unsupported source construct", node.__class__, node
+ )
+
+ def visitName(self, node, **kw):
+ if node.name in NAME_MAP:
+ return NAME_MAP[node.name]
+
+ raise UnsafeSourceError(
+ "Strings must be quoted", node.name, node
+ )
+
+ #_________________________________________________________________________
+ # supported nodes
+
+ def visitConst(self, node, **kw):
+ return node.value
+
+ def visitUnarySub(self, node, **kw):
+ """ Algebraic negative number """
+ node = node.asList()[0]
+ number = self.visitConst(node)
+ return - number # return the negative number
+
+ def visitDict(self, node, **kw):
+ return dict([(self.visit(k),self.visit(v)) for k,v in node.items])
+
+ def visitTuple(self, node, **kw):
+ return tuple(self.visit(i) for i in node.nodes)
+
+ def visitList(self, node, **kw):
+ return [self.visit(i) for i in node.nodes]
+
+ #_________________________________________________________________________
+ # ALLOWED_CALLABLES nodes
+
+ def visitGetattr(self, node, **kw):
+ """
+ returns the callable object, if its in ALLOWED_CALLABLES.
+ """
+ attrname = node.attrname
+ try:
+ callable_name = ALLOWED_CALLABLES[attrname]
+ except KeyError:
+ raise UnsafeSourceError("Callable not allowed.", attrname, node)
+
+ module = __import__(callable_name, fromlist=[attrname])
+ callable = getattr(module, attrname)
+
+ return callable
+
+ def visitCallFunc(self, node, **kw):
+ """
+ For e.g. datetime and timedelta
+ """
+ child_node = node.asList()[0]
+ callable = self.visit(child_node)
+ args = [self.visit(i) for i in node.args]
+ return callable(*args)
+
+
+def data_eval(source):
+ """
+ Compile the given source string to AST objects and evaluate only data
+ type objects.
+ """
+ if not isinstance(source, basestring):
+ raise DataEvalError("source must be string/unicode!")
+ source = source.replace("\r\n", "\n").replace("\r", "\n")
+
+ try:
+ ast = compiler.parse(source, "eval")
+ except SyntaxError, e:
+ raise EvalSyntaxError(e)
+
+ return SafeEval().visit(ast)
+
+
+#_____________________________________________________________________________
+# ERROR CLASS
+
+class DataEvalError(Exception):
+ """ main error class for all data eval errors """
+ pass
+
+class EvalSyntaxError(DataEvalError):
+ """ compile raised a SyntaxError"""
+ pass
+
+class UnsafeSourceError(DataEvalError):
+ """ Error class for the SafeEval AST walker """
+ def __init__(self, error, descr = None, node = None):
+ self.error = error
+ self.descr = descr
+ self.node = node
+ self.lineno = getattr(node, "lineno", None)
+
+ def __repr__(self):
+ return "%s in line %d: '%s'" % (self.error, self.lineno, self.descr)
+
+ __str__ = __repr__
+
+
+
+#_____________________________________________________________________________
+# UNITTEST
+
+
+
+import unittest
+
+class TestDataEval(unittest.TestCase):
+ def assert_eval(self, data):
+ data_string = repr(data)
+ result = data_eval(data_string)
+ #print data, type(data), result, type(result)
+ self.assertEqual(result, data)
+
+ def testNone(self):
+ self.assert_eval(None)
+
+ def testBool(self):
+ self.assert_eval(True)
+ self.assert_eval(False)
+ self.assert_eval([True, False])
+
+ def testConst(self):
+ self.assert_eval(1)
+ self.assert_eval(1.01)
+ self.assert_eval("FooBar")
+ self.assert_eval(u"FooBar")
+
+ def testNegativeValues(self):
+ self.assert_eval(-1)
+ self.assert_eval(-2.02)
+
+ def testTuple(self):
+ self.assert_eval(())
+ self.assert_eval((1,2))
+ self.assert_eval(("1", u"2", None, True, False))
+
+ def testList(self):
+ self.assert_eval([])
+ self.assert_eval([1,2,-3,-4.41])
+ self.assert_eval(["foo", u"bar", None, True, False])
+
+ def testDict(self):
+ self.assert_eval({})
+ self.assert_eval({1:2, "a":"b", u"c":"c", "d":-1, "e":-2.02})
+ self.assert_eval({"foo":"bar", u"1": None, 1:True, 0:False})
+
+ def testDatetime(self):
+ from datetime import datetime, timedelta
+ self.assert_eval(datetime.now())
+ self.assert_eval({"dt": datetime.now()})
+ self.assert_eval(timedelta(seconds=2))
+
+ def testLineendings(self):
+ data_eval("\r\n{\r\n'foo'\r\n:\r\n1\r\n}\r\n")
+ data_eval("\r{\r'foo'\r:\r1\r}\r")
+
+ def testNoString(self):
+ self.assertRaises(DataEvalError, data_eval, 1)
+
+ def testQuoteErr(self):
+ self.assertRaises(UnsafeSourceError, data_eval, "a")
+ self.assertRaises(DataEvalError, data_eval, "a")
+
+ def testUnsupportedErr(self):
+ self.assertRaises(UnsafeSourceError, data_eval, "a+2")
+ self.assertRaises(UnsafeSourceError, data_eval, "eval()")
+ self.assertRaises(DataEvalError, data_eval, "eval()")
+
+ def testSyntaxError(self):
+ self.assertRaises(EvalSyntaxError, data_eval, ":")
+ self.assertRaises(EvalSyntaxError, data_eval, "import os")
+ self.assertRaises(DataEvalError, data_eval, "import os")
+
+
+
+if __name__ == '__main__':
+ unittest.main()
123 dbpreferences/tools/easy_import.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+
+"""
+ Import2
+ ~~~~~~~
+
+ a easy to use __import__
+
+ Last commit info:
+ ~~~~~~~~~~~~~~~~~
+ $LastChangedDate: 2009-04-18 10:58:43 +0100 (Sa, 18 Apr 2009) $
+ $Rev: 1903 $
+ $Author: JensDiemer $
+
+ :copyleft: 2008 by the PyLucid team, see AUTHORS for more details.
+ :license: GNU GPL v3 or above, see LICENSE for more details.
+"""
+
+#______________________________________________________________________________
+# internal import functions
+
+def import2(from_name, fromlist=None, globals={}, locals={}):
+ """
+ A easy __import__ function.
+ TODO: Python 2.5 level argument not supported, yet.
+ Link: http://www.python-forum.de/topic-14591.html (de)
+
+ >>> import sys
+ >>> sys2 = import2("sys")
+ >>> sys is sys2
+ True
+
+ >>> from time import time
+ >>> time2 = import2("time", "time")
+ >>> time is time2
+ True
+
+ >>> from os.path import sep
+ >>> sep2 = import2("os.path", "sep")
+ >>> sep is sep2
+ True
+
+ >>> from os import sep, pathsep
+ >>> sep2, pathsep2 = import2("os", ["sep", "pathsep"])
+ >>> sep is sep2 ; pathsep is pathsep2
+ True
+ True
+
+ >>> import2("existiertnicht")
+ Traceback (most recent call last):
+ ...
+ ImportError: No module named existiertnicht
+
+ >>> import2("os", "gibtsnicht")
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'os' object has no attribute 'gibtsnicht
+ """
+
+ if isinstance(fromlist, basestring):
+ # Only one from objects name
+ fromlist = [fromlist]
+
+ obj = __import__(from_name, globals, locals, fromlist)
+ if fromlist==None:
+ # Without a fromlist
+ return obj
+
+ # get all 'fromlist' objects
+ result = []
+ for object_name in fromlist:
+ try:
+ result.append(getattr(obj, object_name))
+ except AttributeError, e:
+ msg = "'%s' object has no attribute '%s" % (
+ obj.__name__, object_name
+ )
+ raise AttributeError(msg)
+
+ if len(result) == 1:
+ return result[0]
+ else:
+ return result
+
+
+def import3(from_name, object_name):
+ """
+ Thin layer aroung import2():
+ - other error handling: raise original error or always a ImportError
+ - catch SyntaxError, too (e.g. developing a PyLucid plugin)
+ - convert unicode to string (Names from database are always unicode and
+ the __import__ function must get strings)
+
+ >>> from time import time
+ >>> time2 = import3(u"time", u"time")
+ >>> time is time2
+ True
+
+ >>> import3(u"A", u"B")
+ Traceback (most recent call last):
+ ...
+ ImportError: Can't import 'B' from 'A': No module named A
+ """
+ try:
+ # unicode -> string
+ from_name = str(from_name)
+ object_name = str(object_name)
+
+ return import2(from_name, object_name)
+ except (ImportError, SyntaxError), err:
+ raise ImportError, "Can't import '%s' from '%s': %s" % (
+ object_name, from_name, err
+ )
+
+
+
+if __name__ == "__main__":
+ print "runnint doctest for %s" % __file__
+ verbose = False
+# verbose = True
+ import doctest
+ doctest.testmod(verbose=verbose)
+ print "--- END ---"
211 dbpreferences/tools/forms_utils.py
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+"""
+ some utils around newforms
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Last commit info:
+ ~~~~~~~~~~~~~~~~~
+ $LastChangedDate: 2009-04-17 10:07:35 +0100 (Fr, 17 Apr 2009) $
+ $Rev: 1900 $
+ $Author: JensDiemer $
+
+ :copyleft: 2008 by the PyLucid team, see AUTHORS for more details.
+ :license: GNU GPL v3 or above, see LICENSE for more details.
+"""
+
+import unittest
+
+from django import forms
+from django.forms import ValidationError
+from django.utils.encoding import smart_unicode
+
+
+def setup_help_text(form):
+ """
+ Append on every help_text the default information (The initial value)
+ """
+ for field_name, field in form.base_fields.iteritems():
+ help_text = unicode(field.help_text) # translate gettext_lazy
+ if u"(default: '" in help_text:
+ # The default information was inserted in the past
+ return
+ field.help_text = "%s (default: '%s')" % (
+ field.help_text, field.initial
+ )
+
+def get_init_dict(form):
+ """
+ Returns a dict with all initial values from a newforms class.
+ """
+ init_dict = {}
+ for field_name, field in form.base_fields.iteritems():
+ initial = field.initial
+# if initial == None:
+# msg = (
+# "The preferences model attribute '%s' has no initial value!"
+# ) % field_name
+# raise NoInitialError(msg)
+
+ init_dict[field_name] = initial
+ return init_dict
+
+
+class NoInitialError(Exception):
+ """
+ All preferences newform attributes must habe a initial value.
+ """
+ pass
+
+
+class ChoiceField2(forms.ChoiceField):
+ """
+ Works like a ChoiceField, but accepts a list of items. The list are
+ converted to a tuple fpr rendering.
+ Returns the value and not the key in clean().
+
+ >>> f = ChoiceField2(choices=["A","B","C"])
+ >>> f.choices
+ [('0', u'A'), ('1', u'B'), ('2', u'C')]
+ >>> f.clean('1')
+ u'B'
+ """
+ def __init__(self, *args, **kwargs):
+ choices = kwargs.pop("choices")
+ kwargs["choices"] = self.choices = [
+ (str(no), smart_unicode(value)) for no, value in enumerate(choices)
+ ]
+
+ super(ChoiceField2, self).__init__(*args, **kwargs)
+
+ def clean(self, value):
+ """
+ Validates that the input and returns the choiced value.
+ """
+ key = super(ChoiceField2, self).clean(value)
+ choices_dict = dict(self.choices)
+ return choices_dict[key]
+
+
+class StripedCharField(forms.CharField):
+ """
+ Same as forms.CharField but stripes the output.
+
+ >>> f = StripedCharField()
+ >>> f.clean('\\n\\n[\\nTEST\\n]\\n\\n')
+ u'[\\nTEST\\n]'
+ """
+ def clean(self, value):
+ value = super(StripedCharField, self).clean(value)
+ return value.strip()
+
+
+class ListCharField(forms.CharField):
+ """
+ Items seperated by spaces or other characters.
+ If the initial is a list/tuple, it would be joined with the seperator.
+
+ >>> f = ListCharField()
+ >>> f.clean(' one two tree')
+ [u'one', u'two', u'tree']
+
+ >>> f = ListCharField(seperator="\\n")
+ >>> f.clean('one\\ntwo\\n\\ntree\\n\\n')
+ [u'one', u'two', u'tree']
+ """
+ def __init__(self, seperator=" ", *args, **kwargs):
+ self.seperator = seperator
+ if "initial" in kwargs:
+ initial = kwargs["initial"]
+ if isinstance(initial, (list, tuple)):
+ kwargs["initial"] = self.seperator.join(initial)
+ super(ListCharField, self).__init__(*args, **kwargs)
+
+ def clean(self, value):
+ raw_value = super(ListCharField, self).clean(value)
+ value = raw_value.strip()
+ items = [i.strip() for i in value.split(self.seperator)]
+ items = [i for i in items if i] # eliminate empty items
+ return items
+
+class ListCharFieldTest(unittest.TestCase):
+ def test(self):
+ """ Test if a give list would be joined with the seperator"""
+ class TestForm(forms.Form):
+ foo = ListCharField(
+ seperator="\n", initial=[u'one', u'two', u'tree']
+ )
+
+ f = TestForm()
+ self.failUnlessEqual(
+ f.as_p(),
+ '<p><label for="id_foo">Foo:</label> '
+ '<input type="text" name="foo" value="one\ntwo\ntree" id="id_foo" />'
+ '</p>'
+ )
+
+class InternalURLField(forms.CharField):
+ """
+ Uses e.g. for back urls via a http GET parameter
+ validates the URL and check if is't a internal url and not
+ a external.
+
+ >>> f = InternalURLField()
+ >>> f.clean('/a/foobar/url/')
+ u'/a/foobar/url/'
+
+ >>> f.clean('http://eval.domain.tld')
+ Traceback (most recent call last):
+ ...
+ ValidationError: [u'Open redirect found.']
+
+ >>> f = InternalURLField(must_start_with="/_command/")
+ >>> f.clean('/_command/a/foobar/url/')
+ u'/_command/a/foobar/url/'
+
+ >>> f.clean('/a/wrong/url/')
+ Traceback (most recent call last):
+ ...
+ ValidationError: [u'Open redirect found.']
+ """
+ default_error_message = "Open redirect found."
+
+ def __init__(self, must_start_with=None, *args, **kwargs):
+ self.must_start_with = must_start_with
+ super(InternalURLField, self).__init__(*args, **kwargs)
+
+ def clean(self, value):
+ value = super(InternalURLField, self).clean(value)
+ if "://" in value:
+ raise ValidationError(self.default_error_message)
+ if self.must_start_with and not value.startswith(self.must_start_with):
+ raise ValidationError(self.default_error_message)
+ return value
+
+
+class ModelForm2(forms.ModelForm):
+ """
+ A model form witch don't validate unique fields.
+
+ This ModelForm is only for generating the forms and not for create/update
+ any database data. So a field unique Test would like generate Errors like:
+ User with this Username already exists.
+
+ see also:
+ http://www.jensdiemer.de/_command/118/blog/detail/30/ (de)
+ http://www.python-forum.de/topic-16000.html (de)
+ """
+ def validate_unique(self):
+ pass
+
+
+
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod(
+# verbose=True
+ verbose=False
+ )
+ print "DocTest end."
+
+ unittest.main()
71 setup.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ distutils setup
+ ~~~~~~~~~~~~~~~
+
+ Last commit info:
+ ~~~~~~~~~~~~~~~~~
+ $LastChangedDate$
+ $Rev$
+ $Author$
+
+ :copyleft: 2009 by the django-dbpreferences team, see AUTHORS for more details.
+ :license: GNU GPL v3 or above, see LICENSE for more details.
+"""
+
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+from dbpreferences import VERSION_STRING
+
+
+def get_authors():
+ authors = []
+ f = file("AUTHORS", "r")
+ for line in f:
+ if line.startswith('*'):
+ authors.append(line[1:].strip())
+ f.close()
+ return authors
+
+def get_long_description():
+ f = file("README", "r")
+ long_description = f.read()
+ f.close()
+ long_description.strip()
+ return long_description
+
+
+setup(
+ name='django-dbpreferences',
+ version=PYLUCID_VERSION_STRING,
+ description='With django-dbpreferences you can store some app preferences into the database.',
+ long_description = get_long_description(),
+ author = get_authors(),
+ maintainer = "Jens Diemer",
+ url='http://code.google.com/p/django-dbpreferences/',
+ packages=find_packages(),
+ include_package_data=True, # include package data under svn source control
+ zip_safe=False,
+ classifiers = [
+ "Development Status :: 4 - Beta",
+# "Development Status :: 5 - Production/Stable",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+# "Intended Audience :: Education",
+# "Intended Audience :: End Users/Desktop",
+ "License :: OSI Approved :: GNU General Public License (GPL)",
+ "Programming Language :: Python",
+ 'Framework :: Django',
+ "Topic :: Database :: Front-Ends",
+ "Topic :: Documentation",
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+ "Topic :: Internet :: WWW/HTTP :: Site Management",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ "Operating System :: OS Independent",
+ ]
+)
Please sign in to comment.
Something went wrong with that request. Please try again.