From ac18b987d07cd00583e4e853a567b30b54e46366 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Mon, 23 Jan 2023 13:49:31 +0100 Subject: [PATCH 1/4] Bumped version for breaking release. --- CHANGES.rst | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 043d247..5aeca5f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,7 @@ Changes ========= -4.4 (unreleased) +5.0 (unreleased) ================ - Add support for Python 3.11. diff --git a/setup.py b/setup.py index 19a0061..62b8b93 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ def read(*rnames): ] setup(name='zope.viewlet', - version='4.4.dev0', + version='5.0.dev0', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Viewlets', From 3f451857bc917208a7d9663675b047e79ae5d5a8 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Mon, 23 Jan 2023 13:49:34 +0100 Subject: [PATCH 2/4] Drop support for Python 2.7, 3.5, 3.6. --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5aeca5f..17b4856 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,8 @@ 5.0 (unreleased) ================ +- Drop support for Python 2.7, 3.5, 3.6. + - Add support for Python 3.11. From 0d00c612e0339126f18b8954354de38e040a544a Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Mon, 23 Jan 2023 13:51:34 +0100 Subject: [PATCH 3/4] Configuring for pure-python --- .github/workflows/tests.yml | 8 ++------ .meta.toml | 3 +-- setup.cfg | 4 ++-- tox.ini | 6 ------ 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cedf24b..d8889f3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,16 +21,12 @@ jobs: config: # [Python version, tox env] - ["3.9", "lint"] - - ["2.7", "py27"] - - ["3.5", "py35"] - - ["3.6", "py36"] - ["3.7", "py37"] - ["3.8", "py38"] - ["3.9", "py39"] - ["3.10", "py310"] - ["3.11", "py311"] - - ["pypy-2.7", "pypy"] - - ["pypy-3.7", "pypy3"] + - ["pypy-3.9", "pypy3"] - ["3.9", "docs"] - ["3.9", "coverage"] @@ -60,7 +56,7 @@ jobs: - name: Coverage if: matrix.config[1] == 'coverage' run: | - pip install coveralls coverage-python-version + pip install coveralls coveralls --service=github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.meta.toml b/.meta.toml index 8f14aae..39f9cc4 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,13 +2,12 @@ # https://github.com/zopefoundation/meta/tree/master/config/pure-python [meta] template = "pure-python" -commit-id = "200573eb414d2228d463da3de7d71a6d6335a704" +commit-id = "487d9939" [python] with-windows = false with-pypy = true with-future-python = false -with-legacy-python = true with-docs = true with-sphinx-doctests = false with-macos = false diff --git a/setup.cfg b/setup.cfg index 1ecf58c..b1ce029 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python [bdist_wheel] -universal = 1 +universal = 0 [flake8] doctests = 1 @@ -16,7 +16,7 @@ ignore = force_single_line = True combine_as_imports = True sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER -known_third_party = six, docutils, pkg_resources +known_third_party = six, docutils, pkg_resources, pytz known_zope = known_first_party = default_section = ZOPE diff --git a/tox.ini b/tox.ini index f260fbb..3684858 100644 --- a/tox.ini +++ b/tox.ini @@ -4,15 +4,11 @@ minversion = 3.18 envlist = lint - py27 - py35 - py36 py37 py38 py39 py310 py311 - pypy pypy3 docs coverage @@ -64,7 +60,6 @@ allowlist_externals = mkdir deps = coverage - coverage-python-version commands = mkdir -p {toxinidir}/parts/htmlcov coverage run -m zope.testrunner --test-path=src {posargs:-vc} @@ -73,7 +68,6 @@ commands = [coverage:run] branch = True -plugins = coverage_python_version source = zope.viewlet [coverage:report] From 8404bdcc22721da93cae54216ff855465fb0ccbf Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Tue, 24 Jan 2023 14:27:11 +0100 Subject: [PATCH 4/4] Drop support for Python 2.7 up to 3.6. --- setup.py | 5 +---- src/zope/viewlet/README.rst | 20 ++++++++++---------- src/zope/viewlet/directives.rst | 20 ++++++++++---------- src/zope/viewlet/manager.py | 6 +++--- src/zope/viewlet/metadirectives.py | 10 +++++----- src/zope/viewlet/tests.py | 30 +++++++----------------------- src/zope/viewlet/viewlet.py | 10 +++++----- 7 files changed, 41 insertions(+), 60 deletions(-) diff --git a/setup.py b/setup.py index 62b8b93..271aa4a 100644 --- a/setup.py +++ b/setup.py @@ -52,11 +52,7 @@ def read(*rnames): 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', @@ -74,6 +70,7 @@ def read(*rnames): packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=['zope'], + python_requires='>=3.7', extras_require={ 'test': TESTS_REQUIRE, 'docs': [ diff --git a/src/zope/viewlet/README.rst b/src/zope/viewlet/README.rst index 9707351..c12b157 100644 --- a/src/zope/viewlet/README.rst +++ b/src/zope/viewlet/README.rst @@ -75,7 +75,7 @@ So initially nothing gets rendered: >>> leftColumn.update() >>> leftColumn.render() - u'' + '' But now we register some :class:`viewlets ` for the manager: @@ -93,7 +93,7 @@ But now we register some :class:`viewlets ... pass ... ... def render(self): - ... return u'
It is sunny today!
' + ... return '
It is sunny today!
' ... ... def __repr__(self): ... return '' % id(self) @@ -121,7 +121,7 @@ But now we register some :class:`viewlets ... pass ... ... def render(self): - ... return u'
Patriots (23) : Steelers (7)
' + ... return '
Patriots (23) : Steelers (7)
' >>> defineChecker(SportBox, viewletChecker) @@ -188,7 +188,7 @@ If a viewlet provides :class:`zope.location.interfaces.ILocation` the the viewlet is registered. >>> [getattr(viewlet, '__name__', None) for viewlet in leftColumn.viewlets] - [u'sport', None] + ['sport', None] You can also lookup the viewlets directly for management purposes: @@ -323,26 +323,26 @@ Let's create some viewlets: ... weight = 1 ... ... def render(self): - ... return u'
first
' + ... return '
first
' >>> class SecondViewlet(viewlet.ViewletBase): ... ... weight = 2 ... ... def render(self): - ... return u'
second
' + ... return '
second
' >>> class ThirdViewlet(viewlet.ViewletBase): ... ... weight = 3 ... ... def render(self): - ... return u'
third
' + ... return '
third
' >>> class UnWeightedViewlet(viewlet.ViewletBase): ... ... def render(self): - ... return u'
unweighted
' + ... return '
unweighted
' >>> defineChecker(FirstViewlet, viewletChecker) >>> defineChecker(SecondViewlet, viewletChecker) @@ -431,7 +431,7 @@ weight and or no available attribute: ... available = True ... ... def render(self): - ... return u'
available
' + ... return '
available
' >>> class UnAvailableViewlet(viewlet.ViewletBase): ... @@ -440,7 +440,7 @@ weight and or no available attribute: ... available = False ... ... def render(self): - ... return u'
not available
' + ... return '
not available
' >>> defineChecker(AvailableViewlet, viewletChecker) >>> defineChecker(UnAvailableViewlet, viewletChecker) diff --git a/src/zope/viewlet/directives.rst b/src/zope/viewlet/directives.rst index 64f019f..2318b4a 100644 --- a/src/zope/viewlet/directives.rst +++ b/src/zope/viewlet/directives.rst @@ -60,7 +60,7 @@ Now let's lookup the manager. This particular registration is pretty boring: True >>> manager.update() >>> manager.render() - u'' + '' However, this registration is not very useful, since we did specify a specific viewlet manager interface, a specific content interface, specific view or @@ -95,7 +95,7 @@ Now we can register register a manager providing this interface: True >>> manager.update() >>> manager.render() - u'' + '' Next let's see what happens, if we specify a template for the viewlet manager: @@ -224,9 +224,9 @@ If we look into the adapter registry, we will find the viewlet: ... (content, request, view, manager), interfaces.IViewlet, ... name='weather') >>> viewlet.render().strip() - u'
sunny
' + '
sunny
' >>> viewlet.extra_string_attributes - u'can be specified' + 'can be specified' The manager now also gives us the output of the one and only viewlet: @@ -260,7 +260,7 @@ Let's now ensure that we can also specify a viewlet class: ... (content, request, view, manager), interfaces.IViewlet, ... name='weather2') >>> viewlet().strip() - u'
sunny
' + '
sunny
' Okay, so the template-driven cases work. But just specifying a class should also work: @@ -268,7 +268,7 @@ also work: >>> class Sport(object): ... weight = 0 ... def __call__(self): - ... return u'Red Sox vs. White Sox' + ... return 'Red Sox vs. White Sox' >>> context = xmlconfig.string(''' ... @@ -285,7 +285,7 @@ also work: >>> viewlet = zope.component.getMultiAdapter( ... (content, request, view, manager), interfaces.IViewlet, name='sport') >>> viewlet() - u'Red Sox vs. White Sox' + 'Red Sox vs. White Sox' It should also be possible to specify an alternative attribute of the class to be rendered upon calling the viewlet: @@ -293,7 +293,7 @@ be rendered upon calling the viewlet: >>> class Stock(object): ... weight = 0 ... def getStockTicker(self): - ... return u'SRC $5.19' + ... return 'SRC $5.19' >>> context = xmlconfig.string(''' ... @@ -312,7 +312,7 @@ be rendered upon calling the viewlet: ... (content, request, view, manager), interfaces.IViewlet, ... name='stock') >>> viewlet.render() - u'SRC $5.19' + 'SRC $5.19' If the class mentions that it implements any interfaces using the old-fashioned style, the resulting viewlet will @@ -358,7 +358,7 @@ specification of any number of keyword arguments: ... (content, request, view, manager), interfaces.IViewlet, ... name='stock2') >>> viewlet.weight - u'8' + '8' Error Scenarios diff --git a/src/zope/viewlet/manager.py b/src/zope/viewlet/manager.py index 0c18388..8e63bff 100644 --- a/src/zope/viewlet/manager.py +++ b/src/zope/viewlet/manager.py @@ -27,7 +27,7 @@ @zope.interface.implementer(interfaces.IViewletManager) -class ViewletManagerBase(object): +class ViewletManagerBase: """The Viewlet Manager Base A generic manager class which can be instantiated. @@ -177,7 +177,7 @@ def render(self): # Now render the view if self.template: return self.template(viewlets=self.viewlets) - return u'\n'.join([viewlet.render() for viewlet in self.viewlets]) + return '\n'.join([viewlet.render() for viewlet in self.viewlets]) def ViewletManager(name, interface, template=None, bases=()): @@ -234,7 +234,7 @@ def render(self): """ # do not render a manager template if no viewlets are avaiable if not self.viewlets: - return u'' + return '' return ViewletManagerBase.render(self) diff --git a/src/zope/viewlet/metadirectives.py b/src/zope/viewlet/metadirectives.py index b91921b..1646822 100644 --- a/src/zope/viewlet/metadirectives.py +++ b/src/zope/viewlet/metadirectives.py @@ -55,13 +55,13 @@ class IContentProvider(Interface): required=True) for_ = zope.configuration.fields.GlobalObject( - title=u"The interface or class this view is for.", + title="The interface or class this view is for.", required=False ) permission = Permission( - title=u"Permission", - description=u"The permission needed to use the view.", + title="Permission", + description="The permission needed to use the view.", required=True ) @@ -151,8 +151,8 @@ class IViewletDirective(ITemplatedContentProvider): manager = zope.configuration.fields.GlobalObject( title=_("view"), - description=u"The interface of the view this viewlet is for. " - u"(default IBrowserView)", + description="The interface of the view this viewlet is for. " + "(default IBrowserView)", required=False, default=interfaces.IViewletManager) diff --git a/src/zope/viewlet/tests.py b/src/zope/viewlet/tests.py index d9c873b..1ee5390 100644 --- a/src/zope/viewlet/tests.py +++ b/src/zope/viewlet/tests.py @@ -15,14 +15,12 @@ """ import doctest import os -import re import sys import unittest import zope.component from zope.component import eventtesting from zope.testing import cleanup -from zope.testing import renormalizing from zope.traversing.testing import setUp as traversingSetUp from zope.viewlet import manager as managers @@ -32,7 +30,7 @@ class TestWeightOrderedViewletManager(unittest.TestCase): def test_render_no_viewlets(self): manager = managers.WeightOrderedViewletManager(None, None, None) - self.assertEqual(u'', manager.render()) + self.assertEqual('', manager.render()) def test_render_with_template(self): manager = managers.WeightOrderedViewletManager(None, None, None) @@ -44,22 +42,17 @@ def test_render_with_template(self): def test_render_without_template(self): manager = managers.WeightOrderedViewletManager(None, None, None) - class Viewlet(object): + class Viewlet: def render(self): - return u"Hi" + return "Hi" manager.viewlets = [Viewlet(), Viewlet()] - self.assertEqual(u"Hi\nHi", manager.render()) + self.assertEqual("Hi\nHi", manager.render()) class TestViewletManagerBase(unittest.TestCase): - # Avoid DeprecationWarning for assertRaisesRegexp on Python 3 while - # coping with Python 2 not having the Regex spelling variant - assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegex', - unittest.TestCase.assertRaisesRegexp) - def test_unauthorized(self): import zope.security from zope.security.interfaces import Unauthorized @@ -87,15 +80,6 @@ def canAccess(*args): manager['name'] -checker = renormalizing.RENormalizing([ - # Python 3 unicode removed the "u". - (re.compile("u('.*?')"), - r"\1"), - (re.compile('u(".*?")'), - r"\1"), -]) - - def doctestSetUp(test): cleanup.setUp() eventtesting.setUp() @@ -118,7 +102,7 @@ def doctestTearDown(test): cleanup.tearDown() -class FakeModule(object): +class FakeModule: """A fake module.""" def __init__(self, dict): @@ -147,7 +131,7 @@ def test_suite(): doctest.DocFileSuite( 'README.rst', setUp=doctestSetUp, tearDown=doctestTearDown, - optionflags=flags, checker=checker, + optionflags=flags, globs={ '__file__': os.path.join( os.path.dirname(__file__), 'README.rst')} @@ -155,7 +139,7 @@ def test_suite(): doctest.DocFileSuite( 'directives.rst', setUp=directivesSetUp, tearDown=directivesTearDown, - optionflags=flags, checker=checker, + optionflags=flags, globs={'__file__': os.path.join( os.path.dirname(__file__), 'directives.rst')} ), diff --git a/src/zope/viewlet/viewlet.py b/src/zope/viewlet/viewlet.py index 7453e28..5c52f6b 100644 --- a/src/zope/viewlet/viewlet.py +++ b/src/zope/viewlet/viewlet.py @@ -33,7 +33,7 @@ class ViewletBase(BrowserView): """Viewlet adapter class used in meta directive as a mixin class.""" def __init__(self, context, request, view, manager): - super(ViewletBase, self).__init__(context, request) + super().__init__(context, request) self.__parent__ = view self.context = context self.request = request @@ -76,7 +76,7 @@ class simple(simpleviewclass.simple): def SimpleViewletClass(template, offering=None, bases=(), attributes=None, - name=u''): + name=''): """A function that can be used to generate a viewlet from a set of information. """ @@ -98,7 +98,7 @@ def SimpleViewletClass(template, offering=None, bases=(), attributes=None, return class_ -class ResourceViewletBase(object): +class ResourceViewletBase: """A simple viewlet for inserting references to resources. This is an abstract class that is expected to be used as a base only. @@ -164,7 +164,7 @@ def CSSViewlet(path, media="all", rel="stylesheet"): return klass -class ResourceBundleViewletBase(object): +class ResourceBundleViewletBase: """A simple viewlet for inserting references to different resources. This is an abstract class that is expected to be used as a base only. @@ -205,7 +205,7 @@ def JavaScriptBundleViewlet(paths): return klass -class CSSResourceBundleViewletBase(object): +class CSSResourceBundleViewletBase: """A simple viewlet for inserting css references to different resources. There is a sequence of dict used for the different resource