# HG changeset patch # User Thierry Florac # Date 1574866641 -3600 # Node ID df022d00a9c4ea6c89f45417ae5d90c2661e6608 # Parent ed2dc23f7f99dd2016ab71937ba4868e36888829 Version 0.1.4.1 diff -r ed2dc23f7f99 -r df022d00a9c4 .installed.cfg --- a/.installed.cfg Fri Jan 18 15:32:56 2019 +0100 +++ b/.installed.cfg Wed Nov 27 15:57:21 2019 +0100 @@ -1,91 +1,88 @@ [buildout] installed_develop_eggs = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-template.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-form.egg-link /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/lingua.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-pagelet.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-catalog.egg-link /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-viewlet.egg-link /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-utils.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-file.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-zmi.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-skin.egg-link - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs/pyams-i18n.egg-link -parts = package i18n pyflakes test +parts = package i18n pyflakes pylint test [package] -__buildout_installed__ = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pyams_upgrade - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pcreate - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pserve - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pdistreport - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pviews - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/ptweens - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pshell - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/proutes - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/prequest -__buildout_signature__ = zc.recipe.egg-25289128786a29bd5395ec7b9e3ceb3a zc.buildout-25289128786a29bd5395ec7b9e3ceb3a setuptools-25289128786a29bd5395ec7b9e3ceb3a +__buildout_installed__ = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/python +__buildout_signature__ = zc.recipe.egg-fc68d03e7c9074dcb88f8edb384c0f13 zc.buildout-fc68d03e7c9074dcb88f8edb384c0f13 setuptools-fc68d03e7c9074dcb88f8edb384c0f13 _b = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin _d = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs -_e = /var/local/env/pyams/eggs +_e = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs bin-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin develop-eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs -eggs = chameleon - pyams_utils - pyams_viewlet - pyramid - zope.component - zope.configuration - zope.contentprovider - zope.interface - zope.location - zope.schema -eggs-directory = /var/local/env/pyams/eggs +eggs = pyams_viewlet +eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs find-links = http://download.ztfy.org/eggs +interpreter = python recipe = zc.recipe.egg [i18n] __buildout_installed__ = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pybabel + /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pot-create /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/polint - /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pot-create -__buildout_signature__ = zc.recipe.egg-25289128786a29bd5395ec7b9e3ceb3a zc.buildout-25289128786a29bd5395ec7b9e3ceb3a setuptools-25289128786a29bd5395ec7b9e3ceb3a +__buildout_signature__ = zc.recipe.egg-fc68d03e7c9074dcb88f8edb384c0f13 zc.buildout-fc68d03e7c9074dcb88f8edb384c0f13 setuptools-fc68d03e7c9074dcb88f8edb384c0f13 _b = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin _d = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs -_e = /var/local/env/pyams/eggs +_e = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs bin-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin develop-eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs eggs = babel lingua -eggs-directory = /var/local/env/pyams/eggs +eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs find-links = http://download.ztfy.org/eggs recipe = zc.recipe.egg [pyflakes] __buildout_installed__ = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pyflakes /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pyflakes -__buildout_signature__ = zc.recipe.egg-25289128786a29bd5395ec7b9e3ceb3a zc.buildout-25289128786a29bd5395ec7b9e3ceb3a setuptools-25289128786a29bd5395ec7b9e3ceb3a +__buildout_signature__ = zc.recipe.egg-fc68d03e7c9074dcb88f8edb384c0f13 zc.buildout-fc68d03e7c9074dcb88f8edb384c0f13 setuptools-fc68d03e7c9074dcb88f8edb384c0f13 _b = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin _d = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs -_e = /var/local/env/pyams/eggs +_e = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs bin-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin develop-eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs eggs = pyflakes -eggs-directory = /var/local/env/pyams/eggs +eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs entry-points = pyflakes=pyflakes.scripts.pyflakes:main find-links = http://download.ztfy.org/eggs initialization = if not sys.argv[1:]: sys.argv[1:] = ["src"] recipe = zc.recipe.egg scripts = pyflakes +[pylint] +__buildout_installed__ = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/epylint + /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pylint + /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/symilar + /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pyreverse + /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/pylint +__buildout_signature__ = zc.recipe.egg-fc68d03e7c9074dcb88f8edb384c0f13 zc.buildout-fc68d03e7c9074dcb88f8edb384c0f13 setuptools-fc68d03e7c9074dcb88f8edb384c0f13 +_b = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin +_d = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs +_e = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs +arguments = sys.argv[1:] +bin-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin +develop-eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs +eggs = pyams_viewlet + pylint +eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs +entry-points = pylint=pylint.lint:Run +find-links = http://download.ztfy.org/eggs +recipe = zc.recipe.egg + [test] __buildout_installed__ = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/parts/test /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin/test -__buildout_signature__ = six-25289128786a29bd5395ec7b9e3ceb3a zc.recipe.testrunner-25289128786a29bd5395ec7b9e3ceb3a zc.recipe.egg-25289128786a29bd5395ec7b9e3ceb3a zc.buildout-25289128786a29bd5395ec7b9e3ceb3a zope.exceptions-25289128786a29bd5395ec7b9e3ceb3a zope.interface-25289128786a29bd5395ec7b9e3ceb3a zope.testrunner-25289128786a29bd5395ec7b9e3ceb3a setuptools-25289128786a29bd5395ec7b9e3ceb3a +__buildout_signature__ = six-fc68d03e7c9074dcb88f8edb384c0f13 zc.recipe.egg-fc68d03e7c9074dcb88f8edb384c0f13 zc.recipe.testrunner-fc68d03e7c9074dcb88f8edb384c0f13 zc.buildout-fc68d03e7c9074dcb88f8edb384c0f13 zope.exceptions-fc68d03e7c9074dcb88f8edb384c0f13 zope.interface-fc68d03e7c9074dcb88f8edb384c0f13 zope.testrunner-fc68d03e7c9074dcb88f8edb384c0f13 setuptools-fc68d03e7c9074dcb88f8edb384c0f13 _b = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin _d = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs -_e = /var/local/env/pyams/eggs +_e = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs bin-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/bin develop-eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/develop-eggs eggs = pyams_viewlet [test] -eggs-directory = /var/local/env/pyams/eggs +eggs-directory = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/eggs find-links = http://download.ztfy.org/eggs location = /home/tflorac/Dropbox/src/PyAMS/pyams_viewlet/parts/test recipe = zc.recipe.testrunner diff -r ed2dc23f7f99 -r df022d00a9c4 buildout.cfg --- a/buildout.cfg Fri Jan 18 15:32:56 2019 +0100 +++ b/buildout.cfg Wed Nov 27 15:57:21 2019 +0100 @@ -1,56 +1,32 @@ [buildout] -eggs-directory = /var/local/env/pyams/eggs +eggs-directory = eggs extends = http://download.ztfy.org/pyams/pyams-dev.cfg find-links = http://download.ztfy.org/eggs - socket-timeout = 3 -#allow-picked-versions = false +versions = versions +allow-picked-versions = false show-picked-versions = true newest = false -allow-hosts = - bitbucket.org - *.python.org - *.sourceforge.net - github.com - -versions = versions - src = src develop = . ../ext/lingua - ../pyams_catalog - ../pyams_file - ../pyams_form - ../pyams_i18n - ../pyams_pagelet - ../pyams_skin ../pyams_template ../pyams_utils - ../pyams_viewlet - ../pyams_zmi parts = package i18n pyflakes + pylint test [package] recipe = zc.recipe.egg -eggs = - chameleon - pyams_utils - pyams_viewlet - pyramid - zope.component - zope.configuration - zope.contentprovider - zope.interface - zope.location - zope.schema +eggs = pyams_viewlet +interpreter = python [i18n] recipe = zc.recipe.egg @@ -70,9 +46,17 @@ on_install = true cmds = ${buildout:develop}/bin/${pyflakes:scripts} +[pylint] +recipe = zc.recipe.egg +eggs = + ${package:eggs} + pylint +entry-points = pylint=pylint.lint:Run +arguments = sys.argv[1:] + [test] recipe = zc.recipe.testrunner eggs = pyams_viewlet [test] [versions] -pyams_viewlet = 0.1.7 +pyams_viewlet = 0.1.8 diff -r ed2dc23f7f99 -r df022d00a9c4 docs/HISTORY.txt --- a/docs/HISTORY.txt Fri Jan 18 15:32:56 2019 +0100 +++ b/docs/HISTORY.txt Wed Nov 27 15:57:21 2019 +0100 @@ -1,6 +1,11 @@ Changelog ========= +0.1.8 +----- + - code cleanup + - Gitlab-CI integration + 0.1.7 ----- - updated "provider" TALES expression to be able to provide arguments diff -r ed2dc23f7f99 -r df022d00a9c4 docs/README.txt --- a/docs/README.txt Fri Jan 18 15:32:56 2019 +0100 +++ b/docs/README.txt Wed Nov 27 15:57:21 2019 +0100 @@ -0,0 +1,31 @@ +===================== +PyAMS_viewlet package +===================== + +.. contents:: + + +What is PyAMS +============= + +PyAMS (Pyramid Application Management Suite) is a small suite of packages written for applications +and content management with the Pyramid framework. + +**PyAMS** is actually mainly used to manage web sites through content management applications (CMS, +see PyAMS_content package), but many features are generic and can be used inside any kind of web +application. + + +What is PyAMS_viewlet? +====================== + +PyAMS_viewlet is a package which defines components called "content providers"; these content +providers are named adapters which can be used inside Chameleon templates. + +PyAMS_viewlet is an adaptation of zope.viewlet package to be used inside Pyramid. + + +How to use pyams_viewlet? +========================= + +A whole set of PyAMS documentation is available on `ReadTheDocs `_ diff -r ed2dc23f7f99 -r df022d00a9c4 setup.py --- a/setup.py Fri Jan 18 15:32:56 2019 +0100 +++ b/setup.py Wed Nov 27 15:57:21 2019 +0100 @@ -25,14 +25,14 @@ README = os.path.join(DOCS, 'README.txt') HISTORY = os.path.join(DOCS, 'HISTORY.txt') -version = '0.1.7' +version = '0.1.8' long_description = open(README).read() + '\n\n' + open(HISTORY).read() tests_require = [] setup(name='pyams_viewlet', version=version, - description="PyAMS base viewlet interfaces and classes; z3c.viewlet package adapted to Pyramid", + description="PyAMS viewlet interfaces and classes; z3c.viewlet package adapted to Pyramid", long_description=long_description, classifiers=[ "License :: OSI Approved :: Zope Public License", @@ -60,8 +60,11 @@ 'setuptools', # -*- Extra requirements: -*- 'chameleon', + 'pyams_template', 'pyams_utils', 'pyramid', + 'pyramid_zope_request', + 'venusian', 'zope.component', 'zope.configuration', 'zope.contentprovider', diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet.egg-info/PKG-INFO --- a/src/pyams_viewlet.egg-info/PKG-INFO Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet.egg-info/PKG-INFO Wed Nov 27 15:57:21 2019 +0100 @@ -1,16 +1,52 @@ Metadata-Version: 2.1 Name: pyams-viewlet -Version: 0.1.7 -Summary: PyAMS base viewlet interfaces and classes; z3c.viewlet package adapted to Pyramid +Version: 0.1.8 +Summary: PyAMS viewlet interfaces and classes; z3c.viewlet package adapted to Pyramid Home-page: http://hg.ztfy.org/pyams/pyams_viewlet Author: Thierry Florac Author-email: tflorac@ulthar.net License: ZPL -Description: +Description: ===================== + PyAMS_viewlet package + ===================== + + .. contents:: + + + What is PyAMS + ============= + + PyAMS (Pyramid Application Management Suite) is a small suite of packages written for applications + and content management with the Pyramid framework. + + **PyAMS** is actually mainly used to manage web sites through content management applications (CMS, + see PyAMS_content package), but many features are generic and can be used inside any kind of web + application. + + + What is PyAMS_viewlet? + ====================== + + PyAMS_viewlet is a package which defines components called "content providers"; these content + providers are named adapters which can be used inside Chameleon templates. + + PyAMS_viewlet is an adaptation of zope.viewlet package to be used inside Pyramid. + + + How to use pyams_viewlet? + ========================= + + A whole set of PyAMS documentation is available on `ReadTheDocs `_ + Changelog ========= + 0.1.8 + ----- + - code cleanup + - Gitlab-CI integration + 0.1.7 ----- - updated "provider" TALES expression to be able to provide arguments diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet.egg-info/SOURCES.txt --- a/src/pyams_viewlet.egg-info/SOURCES.txt Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet.egg-info/SOURCES.txt Wed Nov 27 15:57:21 2019 +0100 @@ -4,6 +4,7 @@ docs/README.txt src/pyams_viewlet/__init__.py src/pyams_viewlet/configure.zcml +src/pyams_viewlet/interfaces.py src/pyams_viewlet/manager.py src/pyams_viewlet/meta.zcml src/pyams_viewlet/metaconfigure.py @@ -18,8 +19,7 @@ src/pyams_viewlet.egg-info/not-zip-safe src/pyams_viewlet.egg-info/requires.txt src/pyams_viewlet.egg-info/top_level.txt -src/pyams_viewlet/doctests/README.txt -src/pyams_viewlet/interfaces/__init__.py +src/pyams_viewlet/doctests/README.rst src/pyams_viewlet/tests/__init__.py src/pyams_viewlet/tests/test_utilsdocs.py src/pyams_viewlet/tests/test_utilsdocstrings.py \ No newline at end of file diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet.egg-info/requires.txt --- a/src/pyams_viewlet.egg-info/requires.txt Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet.egg-info/requires.txt Wed Nov 27 15:57:21 2019 +0100 @@ -1,7 +1,10 @@ setuptools chameleon +pyams_template pyams_utils pyramid +pyramid_zope_request +venusian zope.component zope.configuration zope.contentprovider diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/__init__.py --- a/src/pyams_viewlet/__init__.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/__init__.py Wed Nov 27 15:57:21 2019 +0100 @@ -10,18 +10,26 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' +"""PyAMS_viewlet package + +Viewlets provide a generic framework for building pluggable user interfaces. + +Generally speaking, viewlets managers are a special type of content providers, which are used +inside a page layout to define "regions" into which other content providers called "viewlets" +can be plugged. + +By using this approach, developers can extend PyAMS features by registering new components +which will integrate easilly into default PyAMS layout; these viewlets are registered to be +plugged into a specific viewlet manager, which "owns" its viewlets and is defined by a +specific interface. +""" + +from chameleon import PageTemplateFile +from pyramid.i18n import TranslationStringFactory + +from pyams_viewlet.provider import ProviderExpr -# import standard library - -# import interfaces - -# import packages -from chameleon import PageTemplateFile -from pyams_viewlet.provider import ProviderExpr - -from pyramid.i18n import TranslationStringFactory _ = TranslationStringFactory('pyams_viewlet') diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/doctests/README.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_viewlet/doctests/README.rst Wed Nov 27 15:57:21 2019 +0100 @@ -0,0 +1,76 @@ +===================== +pyams_viewlet package +===================== + +These doctests are based on zope.viewlet doctests. + +In this implementation of viewlets, we first have to define *viewlet managers*, which are special +content providers which manage a special type of content providers called *viewlets*. Every +viewlets manager handles the viewlets registered for it: + + >>> from pyramid.testing import setUp, tearDown + >>> config = setUp() + + >>> from pyams_viewlet.interfaces import IViewletManager + + >>> class ILeftColumn(IViewletManager): + ... """Left column viewlet manager""" + +We can then create a viewlet manager factory using this interface: + + >>> from pyams_viewlet.manager import ViewletManagerFactory + >>> LeftColumn = ViewletManagerFactory('left-column', ILeftColumn) + +Having the factory, we can instantiate it: + + >>> from zope.interface import implementer, Interface + >>> @implementer(Interface) + ... class Content: + ... """Content class""" + >>> content = Content() + + >>> from pyramid.interfaces import IView, IRequest + >>> from pyramid.testing import DummyRequest + >>> request = DummyRequest() + + >>> @implementer(IView) + ... class View: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + >>> view = View(content, request) + + >>> left_column = LeftColumn(content, request, view) + +Actually, viewlet manager doesn't render anything: + + >>> left_column.update() + >>> left_column.render() + '' + +We have to create and register viewlets for the manager: + + >>> from pyams_viewlet.interfaces import IViewlet + >>> from pyams_viewlet.viewlet import EmptyViewlet + >>> class TextBox(EmptyViewlet): + ... def render(self): + ... return '
Text box!
' + ... def __repr__(self): + ... return '' % id(self) + + >>> config.registry.registerAdapter(TextBox, + ... (Interface, IRequest, IView, ILeftColumn), + ... IViewlet, name='text-box') + + >>> left_column.render() + '' + +Viewlet managers are memoized on rendering, so we have to reset it's state if we want to +render it another time: + + >>> left_column.reset() + >>> left_column.update() + >>> left_column.render() + '
Text box!
' + + >>> tearDown() diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/doctests/README.txt --- a/src/pyams_viewlet/doctests/README.txt Fri Jan 18 15:32:56 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -================== -ZTFY.utils package -================== - -Introduction ------------- - -This package is composed of a set of utility functions, which in complement with zope.app.zapi -package can make Zope management easier. - - -Unicode functions ------------------ - -While working with extended characters sets containing accentuated characters, it's necessary to -conversing strings to UTF8 so that they can be used without any conversion problem. - - >>> from ztfy.utils import unicode - -'translateString' is a utility function which can be used, for example, to generate an object's id -without space and with accentuated characters converted to their unaccentuated version: - - >>> sample = 'Mon titre accentué' - >>> unicode.translateString(sample) - u'mon titre accentue' - -Results are lower-cased by default ; this can be avoided be setting the 'forceLower' parameter -to False: - - >>> unicode.translateString(sample, forceLower=False) - u'Mon titre accentue' - -If input string can contain 'slashes' (/) or 'backslashes' (\), they are normally removed ; -by using the 'escapeSlashes' parameter, the input string is splitted and only the last element is -returned ; this is handy to handle filenames on Windows platform: - - >>> sample = 'Autre / chaîne / accentuée' - >>> unicode.translateString(sample) - u'autre chaine accentuee' - >>> unicode.translateString(sample, escapeSlashes=True) - u'accentuee' - >>> sample = 'C:\\Program Files\\My Application\\test.txt' - >>> unicode.translateString(sample) - u'cprogram filesmy applicationtest.txt' - >>> unicode.translateString(sample, escapeSlashes=True) - u'test.txt' - -To remove remaining spaces or convert them to another character, you can use the "spaces" parameter -which can contain any string to be used instead of initial spaces: - - >>> sample = 'C:\\Program Files\\My Application\\test.txt' - >>> unicode.translateString(sample, spaces=' ') - u'cprogram filesmy applicationtest.txt' - >>> unicode.translateString(sample, spaces='-') - u'cprogram-filesmy-applicationtest.txt' - -Spaces replacement is made in the last step, so using it with "escapeSlashes" parameter only affects -the final result: - - >>> unicode.translateString(sample, escapeSlashes=True, spaces='-') - u'test.txt' - -Unicode module also provides encoding and decoding functions: - - >>> var = 'Chaîne accentuée' - >>> unicode.decode(var) - u'Cha\xeene accentu\xe9e' - >>> unicode.encode(unicode.decode(var)) == var - True - - >>> utf = u'Cha\xeene accentu\xe9e' - >>> unicode.encode(utf) - 'Cha\xc3\xaene accentu\xc3\xa9e' - >>> unicode.decode(unicode.encode(utf)) == utf - True - - -Dates functions ---------------- - -Dates functions are used to convert dates from/to string representation: - - >>> from datetime import datetime - >>> from ztfy.utils import date - >>> now = datetime.fromtimestamp(1205000000) - >>> now - datetime.datetime(2008, 3, 8, 19, 13, 20) - -You can get an unicode representation of a date in ASCII format using 'unidate' fonction ; date is -converted to GMT: - - >>> udate = date.unidate(now) - >>> udate - u'2008-03-08T19:13:20+00:00' - -'parsedate' can be used to convert ASCII format into datetime: - - >>> ddate = date.parsedate(udate) - >>> ddate - datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=) - -'datetodatetime' can be used to convert a 'date' type to a 'datetime' value ; if a 'datetime' value -is used as argument, it is returned 'as is': - - >>> ddate.date() - datetime.date(2008, 3, 8) - >>> date.datetodatetime(ddate) - datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=) - >>> date.datetodatetime(ddate.date()) - datetime.datetime(2008, 3, 8, 0, 0) - - -Timezones handling ------------------- - -Timezones handling game me headaches at first. I finally concluded that the best way (for me !) to handle -TZ data was to store every datetime value in GMT timezone. -As far as I know, there is no easy way to know the user's timezone from his request settings. So you can: -- store this timezone in user's profile, -- define a static server's timezone -- create and register a ServerTimezoneUtility to handle server default timezone. - -My current default user's timezone is set to 'Europe/Paris' ; you should probably update this setting in -'timezone.py' if you are located elsewhere. - - >>> from ztfy.utils import timezone - >>> timezone.tztime(ddate) - datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=) - -'gmtime' function can be used to convert a datetime to GMT: - - >>> timezone.gmtime(now) - datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=) diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/interfaces.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_viewlet/interfaces.py Wed Nov 27 15:57:21 2019 +0100 @@ -0,0 +1,66 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +"""PyAMS_viewlet.interfaces module + +The module defines viewlet and viewlets manager interfaces. +""" + +from zope.contentprovider.interfaces import IContentProvider +from zope.interface import Attribute +from zope.interface.common.mapping import IReadMapping + + +__docformat__ = 'restructuredtext' + + +class IViewlet(IContentProvider): + """A content provider that is managed by another content provider, known + as viewlet manager. + + Note that you *cannot* call viewlets directly as a provider, i.e. through + the TALES ``provider`` expression, since it always has to know its manager. + """ + + manager = Attribute("""The Viewlet Manager + + The viewlet manager for which the viewlet is registered. The viewlet + manager will contain any additional data that was provided by the + view. + """) + + +class IViewletManager(IContentProvider, IReadMapping): + """A component that provides access to the content providers. + + The viewlet manager's responsibilities are: + + (1) Aggregation of all viewlets registered for the manager. + + (2) Apply a set of filters to determine the availability of the + viewlets. + + (3) Sort the viewlets based on some implemented policy. + + (4) Provide an environment in which the viewlets are rendered. + + (5) Render itself by rendering the HTML content of the viewlets. + """ + + def filter(self, viewlets): + """Filter manager viewlets""" + + def sort(self, viewlets): + """Sort manager viewlets""" + + def reset(self): + """Reset manager status; this can be required if the manager between renderings""" diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/interfaces/__init__.py --- a/src/pyams_viewlet/interfaces/__init__.py Fri Jan 18 15:32:56 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -# -# Copyright (c) 2008-2015 Thierry Florac -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# - -__docformat__ = 'restructuredtext' - - -# import standard library - -# import interfaces -from zope.contentprovider.interfaces import IContentProvider -from zope.interface.common.mapping import IReadMapping - -# import packages -from zope.interface import Attribute - - -class IViewlet(IContentProvider): - """A content provider that is managed by another content provider, known - as viewlet manager. - - Note that you *cannot* call viewlets directly as a provider, i.e. through - the TALES ``provider`` expression, since it always has to know its manager. - """ - - manager = Attribute("""The Viewlet Manager - - The viewlet manager for which the viewlet is registered. The viewlet - manager will contain any additional data that was provided by the - view, for example the TAL namespace attributes. - """) - - -class IViewletManager(IContentProvider, IReadMapping): - """A component that provides access to the content providers. - - The viewlet manager's responsibilities are: - - (1) Aggregation of all viewlets registered for the manager. - - (2) Apply a set of filters to determine the availability of the - viewlets. - - (3) Sort the viewlets based on some implemented policy. - - (4) Provide an environment in which the viewlets are rendered. - - (5) Render itself containing the HTML content of the viewlets. - """ diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/manager.py --- a/src/pyams_viewlet/manager.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/manager.py Wed Nov 27 15:57:21 2019 +0100 @@ -10,7 +10,11 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' +"""PyAMS_viewlet.manager module + +This module defines the viewlet manager, as weel as a "viewletmanager_config" decorator +which can be used instead of ZCML to declare a viewlets manager. +""" import logging @@ -19,9 +23,9 @@ from pyramid.httpexceptions import HTTPUnauthorized from pyramid.interfaces import IRequest, IView from pyramid.threadlocal import get_current_registry -from zope.component.interfaces import ComponentLookupError from zope.contentprovider.interfaces import BeforeUpdateEvent from zope.interface import Interface, classImplements, implementer +from zope.interface.interfaces import ComponentLookupError from zope.location.interfaces import ILocation from pyams_template.template import get_view_template @@ -29,19 +33,26 @@ from pyams_viewlet.interfaces import IViewlet, IViewletManager -logger = logging.getLogger('PyAMS (viewlet)') +__docformat__ = 'restructuredtext' + + +LOGGER = logging.getLogger('PyAMS (viewlet)') @implementer(IViewletManager) -class ViewletManager(object): +class ViewletManager: """The Viewlet Manager base - A generic manager class which can be instantiated + A generic manager class which can be instantiated. + + A viewlet manager can be used as mapping and can get to a given viewlet by it's name. """ permission = None template = None + viewlets = None + def __init__(self, context, request, view): self.__updated = False self.__parent__ = view @@ -61,8 +72,10 @@ # If the viewlet cannot be accessed, then raise an # unauthorized error - if viewlet.permission and not self.request.has_permission(viewlet.permission, context=self.context): - raise HTTPUnauthorized('You are not authorized to access the provider called `%s`.' % name) + if viewlet.permission and not self.request.has_permission(viewlet.permission, + context=self.context): + raise HTTPUnauthorized('You are not authorized to access the ' + 'provider called `%s`.' % name) # Return the viewlet. return viewlet @@ -79,38 +92,31 @@ return bool(self.get(name, False)) def filter(self, viewlets): - """Sort out all content providers + """Filter out all content providers - ``viewlets`` is a list of tuples of the form (name, viewlet). + :param viewlets: list of viewlets, each element being a tuple of (name, viewlet) form + + Default implementation is filtering out viewlets for which a permission which is not + granted to the current principal is defined. """ - # Only return viewlets accessible to the principal request = self.request - return [(name, viewlet) for name, viewlet in viewlets - if (not viewlet.permission) or request.has_permission(viewlet.permission, context=self.context)] - def sort(self, viewlets): + def _filter(viewlet): + """Filter viewlet based on permission""" + _, viewlet = viewlet + return (not viewlet.permission) or request.has_permission(viewlet.permission, + context=self.context) + + return filter(_filter, viewlets) + + def sort(self, viewlets): # pylint: disable=no-self-use """Sort the viewlets. - ``viewlets`` is a list of tuples of the form (name, viewlet). - """ - # By default, we are not sorting by viewlet name. - return sorted(viewlets, key=lambda x: x[0]) + :param viewlets: list of viewlets, each element being a tuple of (name, viewlet) form - def update(self): - """See zope.contentprovider.interfaces.IContentProvider""" - # check permission - if self.permission and not self.request.has_permission(self.permission, context=self.context): - return - # get the viewlets from now on - self.viewlets = [] - append = self.viewlets.append - for name, viewlet in self._get_viewlets(): - if ILocation.providedBy(viewlet): - viewlet.__name__ = name - append(viewlet) - # and update them... - self._update_viewlets() - self.__updated = True + Default implementation is sorting viewlets by name + """ + return sorted(viewlets, key=lambda x: x[0]) def _get_viewlets(self): """Find all content providers for the region""" @@ -128,19 +134,40 @@ registry.notify(BeforeUpdateEvent(viewlet, self.request)) viewlet.update() + def update(self): + """See :py:class:`zope.contentprovider.interfaces.IContentProvider`""" + # check permission + if self.permission and not self.request.has_permission(self.permission, + context=self.context): + return + # get the viewlets from now on + self.viewlets = [] + append = self.viewlets.append + for name, viewlet in self._get_viewlets(): + if ILocation.providedBy(viewlet): + viewlet.__name__ = name + append(viewlet) + # and update them... + self._update_viewlets() + self.__updated = True + def render(self): - """See zope.contentprovider.interfaces.IContentProvider""" + """See :py:class:`zope.contentprovider.interfaces.IContentProvider`""" # Check for previous update if not (self.__updated and self.viewlets): return '' # Now render the view if self.template: - return self.template(viewlets=self.viewlets) - else: - return '\n'.join([viewlet.render() for viewlet in self.viewlets]) + return self.template(viewlets=self.viewlets) # pylint: disable=not-callable + return '\n'.join([viewlet.render() for viewlet in self.viewlets]) + + def reset(self): + """Reset viewlet manager status""" + self.viewlets = None + self.__updated = False -def ViewletManagerFactory(name, interface, bases=(), cdict=None): +def ViewletManagerFactory(name, interface, bases=(), cdict=None): # pylint: disable=invalid-name """Viewlet manager factory""" attr_dict = {'__name__': name} @@ -149,17 +176,19 @@ if ViewletManager not in bases: # Make sure that we do not get a default viewlet manager mixin, if the # provided base is already a full viewlet manager implementation. + # pylint: disable=no-value-for-parameter if not (len(bases) == 1 and IViewletManager.implementedBy(bases[0])): bases = bases + (ViewletManager,) - viewlet_manager_class = type('' % interface.getName(), bases, attr_dict) + viewlet_manager_class = type('' % interface.getName(), + bases, attr_dict) classImplements(viewlet_manager_class, interface) return viewlet_manager_class def get_weight(item): """Get sort weight of a given viewlet""" - name, viewlet = item + _, viewlet = item try: return int(viewlet.weight) except (TypeError, AttributeError): @@ -168,7 +197,7 @@ def get_label(item, request=None): """Get sort label of a given viewlet""" - name, viewlet = item + _, viewlet = item try: if request is None: request = check_request() @@ -192,48 +221,51 @@ return sorted(viewlets, key=lambda x: get_weight_and_label(x, request=self.request)) -def is_available(viewlet): - try: - return ((not viewlet.permission) or - viewlet.request.has_permission(viewlet.permission, context=viewlet.context)) and \ - viewlet.available - except AttributeError: - return True - - class ConditionalViewletManager(WeightOrderedViewletManager): """Conditional weight ordered viewlet managers""" def filter(self, viewlets): - """Sort out all viewlets which are explicit not available + """Sort out all viewlets which are explicitly not available + + Viewlets shoud have a boolean "available" attribute to specify if they are available + or not. + """ - ``viewlets`` is a list of tuples of the form (name, viewlet). - """ - return [(name, viewlet) for name, viewlet in viewlets if is_available(viewlet)] + def is_available(viewlet): + _, viewlet = viewlet + try: + return ((not viewlet.permission) or + viewlet.request.has_permission(viewlet.permission, + context=viewlet.context)) and \ + viewlet.available + except AttributeError: + return True + + return filter(is_available, viewlets) -class TemplateBasedViewletManager(object): +class TemplateBasedViewletManager: """Template based viewlet manager mixin class""" template = get_view_template() -class viewletmanager_config(object): +class viewletmanager_config: # pylint: disable=invalid-name """Class or interface decorator used to declare a viewlet manager You can provide same arguments as in 'viewletManager' ZCML directive: - @name = name of the viewlet; may be unique for a given viewlet manager - @view = the view class or interface for which viewlet is displayed - @for_ = the context class or interface for which viewlet is displayed - @permission = name of a permission required to display the viewlet - @layer = request interface required to display the viewlet - @class_ = the class handling the viewlet manager; if the decorator is applied - on an interface and if this argument is not provided, the viewlet manager - will be handled by a default ViewletManager class - @provides = an interface the viewlet manager provides; if the decorator is - applied on an Interface, this will be the decorated interface; if the - decorated is applied on a class and if this argument is not specified, - the manager will provide IViewletManager interface. + :param name: name of the viewlet; may be unique for a given viewlet manager + :param view: the view class or interface for which viewlet is displayed + :param for_: the context class or interface for which viewlet is displayed + :param permission: name of a permission required to display the viewlet + :param layer: request interface required to display the viewlet + :param class_: the class handling the viewlet manager; if the decorator is applied + on an interface and if this argument is not provided, the viewlet manager + will be handled by a default ViewletManager class + :param provides: an interface the viewlet manager provides; if the decorator is + applied on an Interface, this will be the decorated interface; if the + decorated is applied on a class and if this argument is not specified, + the manager will provide IViewletManager interface. """ venusian = venusian # for testing injection @@ -249,22 +281,22 @@ def __call__(self, wrapped): settings = self.__dict__.copy() - def callback(context, name, ob): + def callback(context, name, obj): # pylint: disable=unused-argument cdict = {'__name__': settings.get('name')} if 'permission' in settings: cdict['permission'] = settings.get('permission') - if issubclass(ob, Interface): + if issubclass(obj, Interface): class_ = settings.get('class_', ViewletManager) - provides = ob + provides = obj else: - class_ = ob + class_ = obj provides = settings.get('provides', IViewletManager) new_class = ViewletManagerFactory(settings.get('name'), provides, (class_,), cdict) - logger.debug("Registering viewlet manager {0} ({1})".format(settings.get('name'), + LOGGER.debug("Registering viewlet manager {0} ({1})".format(settings.get('name'), str(new_class))) - config = context.config.with_package(info.module) + config = context.config.with_package(info.module) # pylint: disable=no-member config.registry.registerAdapter(new_class, (settings.get('context', Interface), settings.get('layer', IRequest), @@ -273,12 +305,12 @@ info = self.venusian.attach(wrapped, callback, category='pyams_viewlet') - if info.scope == 'class': + if info.scope == 'class': # pylint: disable=no-member # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if settings.get('attr') is None: settings['attr'] = wrapped.__name__ - settings['_info'] = info.codeinfo # fbo "action_method" + settings['_info'] = info.codeinfo # pylint: disable=no-member return wrapped diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/metaconfigure.py --- a/src/pyams_viewlet/metaconfigure.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/metaconfigure.py Wed Nov 27 15:57:21 2019 +0100 @@ -10,22 +10,23 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' - - -# import standard library +"""PyAMS_viewlet.metaconfigure module -# import interfaces -from pyams_viewlet.interfaces import IViewletManager, IViewlet -from pyramid.interfaces import IRequest, IView +This module provides ZCML directives handlers. +""" -# import packages -from pyams_viewlet.manager import ViewletManager, ViewletManagerFactory from pyramid.exceptions import ConfigurationError +from pyramid.interfaces import IRequest, IView from zope.component import zcml from zope.component.interface import provideInterface from zope.interface import Interface, classImplements +from pyams_viewlet.interfaces import IViewlet, IViewletManager +from pyams_viewlet.manager import ViewletManager, ViewletManagerFactory + + +__docformat__ = 'restructuredtext' + def ViewletManagerDirective(_context, name, context=Interface, @@ -34,6 +35,8 @@ provides=IViewletManager, class_=None, permission=None): + # pylint: disable=invalid-name,too-many-arguments + """Viewlet manager ZCML directive""" # If class is not given we use the basic viewlet manager. if class_ is None: @@ -66,16 +69,19 @@ attribute='render', permission=None, **kwargs): + # pylint: disable=invalid-name,too-many-arguments + """Viewlet ZCML directive""" # Make sure the has the right form, if specified. if attribute != 'render': if not hasattr(class_, attribute): raise ConfigurationError("The provided class doesn't have the specified attribute") - cdict = {} - cdict['__name__'] = name - cdict['__page_attribute__'] = attribute - cdict['permission'] = permission + cdict = { + '__name__': name, + '__page_attribute__': attribute, + 'permission': permission + } cdict.update(kwargs) new_class = type(class_.__name__, (class_,), cdict) diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/metadirectives.py --- a/src/pyams_viewlet/metadirectives.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/metadirectives.py Wed Nov 27 15:57:21 2019 +0100 @@ -10,19 +10,20 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' - - -# import standard library +"""PyAMS_viewlet.metadirectives module -# import interfaces -from pyams_viewlet.interfaces import IViewletManager +This template provides definition of ZCML directives. +""" -# import packages from zope.configuration.fields import GlobalInterface, GlobalObject from zope.interface import Interface from zope.schema import TextLine +from pyams_viewlet.interfaces import IViewletManager + + +__docformat__ = 'restructuredtext' + class IContentProvider(Interface): """A directive to register a simple content provider. @@ -59,9 +60,12 @@ required=False) layer = GlobalInterface(title="The layer the view is in", - description="""A skin is composed of layers. It is common to put skin - specific views in a layer named after the skin. If the 'layer' - attribute is not supplied, it defaults to 'default'.""", + description="A skin is composed of layers; layers are defined as " + "interfaces, which are provided to the request when the " + "skin is applied. It is common to put skin " + "specific views in a layer named after the skin. If the " + "'layer' attribute is not supplied, it defaults to " + "IRequest, which is the base interface of any request.", required=False) @@ -101,4 +105,5 @@ # Arbitrary keys and values are allowed to be passed to the viewlet. +# pylint: disable=no-value-for-parameter IViewletDirective.setTaggedValue('keyword_arguments', True) diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/provider.py --- a/src/pyams_viewlet/provider.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/provider.py Wed Nov 27 15:57:21 2019 +0100 @@ -10,24 +10,32 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' +"""PyAMS_viewlet.provider module + +This module provides the "provider:" TALES expression, which allows inclusion of any registered +content provider into a Chameleon or ZPT template. +""" import re from chameleon.astutil import Symbol from chameleon.tales import StringExpr from pyramid_zope_request import PyramidPublisherRequest -from zope.contentprovider.interfaces import BeforeUpdateEvent, ContentProviderLookupError, IContentProvider +from zope.contentprovider.interfaces import BeforeUpdateEvent, ContentProviderLookupError, \ + IContentProvider from zope.contentprovider.tales import addTALNamespaceData from zope.location.interfaces import ILocation from pyams_utils.tales import ContextExprMixin -FUNCTION_EXPRESSION = re.compile('(.+)\((.+)\)', re.MULTILINE | re.DOTALL) -ARGUMENTS_EXPRESSION = re.compile('[^(,)]+') +__docformat__ = 'restructuredtext' + -CONTENT_PROVIDER_NAME = re.compile('([A-Za-z0-9_\-\.]+)') +FUNCTION_EXPRESSION = re.compile(r'(.+)\((.+)\)', re.MULTILINE | re.DOTALL) +ARGUMENTS_EXPRESSION = re.compile(r'[^(,)]+') + +CONTENT_PROVIDER_NAME = re.compile(r'([A-Za-z0-9_\-\.]+)') def render_content_provider(econtext, name): @@ -67,7 +75,7 @@ except ValueError: args = arg.split('.') result = econtext.get(args.pop(0)) - for arg in args: + for arg in args: # pylint: disable=redefined-argument-from-local result = getattr(result, arg) return result else: @@ -77,7 +85,7 @@ context = econtext.get('context') request = econtext.get('request') if isinstance(request, PyramidPublisherRequest): - request = request._request + request = request._request # pylint: disable=protected-access view = econtext.get('view') args, kwargs = [], {} diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/tests/__init__.py --- a/src/pyams_viewlet/tests/__init__.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/tests/__init__.py Wed Nov 27 15:57:21 2019 +0100 @@ -1,1 +1,29 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +""" +Generic Test case for pyams_viewlet doctest +""" + +import os +import sys + +__docformat__ = 'restructuredtext' + + +def get_package_dir(value): + """Get package directory""" + + package_dir = os.path.split(value)[0] + if package_dir not in sys.path: + sys.path.append(package_dir) + return package_dir diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/tests/test_utilsdocs.py --- a/src/pyams_viewlet/tests/test_utilsdocs.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/tests/test_utilsdocs.py Wed Nov 27 15:57:21 2019 +0100 @@ -1,5 +1,3 @@ -### -*- coding: utf-8 -*- #################################################### -############################################################################## # # Copyright (c) 2008-2015 Thierry Florac # All Rights Reserved. @@ -11,23 +9,25 @@ # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # -############################################################################## """ Generic Test case for pyams_viewlet doctest """ + +import doctest +import os +import unittest + +from pyams_viewlet.tests import get_package_dir + + __docformat__ = 'restructuredtext' -import unittest -import doctest -import sys -import os +CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) -current_dir = os.path.dirname(__file__) - -def doc_suite(test_dir, setUp=None, tearDown=None, globs=None): - """Returns a test suite, based on doctests found in /doctest.""" +def doc_suite(test_dir, setUp=None, tearDown=None, globs=None): # pylint: disable=invalid-name + """Returns a test suite, based on doctests found in /doctests""" suite = [] if globs is None: globs = globals() @@ -35,15 +35,12 @@ flags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE) - package_dir = os.path.split(test_dir)[0] - if package_dir not in sys.path: - sys.path.append(package_dir) - + package_dir = get_package_dir(test_dir) doctest_dir = os.path.join(package_dir, 'doctests') # filtering files on extension docs = [os.path.join(doctest_dir, doc) for doc in - os.listdir(doctest_dir) if doc.endswith('.txt')] + os.listdir(doctest_dir) if doc.endswith('.txt') or doc.endswith('.rst')] for test in docs: suite.append(doctest.DocFileSuite(test, optionflags=flags, @@ -53,10 +50,11 @@ return unittest.TestSuite(suite) + def test_suite(): """returns the test suite""" - return doc_suite(current_dir) + return doc_suite(CURRENT_DIR) + if __name__ == '__main__': unittest.main(defaultTest='test_suite') - diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/tests/test_utilsdocstrings.py --- a/src/pyams_viewlet/tests/test_utilsdocstrings.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/tests/test_utilsdocstrings.py Wed Nov 27 15:57:21 2019 +0100 @@ -1,5 +1,3 @@ -### -*- coding: utf-8 -*- #################################################### -############################################################################## # # Copyright (c) 2008-2015 Thierry Florac # All Rights Reserved. @@ -11,21 +9,22 @@ # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # -############################################################################## """ Generic Test case for pyams_viewlet doc strings """ + +import doctest +import os +import sys +import unittest + + __docformat__ = 'restructuredtext' -import unittest -import doctest -import sys -import os +CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) -current_dir = os.path.abspath(os.path.dirname(__file__)) - def doc_suite(test_dir, globs=None): """Returns a test suite, based on doc tests strings found in /*.py""" suite = [] @@ -45,7 +44,7 @@ docs = [doc for doc in docs if not doc.startswith('__')] for test in docs: - fd = open(os.path.join(package_dir, test)) + fd = open(os.path.join(package_dir, test)) # pylint: disable=invalid-name content = fd.read() fd.close() if '>>> ' not in content: @@ -57,9 +56,11 @@ return unittest.TestSuite(suite) + def test_suite(): """returns the test suite""" - return doc_suite(current_dir) + return doc_suite(CURRENT_DIR) + if __name__ == '__main__': unittest.main(defaultTest='test_suite') diff -r ed2dc23f7f99 -r df022d00a9c4 src/pyams_viewlet/viewlet.py --- a/src/pyams_viewlet/viewlet.py Fri Jan 18 15:32:56 2019 +0100 +++ b/src/pyams_viewlet/viewlet.py Wed Nov 27 15:57:21 2019 +0100 @@ -10,7 +10,11 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' +"""PyAMS_viewlet.viewlet module + +This module provides base content providers and viewlets classes, as well as a decorators +which can be used instead of ZCML declarations to register content providers and viewlets. +""" import logging @@ -23,12 +27,14 @@ from pyams_template.template import get_view_template from pyams_viewlet.interfaces import IViewlet, IViewletManager +__docformat__ = 'restructuredtext' -logger = logging.getLogger('PyAMS (viewlet)') + +LOGGER = logging.getLogger('PyAMS (viewlet)') @implementer(IContentProvider) -class EmptyContentProvider(object): +class EmptyContentProvider: """Empty content provider base class""" permission = None @@ -38,15 +44,17 @@ self.request = request def __call__(self): - if self.permission and not self.request.has_permission(self.permission, context=self.context): + if self.permission and not self.request.has_permission(self.permission, + context=self.context): return '' self.update() return self.render() def update(self): - pass + """See `IContentProvider` interface""" - def render(self): + def render(self): # pylint: disable=no-self-use + """See `IContentProvider` interface""" return '' @@ -71,7 +79,7 @@ self.view = self.__parent__ = view -class contentprovider_config(object): +class contentprovider_config: # pylint: disable=invalid-name """Class decorator used to declare a content provider You can provide same arguments as in 'viewlet' ZCML directive: @@ -95,22 +103,22 @@ def __call__(self, wrapped): settings = self.__dict__.copy() - def callback(context, name, ob): + def callback(context, name, obj): # pylint: disable=unused-argument cdict = { '__name__': settings.get('name'), - '__module__': ob.__module__ + '__module__': obj.__module__ } if 'permission' in settings: settings['permission'] = settings.get('permission') - bases = (ob,) - if not IContentProvider.implementedBy(ob): + bases = (obj,) + if not IContentProvider.implementedBy(obj): # pylint: disable=no-value-for-parameter bases = bases + (ViewContentProvider,) new_class = type('' % settings.get('name'), bases, cdict) - logger.debug("Registering content provider {0} ({1})".format(settings.get('name'), + LOGGER.debug("Registering content provider {0} ({1})".format(settings.get('name'), str(new_class))) - config = context.config.with_package(info.module) + config = context.config.with_package(info.module) # pylint: disable=no-member config.registry.registerAdapter(new_class, (settings.get('context', Interface), settings.get('layer', IRequest), @@ -119,19 +127,19 @@ info = self.venusian.attach(wrapped, callback, category='pyams_viewlet') - if info.scope == 'class': + if info.scope == 'class': # pylint: disable=no-member # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if settings.get('attr') is None: settings['attr'] = wrapped.__name__ - settings['_info'] = info.codeinfo # fbo "action_method" + settings['_info'] = info.codeinfo # pylint: disable=no-member return wrapped @implementer(IViewlet) -class EmptyViewlet(object): +class EmptyViewlet: """Empty viewlet base class""" permission = None @@ -143,9 +151,10 @@ self.manager = manager def update(self): - pass + """See `IContentProvider` interface""" - def render(self): + def render(self): # pylint: disable=no-self-use + """See `IContentProvider` interface""" return '' @@ -155,7 +164,7 @@ render = get_view_template() -class viewlet_config(object): +class viewlet_config: # pylint: disable=invalid-name """Class decorator used to declare a viewlet You can provide same arguments as in 'viewlet' ZCML directive: @@ -181,24 +190,24 @@ def __call__(self, wrapped): settings = self.__dict__.copy() - def callback(context, name, ob): + def callback(context, name, obj): # pylint: disable=unused-argument cdict = { '__name__': settings.get('name'), - '__module__': ob.__module__ + '__module__': obj.__module__ } if 'permission' in settings: cdict['permission'] = settings.get('permission') if 'weight' in settings: cdict['weight'] = settings.get('weight') - bases = (ob,) - if not IViewlet.implementedBy(ob): + bases = (obj,) + if not IViewlet.implementedBy(obj): # pylint: disable=no-value-for-parameter bases = bases + (Viewlet,) new_class = type('' % settings.get('name'), bases, cdict) - logger.debug("Registering viewlet {0} ({1})".format(settings.get('name'), + LOGGER.debug("Registering viewlet {0} ({1})".format(settings.get('name'), str(new_class))) - config = context.config.with_package(info.module) + config = context.config.with_package(info.module) # pylint: disable=no-member config.registry.registerAdapter(new_class, (settings.get('context', Interface), settings.get('layer', IRequest), @@ -208,12 +217,12 @@ info = self.venusian.attach(wrapped, callback, category='pyams_viewlet') - if info.scope == 'class': + if info.scope == 'class': # pylint: disable=no-member # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if settings.get('attr') is None: settings['attr'] = wrapped.__name__ - settings['_info'] = info.codeinfo # fbo "action_method" + settings['_info'] = info.codeinfo # pylint: disable=no-member return wrapped