diff --git a/CHANGES.txt b/CHANGES.txt index 824f70af..9901eece 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,11 @@ 2.0.15 (unreleased) ------------------- +- Added unofficial deformdemo. This provides a space for contributors to add + their widgets without requiring tests, especially when the functional tests + cannot pass using Selenium. [sydoluciani] + https://github.com/Pylons/deformdemo/pull/92 + - Changed dateparts widget to use ``type="number"`` instead of default `text`. https://github.com/Pylons/deform/issues/442 diff --git a/MANIFEST.in b/MANIFEST.in index f5703e17..5aff4f9b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ graft deformdemo +graft unofficial-deformdemo include *.py include *.sh diff --git a/deformdemo/__init__.py b/deformdemo/__init__.py index b24a71f3..d11b83f3 100644 --- a/deformdemo/__init__.py +++ b/deformdemo/__init__.py @@ -2892,13 +2892,19 @@ def translator(term): return get_localizer(get_current_request()).translate(term) # Configure renderer - configure_zpt_renderer(("deformdemo:custom_widgets",), translator) - + configure_zpt_renderer( + ("deformdemo:custom_widgets", "unofficial-deformdemo:custom_widgets"), + translator, + ) config.add_static_view("static_deform", "deform:static") + config.add_route( + "unofficial-deformdemo", "/unofficial-deformdemo*traverse" + ) config.add_route("deformdemo", "*traverse") def onerror(*arg): pass config.scan("deformdemo", onerror=onerror) + config.include("..unofficial-deformdemo") return config.make_wsgi_app() diff --git a/deformdemo/templates/index.pt b/deformdemo/templates/index.pt index 5e5069b9..745a7739 100644 --- a/deformdemo/templates/index.pt +++ b/deformdemo/templates/index.pt @@ -3,17 +3,20 @@
The official Deform Demo supports tested and documented widgets. + The unofficial Deform Demo provides a space for contributors to add custom widgets that are not supported with the usual rigorous tests and documentation that is required by the Pylons Project.
This demo is written using Pyramid. Note that Deform does - not depend on any particular web framework, - we just had to write a demo application in something. + not depend on any particular web framework. + We had to write a demo application in something. If you would like to run this demo application locally, please read the README file for installation of this application. diff --git a/deformdemo/templates/main.pt b/deformdemo/templates/main.pt index a7ace947..a7587341 100644 --- a/deformdemo/templates/main.pt +++ b/deformdemo/templates/main.pt @@ -60,9 +60,14 @@
diff --git a/unofficial-deformdemo/__init__.py b/unofficial-deformdemo/__init__.py new file mode 100644 index 00000000..0fa929d2 --- /dev/null +++ b/unofficial-deformdemo/__init__.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- + +""" A Pyramid app that demonstrates various Deform widgets and +capabilities and which provides a functional test suite """ + +import inspect +import logging +import pprint +import sys + +import colander +from pyramid.i18n import TranslationStringFactory +from pyramid.i18n import get_locale_name +from pyramid.renderers import get_renderer +from pyramid.response import Response +from pyramid.view import view_config +from pyramid.view import view_defaults + +import deform +from pygments import highlight +from pygments.formatters import HtmlFormatter +from pygments.lexers import PythonLexer + + +log = logging.getLogger(__name__) + + +PY3 = sys.version_info[0] == 3 +PY38MIN = sys.version_info[0] == 3 and sys.version_info[1] >= 8 + +if PY3: + + def unicode(val, encoding="utf-8"): + return val + + +_ = TranslationStringFactory("unofficial-deformdemo") + +formatter = HtmlFormatter(nowrap=True) +css = formatter.get_style_defs() + +# the zpt_renderer above is referred to within the demo.ini file by dotted name + + +class demonstrate(object): + def __init__(self, title): + self.title = title + + def __call__(self, method): + method.demo = self.title + return method + + +# Py2/Py3 compat +# http://stackoverflow.com/a/16888673/315168 +# eliminate u'' +def my_safe_repr(obj, context, maxlevels, level, sort_dicts=True): + + if type(obj) == unicode: + obj = obj.encode("utf-8") + + # Python 3.8 changed the call signature of pprint._safe_repr. + # by adding sort_dicts. + if PY38MIN: + return pprint._safe_repr(obj, context, maxlevels, level, sort_dicts) + else: + return pprint._safe_repr(obj, context, maxlevels, level) + + +@view_defaults(route_name="unofficial-deformdemo") +class UnofficialDeformDemo(object): + def __init__(self, request): + self.request = request + self.macros = get_renderer("templates/main.pt").implementation().macros + + def render_form( + self, + form, + appstruct=colander.null, + submitted="submit", + success=None, + readonly=False, + is_i18n=False, + ): + + captured = None + + if submitted in self.request.POST: + # the request represents a form submission + try: + # try to validate the submitted values + controls = self.request.POST.items() + captured = form.validate(controls) + if success: + response = success() + if response is not None: + return response + html = form.render(captured) + except deform.ValidationFailure as e: + # the submitted values could not be validated + html = e.render() + + else: + # the request requires a simple form rendering + html = form.render(appstruct, readonly=readonly) + + if self.request.is_xhr: + return Response(html) + + code, start, end = self.get_code(2) + locale_name = get_locale_name(self.request) + + reqts = form.get_widget_resources() + + printer = pprint.PrettyPrinter() + printer.format = my_safe_repr + output = printer.pformat(captured) + captured = highlight(output, PythonLexer(), formatter) + + # values passed to template for rendering + return { + "form": html, + "captured": captured, + "code": code, + "start": start, + "end": end, + "is_i18n": is_i18n, + "locale": locale_name, + "demos": self.get_demos(), + "title": self.get_title(), + "css_links": reqts["css"], + "js_links": reqts["js"], + } + + def get_code(self, level): + frame = sys._getframe(level) + lines, start = inspect.getsourcelines(frame.f_code) + end = start + len(lines) + code = "".join(lines) + if not PY3: + code = unicode(code, "utf-8") + return highlight(code, PythonLexer(), formatter), start, end + + @view_config(name="thanks.html", route_name="unofficial-deformdemo") + def thanks(self): + return Response( + "Thanks!
" + 'Up' + ) + + @view_config(name="allcode", + renderer="templates/code.pt", + route_name="unofficial-deformdemo") + def allcode(self): + params = self.request.params + start = params.get("start") + end = params.get("end") + hl_lines = None + if start and end: + start = int(start) + end = int(end) + hl_lines = list(range(start, end)) + code = open(inspect.getsourcefile(self.__class__), "r").read() + code = code.encode("utf-8") + formatter = HtmlFormatter( + linenos="table", + lineanchors="line", + cssclass="hightlight ", + hl_lines=hl_lines, + ) + html = highlight(code, PythonLexer(), formatter) + return {"code": html, "demos": self.get_demos()} + + def get_title(self): + # gross hack; avert your eyes + frame = sys._getframe(3) + attr = frame.f_locals["attr"] + inst = frame.f_locals["inst"] + method = getattr(inst, attr) + return method.demo + + @view_config(name="pygments.css", route_name="unofficial-deformdemo") + def cssview(self): + response = Response(body=css, content_type="text/css") + response.cache_expires = 360 + return response + + @view_config(renderer="templates/index.pt", + route_name="unofficial-deformdemo") + def index(self): + return {"demos": self.get_demos()} + + def get_demos(self): + def predicate(value): + if getattr(value, "demo", None) is not None: + return True + + demos = inspect.getmembers(self, predicate) + L = [] + for name, method in demos: + url = self.request.resource_url( + self.request.root, name, route_name="unofficial-deformdemo" + ) + L.append((method.demo, url)) + L.sort() + return L + + # Unofficial Deform Demo Forms Start Here. + + @view_config(name="textinput", + renderer="templates/form.pt", + route_name="unofficial-deformdemo") + @demonstrate("Text Input Widget") + def textinput(self): + class Schema(colander.Schema): + text = colander.SchemaNode( + colander.String(), + validator=colander.Length(max=100), + widget=deform.widget.TextInputWidget(), + description="Enter some text", + ) + + schema = Schema() + form = deform.Form(schema, buttons=("submit",)) + + return self.render_form(form) + + +def includeme(config): + + config.scan("unofficial-deformdemo") diff --git a/unofficial-deformdemo/templates/code.pt b/unofficial-deformdemo/templates/code.pt new file mode 100644 index 00000000..438404e4 --- /dev/null +++ b/unofficial-deformdemo/templates/code.pt @@ -0,0 +1,6 @@ +The official Deform Demo supports tested and documented widgets. + The unofficial Deform Demo provides a space for contributors to add custom widgets that are not supported with the usual rigorous tests and documentation that is required by the Pylons Project.
++ This demo is written using Pyramid. Note that Deform does + not depend on any particular web framework. + We had to write a demo application in something. + If you would like to run this demo application locally, + please read the README + file for installation of this application. +
+ ++ For further information, see + the Deform documentation. +
+ +