From 555124007851789885fd36de91140a9085091bea Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Fri, 27 Jan 2023 08:08:50 +0100 Subject: [PATCH] Config with pure python template (#13) * Drop support for Python 2.7, 3.5, 3.6. * Add support for Python 3.11. --- .github/workflows/tests.yml | 20 ++++--- .gitignore | 1 + .meta.toml | 4 +- CHANGES.rst | 6 ++- CONTRIBUTING.md | 23 ++++++++ MANIFEST.in | 1 + setup.cfg | 13 ++++- setup.py | 15 +++--- src/z3c/table/batch.py | 22 ++++---- src/z3c/table/column.py | 82 ++++++++++++++--------------- src/z3c/table/header.py | 19 +++---- src/z3c/table/interfaces.py | 75 +++++++++++++------------- src/z3c/table/table.py | 48 ++++++++--------- src/z3c/table/testing.py | 33 +++++++----- src/z3c/table/tests.py | 102 ++++++++++++++++++------------------ src/z3c/table/value.py | 2 +- tox.ini | 30 ++++++----- 17 files changed, 266 insertions(+), 230 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 266ec14..0e45a78 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,31 +17,29 @@ jobs: fail-fast: false matrix: os: - - ubuntu + - ["ubuntu", "ubuntu-20.04"] 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"] - - ["pypy2", "pypy"] - - ["pypy3", "pypy3"] + - ["3.11", "py311"] + - ["pypy-3.9", "pypy3"] - ["3.9", "coverage"] - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os[1] }} + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name name: ${{ matrix.config[1] }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.config[0] }} - name: Pip cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }} @@ -57,7 +55,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/.gitignore b/.gitignore index c724a76..1f321f5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ lib64 log/ parts/ pyvenv.cfg +testing.log var/ diff --git a/.meta.toml b/.meta.toml index 9ec5f7c..f2ca47f 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,14 +2,14 @@ # https://github.com/zopefoundation/meta/tree/master/config/pure-python [meta] template = "pure-python" -commit-id = "fba6d957ba447b6fa369d872e803756bd5176391" +commit-id = "487d9939" [python] with-windows = false with-pypy = true with-future-python = false -with-legacy-python = true with-sphinx-doctests = false +with-macos = false [tox] use-flake8 = true diff --git a/CHANGES.rst b/CHANGES.rst index 1cdf7fa..79e4696 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,10 +2,12 @@ CHANGES ======= -2.3 (unreleased) +3.0 (unreleased) ---------------- -- Nothing changed yet. +- Add support for Python 3.11. + +- Drop support for Python 2.7, 3.5, 3.6. 2.2 (2022-02-11) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..31d95f0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ + +# Contributing to zopefoundation projects + +The projects under the zopefoundation GitHub organization are open source and +welcome contributions in different forms: + +* bug reports +* code improvements and bug fixes +* documentation improvements +* pull request reviews + +For any changes in the repository besides trivial typo fixes you are required +to sign the contributor agreement. See +https://www.zope.dev/developer/becoming-a-committer.html for details. + +Please visit our [Developer +Guidelines](https://www.zope.dev/developer/guidelines.html) if you'd like to +contribute code changes and our [guidelines for reporting +bugs](https://www.zope.dev/developer/reporting-bugs.html) if you want to file a +bug report. diff --git a/MANIFEST.in b/MANIFEST.in index 41f3881..c852823 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python +include *.md include *.rst include *.txt include buildout.cfg diff --git a/setup.cfg b/setup.cfg index 95cd49b..1465440 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 @@ -10,3 +10,14 @@ doctests = 1 ignore = .editorconfig .meta.toml + +[isort] +force_single_line = True +combine_as_imports = True +sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER +known_third_party = six, docutils, pkg_resources, pytz +known_zope = +known_first_party = +default_section = ZOPE +line_length = 79 +lines_after_imports = 2 diff --git a/setup.py b/setup.py index 255a77e..89875fd 100644 --- a/setup.py +++ b/setup.py @@ -14,16 +14,19 @@ """Setup """ import os -from setuptools import setup, find_packages + +from setuptools import find_packages +from setuptools import setup def read(*rnames): - return open(os.path.join(os.path.dirname(__file__), *rnames)).read() + with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: + return f.read() setup( name="z3c.table", - version='2.3.dev0', + version='3.0.dev0', author="Stephan Richter, Roger Ineichen and the Zope Community", author_email="zope-dev@zope.org", description="Modular table rendering implementation for Zope3", @@ -52,15 +55,12 @@ 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", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Natural Language :: English", @@ -73,6 +73,7 @@ def read(*rnames): include_package_data=True, package_dir={"": "src"}, namespace_packages=["z3c"], + python_requires='>=3.7', extras_require=dict( test=[ "zope.container", diff --git a/src/z3c/table/batch.py b/src/z3c/table/batch.py index c3fd0e7..42fe1b5 100644 --- a/src/z3c/table/batch.py +++ b/src/z3c/table/batch.py @@ -11,25 +11,21 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -try: - # Python 2 - from urllib import urlencode -except ImportError: - # Python 3 - from urllib.parse import urlencode +from urllib.parse import urlencode -import zope.interface import zope.i18nmessageid +import zope.interface +from z3c.batching.batch import first_neighbours_last from zope.traversing.browser import absoluteURL from z3c.table import interfaces -from z3c.batching.batch import first_neighbours_last + _ = zope.i18nmessageid.MessageFactory("z3c") @zope.interface.implementer(interfaces.IBatchProvider) -class BatchProvider(object): +class BatchProvider: """Batch content provider. A batch provider is responsible for rendering the batch HTML and not for @@ -69,7 +65,7 @@ class BatchProvider(object): prevBatchSize = 3 nextBatchSize = 3 - batchSpacer = u"..." + batchSpacer = "..." _request_args = ["%(prefix)s-sortOn", "%(prefix)s-sortOrder"] @@ -102,8 +98,8 @@ def renderBatchLink(self, batch, cssClass=None): tableURL = absoluteURL(self.table, self.request) idx = batch.index + 1 css = ' class="%s"' % cssClass - cssClass = cssClass and css or u"" - return '%s' % (tableURL, query, cssClass, idx) + cssClass = cssClass and css or "" + return f'{idx}' def update(self): # 3 is is the placeholder for the first, current and last item. @@ -156,4 +152,4 @@ def render(self): append(self.renderBatchLink(batch, css)) else: append(self.renderBatchLink(batch)) - return u"\n".join(res) + return "\n".join(res) diff --git a/src/z3c/table/column.py b/src/z3c/table/column.py index a02c21f..e44d88c 100644 --- a/src/z3c/table/column.py +++ b/src/z3c/table/column.py @@ -11,26 +11,22 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -""" -$Id:$ -""" __docformat__ = "reStructuredText" -try: - from urllib import urlencode -except ImportError: - from urllib.parse import urlencode - -from z3c.table import interfaces -from zope.dublincore.interfaces import IZopeDublinCore -from zope.security.interfaces import Unauthorized -from zope.traversing import api -from zope.traversing.browser import absoluteURL import html +from urllib.parse import urlencode + import zope.i18n import zope.i18nmessageid import zope.interface import zope.location +from zope.dublincore.interfaces import IZopeDublinCore +from zope.security.interfaces import Unauthorized +from zope.traversing import api +from zope.traversing.browser import absoluteURL + +from z3c.table import interfaces + _ = zope.i18nmessageid.MessageFactory("z3c") @@ -96,7 +92,7 @@ class Column(zope.location.Location): # customize this part if needed colspan = 0 weight = 0 - header = u"" + header = "" cssClasses = {} def __init__(self, context, request, table): @@ -136,7 +132,7 @@ def renderCell(self, item): raise NotImplementedError("Subclass must implement renderCell") def __repr__(self): - return "<%s %r>" % (self.__class__.__name__, self.__name__) + return "<{} {!r}>".format(self.__class__.__name__, self.__name__) @zope.interface.implementer(interfaces.INoneCell) @@ -147,10 +143,10 @@ def getColspan(self, item): return 0 def renderHeadCell(self): - return u"" + return "" def renderCell(self, item): - return u"" + return "" # predefined columns @@ -208,15 +204,16 @@ def update(self): self.selectedItem = items.pop() def renderCell(self, item): - selected = u"" + selected = "" if item == self.selectedItem: selected = ' checked="checked"' - return u'' % ( - "radio-widget", - html.escape(self.getItemKey(item)), - html.escape(self.getItemValue(item)), - selected, - ) + return ( + ''.format( + "radio-widget", + html.escape(self.getItemKey(item)), + html.escape(self.getItemValue(item)), + selected, + )) class CheckBoxColumn(Column): @@ -258,11 +255,11 @@ def update(self): ] def renderCell(self, item): - selected = u"" + selected = "" if item in self.selectedItems: selected = ' checked="checked"' return ( - u'' + '' % ( "checkbox-widget", html.escape(self.getItemKey(item)), @@ -276,7 +273,7 @@ class GetAttrColumn(Column): """Get attribute column.""" attrName = None - defaultValue = u"" + defaultValue = "" def getValue(self, obj): if obj is not None and self.attrName is not None: @@ -291,7 +288,7 @@ class GetItemColumn(Column): """Get value from item index/key column.""" idx = None - defaultValue = u"" + defaultValue = "" def getValue(self, obj): if obj is not None and self.idx is not None: @@ -315,10 +312,10 @@ def renderCell(self, item): class FormatterColumn(Column): """Formatter column.""" - formatterCategory = u"dateTime" - formatterLength = u"medium" + formatterCategory = "dateTime" + formatterLength = "medium" formatterName = None - formatterCalendar = u"gregorian" + formatterCalendar = "gregorian" def getFormatter(self): return self.request.locale.dates.getFormatter( @@ -346,8 +343,8 @@ class CreatedColumn(FormatterColumn, GetAttrColumn): header = _("Created") weight = 100 - formatterCategory = u"dateTime" - formatterLength = u"short" + formatterCategory = "dateTime" + formatterLength = "short" attrName = "created" def renderCell(self, item): @@ -365,8 +362,8 @@ class ModifiedColumn(FormatterColumn, GetAttrColumn): header = _("Modified") weight = 110 - formatterCategory = u"dateTime" - formatterLength = u"short" + formatterCategory = "dateTime" + formatterLength = "short" attrName = "modified" def renderCell(self, item): @@ -391,7 +388,8 @@ class LinkColumn(Column): def getLinkURL(self, item): """Setup link url.""" if self.linkName is not None: - return "%s/%s" % (absoluteURL(item, self.request), self.linkName) + return "{}/{}".format( + absoluteURL(item, self.request), self.linkName) return absoluteURL(item, self.request) def getLinkCSS(self, item): @@ -418,7 +416,7 @@ def getLinkContent(self, item): def renderCell(self, item): # setup a tag - return '%s' % ( + return '{}'.format( html.escape(self.getLinkURL(item)), self.getLinkTarget(item), self.getLinkCSS(item), @@ -430,9 +428,9 @@ def renderCell(self, item): class EMailColumn(LinkColumn, GetAttrColumn): "Column to display mailto links." - header = _(u"E-Mail") + header = _("E-Mail") attrName = None # attribute name which contains the mail address - defaultValue = u"" # value which is rendered when no value is found + defaultValue = "" # value which is rendered when no value is found linkContent = None def getLinkURL(self, item): @@ -447,7 +445,7 @@ def renderCell(self, item): value = self.getValue(item) if value is self.defaultValue or value is None: return self.defaultValue - return super(EMailColumn, self).renderCell(item) + return super().renderCell(item) def ensureList(item): @@ -463,7 +461,7 @@ class SelectedItemColumn(LinkColumn): @property def viewURL(self): - return "%s/%s" % ( + return "{}/{}".format( absoluteURL(self.context, self.request), self.table.__name__, ) @@ -484,7 +482,7 @@ def getLinkContent(self, item): def getLinkURL(self, item): """Setup link url.""" - return "%s?%s" % ( + return "{}?{}".format( self.viewURL, urlencode({self.getItemKey(item): self.getItemValue(item)}), ) diff --git a/src/z3c/table/header.py b/src/z3c/table/header.py index 5c7cad5..55dab4d 100644 --- a/src/z3c/table/header.py +++ b/src/z3c/table/header.py @@ -11,27 +11,20 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -""" -$Id$ -""" __docformat__ = "reStructuredText" -from z3c.table.i18n import _ +from urllib.parse import urlencode -try: - # Python 2 - from urllib import urlencode -except ImportError: - # Python 3 - from urllib.parse import urlencode -import z3c.table.interfaces import zope.i18n import zope.interface +import z3c.table.interfaces +from z3c.table.i18n import _ + @zope.interface.implementer(z3c.table.interfaces.IColumnHeader) -class ColumnHeader(object): +class ColumnHeader: """ColumnHeader renderer provider""" _request_args = [] @@ -101,7 +94,7 @@ def render(self): ) queryString = "?%s" % (urlencode(sorted(args.items()))) - return '%s' % ( + return '{}'.format( queryString, zope.i18n.translate(_("Sort"), context=self.request), zope.i18n.translate(self.column.header, context=self.request), diff --git a/src/z3c/table/interfaces.py b/src/z3c/table/interfaces.py index 6e313a7..f6a4473 100644 --- a/src/z3c/table/interfaces.py +++ b/src/z3c/table/interfaces.py @@ -11,11 +11,12 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -from z3c.table.i18n import _ import zope.contentprovider.interfaces import zope.interface import zope.schema +from z3c.table.i18n import _ + class IValues(zope.interface.Interface): """Table value adapter.""" @@ -27,8 +28,8 @@ class ITable(zope.contentprovider.interfaces.IContentProvider): """Table provider""" columnCounter = zope.schema.Int( - title=_(u"Column counter"), - description=_(u"Column counter"), + title=_("Column counter"), + description=_("Column counter"), default=0 ) @@ -58,66 +59,66 @@ class ITable(zope.contentprovider.interfaces.IContentProvider): # additional (row) css cssClassEven = zope.schema.TextLine( - title=u"Even css row class", - description=(u"CSS class for even rows."), - default=u"even", + title="Even css row class", + description=("CSS class for even rows."), + default="even", required=False, ) cssClassOdd = zope.schema.TextLine( - title=u"Odd css row class", - description=(u"CSS class for odd rows."), - default=u"odd", + title="Odd css row class", + description=("CSS class for odd rows."), + default="odd", required=False, ) cssClassSelected = zope.schema.TextLine( - title=u"Selected css row class", - description=(u"CSS class for selected rows."), - default=u"selected", + title="Selected css row class", + description=("CSS class for selected rows."), + default="selected", required=False, ) # sort attributes sortOn = zope.schema.Int( - title=_(u"Sort on table index"), - description=_(u"Sort on table index"), + title=_("Sort on table index"), + description=_("Sort on table index"), default=0, ) sortOrder = zope.schema.TextLine( - title=_(u"Sort order"), - description=_(u"Row sort order"), - default=u"ascending", + title=_("Sort order"), + description=_("Row sort order"), + default="ascending", ) reverseSortOrderNames = zope.schema.List( - title=u"Selected css row class", - description=(u"CSS class for selected rows."), + title="Selected css row class", + description=("CSS class for selected rows."), value_type=zope.schema.TextLine( - title=_(u"Reverse sort order name"), - description=_(u"Reverse sort order name"), + title=_("Reverse sort order name"), + description=_("Reverse sort order name"), ), - default=[u"descending", u"reverse", u"down"], + default=["descending", "reverse", "down"], required=False, ) # batch attributes batchStart = zope.schema.Int( - title=_(u"Batch start index"), - description=_(u"Index the batch starts with"), + title=_("Batch start index"), + description=_("Index the batch starts with"), default=0, ) batchSize = zope.schema.Int( - title=_(u"Batch size"), - description=_(u"The batch size"), + title=_("Batch size"), + description=_("The batch size"), default=50 ) startBatchingAt = zope.schema.Int( - title=_(u"Batch start size"), - description=_(u"The minimal size the batch starts to get used"), + title=_("Batch start size"), + description=_("The minimal size the batch starts to get used"), default=50, ) @@ -206,28 +207,28 @@ class IColumn(zope.interface.Interface): """Column provider""" id = zope.schema.TextLine( - title=_(u"Id"), - description=_(u"The column id"), + title=_("Id"), + description=_("The column id"), default=None ) # customize this part if needed colspan = zope.schema.Int( - title=_(u"Colspan"), - description=_(u"The colspan value"), + title=_("Colspan"), + description=_("The colspan value"), default=0 ) weight = zope.schema.Int( - title=_(u"Weight"), - description=_(u"The column weight"), + title=_("Weight"), + description=_("The column weight"), default=0 ) header = zope.schema.TextLine( - title=_(u"Header name"), - description=_(u"The header name"), - default=u"" + title=_("Header name"), + description=_("The header name"), + default="" ) cssClasses = zope.interface.Attribute( diff --git a/src/z3c/table/table.py b/src/z3c/table/table.py index bfdef95..5a65a86 100644 --- a/src/z3c/table/table.py +++ b/src/z3c/table/table.py @@ -13,14 +13,14 @@ ############################################################################## from xml.sax.saxutils import quoteattr -import zope.interface import zope.component +import zope.interface import zope.location - -from z3c.batching.interfaces import IBatch from z3c.batching.batch import Batch -from z3c.table import interfaces +from z3c.batching.interfaces import IBatch + from z3c.table import column +from z3c.table import interfaces def getWeight(column): @@ -58,16 +58,16 @@ class Table(zope.location.Location): # css classes cssClasses = {} # additional (row) css - cssClassEven = u"" - cssClassOdd = u"" - cssClassSelected = u"" + cssClassEven = "" + cssClassOdd = "" + cssClassSelected = "" # css to show sorting, set to None to turn off cssClassSortedOn = "sorted-on" # sort attributes sortOn = 0 - sortOrder = u"ascending" - reverseSortOrderNames = [u"descending", u"reverse", u"down"] + sortOrder = "ascending" + reverseSortOrderNames = ["descending", "reverse", "down"] # batch attributes batchProviderName = "batch" @@ -128,7 +128,7 @@ def getCSSClass(self, element, cssClass=None): """Add CSS class based on HTML tag, make a `class=` attribute""" klass = self.cssClasses.get(element) if klass and cssClass: - klass = "%s %s" % (cssClass, klass) + klass = "{} {}".format(cssClass, klass) elif cssClass: klass = cssClass return " class=%s" % quoteattr(klass) if klass else "" @@ -154,7 +154,7 @@ def orderColumns(self): for col in self.columns: self.columnByName[col.__name__] = col idx = self.columnCounter - col.id = "%s-%s-%s" % (self.prefix, col.__name__, idx) + col.id = "{}-{}-{}".format(self.prefix, col.__name__, idx) self.columnIndexById[col.id] = idx self.columnCounter += 1 @@ -247,7 +247,7 @@ def isSelectedRow(self, row): def renderBatch(self): if self.batchProvider is None: - return u"" + return "" return self.batchProvider.render() def renderTable(self): @@ -255,29 +255,29 @@ def renderTable(self): cssClass = self.getCSSClass("table") head = self.renderHead() body = self.renderBody() - return "%s%s\n" % (cssClass, head, body) - return u"" + return "{}{}\n".format(cssClass, head, body) + return "" def renderHead(self): cssClass = self.getCSSClass("thead") rStr = self.renderHeadRow() - return "\n %s\n " % (cssClass, rStr) + return "\n {}\n ".format(cssClass, rStr) def renderHeadRow(self): cssClass = self.getCSSClass("tr") cells = [self.renderHeadCell(col) for col in self.columns] - return u"\n %s\n " % (cssClass, u"".join(cells)) + return "\n {}\n ".format(cssClass, "".join(cells)) def renderHeadCell(self, column): cssClass = column.cssClasses.get("th") cssClass = self.getCSSSortClass(column, cssClass) cssClass = self.getCSSClass("th", cssClass) - return u"\n %s" % (cssClass, column.renderHeadCell()) + return f"\n {column.renderHeadCell()}" def renderBody(self): cssClass = self.getCSSClass("tbody") rStr = self.renderRows() - return "\n %s\n " % (cssClass, rStr) + return f"\n {rStr}\n " def renderRows(self): counter = 0 @@ -287,29 +287,29 @@ def renderRows(self): for row in self.rows: append(self.renderRow(row, cssClasses[counter % 2])) counter += 1 - return u"".join(rows) + return "".join(rows) def renderRow(self, row, cssClass=None): isSelected = self.isSelectedRow(row) if isSelected and self.cssClassSelected and cssClass: - cssClass = "%s %s" % (self.cssClassSelected, cssClass) + cssClass = "{} {}".format(self.cssClassSelected, cssClass) elif isSelected and self.cssClassSelected: cssClass = self.cssClassSelected cssClass = self.getCSSClass("tr", cssClass) cells = [ self.renderCell(item, col, colspan) for item, col, colspan in row ] - return u"\n %s\n " % (cssClass, u"".join(cells)) + return "\n {}\n ".format(cssClass, "".join(cells)) def renderCell(self, item, column, colspan=0): if interfaces.INoneCell.providedBy(column): - return u"" + return "" cssClass = column.cssClasses.get("td") cssClass = self.getCSSHighlightClass(column, item, cssClass) cssClass = self.getCSSSortClass(column, cssClass) cssClass = self.getCSSClass("td", cssClass) colspanStr = ' colspan="%s"' % colspan if colspan else "" - return u"\n %s" % ( + return "\n {}".format( cssClass, colspanStr, column.renderCell(item), @@ -353,7 +353,7 @@ def render(self): return self.renderTable() def __repr__(self): - return "<%s %r>" % (self.__class__.__name__, self.__name__) + return "<{} {!r}>".format(self.__class__.__name__, self.__name__) @zope.interface.implementer(interfaces.ISequenceTable) diff --git a/src/z3c/table/testing.py b/src/z3c/table/testing.py index 132cfe9..cb0ff61 100644 --- a/src/z3c/table/testing.py +++ b/src/z3c/table/testing.py @@ -17,30 +17,35 @@ __docformat__ = "reStructuredText" import datetime -import zope.interface + import zope.component +import zope.interface import zope.traversing.testing -from zope.container import btree, contained, ordered +from zope.container import btree +from zope.container import contained +from zope.container import ordered from zope.dublincore.interfaces import IZopeDublinCore from zope.security import checker -from zope.site.testing import siteSetUp, siteTearDown +from zope.site.testing import siteSetUp +from zope.site.testing import siteTearDown -from z3c.table import column, table import z3c.table.value +from z3c.table import column +from z3c.table import table class TitleColumn(column.Column): weight = 10 - header = u"Title" + header = "Title" def renderCell(self, item): - return u"Title: %s" % item.title + return "Title: %s" % item.title class NumberColumn(column.Column): - header = u"Number" + header = "Number" weight = 20 def getSortKey(self, item): @@ -53,13 +58,13 @@ def renderCell(self, item): class Container(btree.BTreeContainer): """Sample container.""" - __name__ = u"container" + __name__ = "container" class OrderedContainer(ordered.OrderedContainer): """Sample container.""" - __name__ = u"container" + __name__ = "container" class Content(contained.Contained): @@ -76,28 +81,28 @@ def setUpColumns(self): column.addColumn( self, TitleColumn, - u"title", + "title", cellRenderer=cellRenderer, headCellRenderer=headCellRenderer, weight=1, ), column.addColumn( - self, NumberColumn, name=u"number", weight=2, header=u"Number" + self, NumberColumn, name="number", weight=2, header="Number" ), ] def headCellRenderer(): - return u"My items" + return "My items" def cellRenderer(item): - return u"%s item" % item.title + return "%s item" % item.title @zope.interface.implementer(IZopeDublinCore) @zope.component.adapter(zope.interface.Interface) -class DublinCoreAdapterStub(object): +class DublinCoreAdapterStub: """Dublin core adapter stub.""" __Security_checker__ = checker.Checker( diff --git a/src/z3c/table/tests.py b/src/z3c/table/tests.py index 6e432bc..1c6bfae 100644 --- a/src/z3c/table/tests.py +++ b/src/z3c/table/tests.py @@ -11,26 +11,26 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## +import doctest import re import unittest -import doctest -import zope.traversing.testing -from zope.publisher.browser import TestRequest +import zope.traversing.testing +from z3c.batching.batch import Batch from zope.interface.verify import verifyObject - -from zope.site.testing import siteSetUp, siteTearDown +from zope.publisher.browser import TestRequest +from zope.site.testing import siteSetUp +from zope.site.testing import siteTearDown from zope.testing.renormalizing import RENormalizing -from z3c.batching.batch import Batch -from z3c.table import testing +from z3c.table import batch +from z3c.table import column from z3c.table import interfaces from z3c.table import table -from z3c.table import column -from z3c.table import batch +from z3c.table import testing -class FakeContainer(object): +class FakeContainer: def values(self): pass @@ -39,7 +39,7 @@ def values(self): marker_kws = object() -class InterfaceBaseTest(object): +class InterfaceBaseTest: """Base test for IContainer including interface test.""" iface = None @@ -124,11 +124,11 @@ def test_renderHeadCell(self): context = Mock() tbl = Mock() col = column.Column(context, TestRequest(), tbl) - col.header = u"FooBar" - self.assertEqual(col.renderHeadCell(), u"FooBar") + col.header = "FooBar" + self.assertEqual(col.renderHeadCell(), "FooBar") - col.header = u'14" ' - self.assertEqual(col.renderHeadCell(), u"14" <monitor>") + col.header = '14" ' + self.assertEqual(col.renderHeadCell(), "14" <monitor>") class TestNoneCell(InterfaceBaseTest, unittest.TestCase): @@ -161,10 +161,10 @@ def test_renderCell(self): tbl = Mock() tbl.selectedItems = [] col = column.NameColumn(context, TestRequest(), tbl) - self.assertEqual(col.renderCell(item), u"ItemName") + self.assertEqual(col.renderCell(item), "ItemName") item.__name__ = '14" ' - self.assertEqual(col.renderCell(item), u"14" <monitor>") + self.assertEqual(col.renderCell(item), "14" <monitor>") class TestRadioColumn(InterfaceBaseTest, unittest.TestCase): @@ -189,17 +189,17 @@ def test_renderCell(self): out = col.renderCell(item) self.assertEqual( out, - u'', + '', ) tbl.selectedItems = [item] out = col.renderCell(item) self.assertEqual( out, - u'', + '', ) # now let's see how to-be-encoded item __name__ works @@ -208,9 +208,9 @@ def test_renderCell(self): out = col.renderCell(item) self.assertEqual( out, - u'', + '', ) @@ -236,17 +236,17 @@ def test_renderCell(self): out = col.renderCell(item) self.assertEqual( out, - u'', + '', ) tbl.selectedItems = [item] out = col.renderCell(item) self.assertEqual( out, - u'', + '', ) # now let's see how to-be-encoded item __name__ works @@ -255,9 +255,9 @@ def test_renderCell(self): out = col.renderCell(item) self.assertEqual( out, - u'', + '', ) @@ -287,19 +287,19 @@ def test_getLinkContent(self): context = Mock() tbl = Mock() col = column.LinkColumn(context, TestRequest(), tbl) - self.assertEqual(col.getLinkContent(item), u"ItemName") + self.assertEqual(col.getLinkContent(item), "ItemName") item.__name__ = '14" ' - self.assertEqual(col.getLinkContent(item), u'14" ') + self.assertEqual(col.getLinkContent(item), '14" ') col.linkContent = "Solo" - self.assertEqual(col.getLinkContent(item), u"Solo") + self.assertEqual(col.getLinkContent(item), "Solo") col.linkContent = '32" ' - self.assertEqual(col.getLinkContent(item), u'32" ') + self.assertEqual(col.getLinkContent(item), '32" ') def test_getLinkURL(self): - item = testing.Content(u"foobar", 42) + item = testing.Content("foobar", 42) self.container["fourty-two"] = item request = TestRequest() tbl = table.Table(None, request) @@ -308,13 +308,13 @@ def test_getLinkURL(self): col.getLinkURL(item), "http://127.0.0.1/container/fourty-two" ) - col.linkName = u'index.html' + col.linkName = 'index.html' self.assertEqual( col.getLinkURL(item), "http://127.0.0.1/container/fourty-two/index.html" ) - item = testing.Content(u"another bar", 2) + item = testing.Content("another bar", 2) self.container['14" '] = item self.assertEqual( col.getLinkURL(item), @@ -322,7 +322,7 @@ def test_getLinkURL(self): ) def test_renderCell(self): - item = testing.Content(u"foobar", 42) + item = testing.Content("foobar", 42) self.container["fourty-two"] = item request = TestRequest() tbl = table.Table(None, request) @@ -332,7 +332,7 @@ def test_renderCell(self): 'fourty-two' ) - item = testing.Content(u"another bar", 2) + item = testing.Content("another bar", 2) self.container['14" '] = item self.assertEqual( col.renderCell(item), @@ -364,24 +364,24 @@ def getTestPos(self): def test_getLinkContent(self): item = Mock() item.__name__ = "ItemName" - item.title = u"fooTitle" + item.title = "fooTitle" context = Mock() tbl = Mock() col = column.EMailColumn(context, TestRequest(), tbl) col.attrName = "title" - self.assertEqual(col.getLinkContent(item), u"fooTitle") + self.assertEqual(col.getLinkContent(item), "fooTitle") item.title = '14" ' - self.assertEqual(col.getLinkContent(item), u'14" ') + self.assertEqual(col.getLinkContent(item), '14" ') col.linkContent = "Solo" - self.assertEqual(col.getLinkContent(item), u"Solo") + self.assertEqual(col.getLinkContent(item), "Solo") col.linkContent = '32" ' - self.assertEqual(col.getLinkContent(item), u'32" ') + self.assertEqual(col.getLinkContent(item), '32" ') def test_renderCell(self): - item = testing.Content(u"", 42) + item = testing.Content("", 42) self.container["fourty-two"] = item request = TestRequest() tbl = table.Table(None, request) @@ -389,14 +389,14 @@ def test_renderCell(self): col.attrName = "title" self.assertEqual(col.renderCell(item), "") - item.title = u"foo@mail.com" + item.title = "foo@mail.com" self.assertEqual( col.renderCell(item), 'foo@mail.com', ) # according to RFC 5321 an email addr can contain <>& - item = testing.Content(u"John Doe ", 2) + item = testing.Content("John Doe ", 2) self.container["john"] = item self.assertEqual( col.renderCell(item), @@ -419,7 +419,7 @@ def getTestPos(self): return ({}, TestRequest(), t) -class Mock(object): +class Mock: pass diff --git a/src/z3c/table/value.py b/src/z3c/table/value.py index 15ec677..4806f51 100644 --- a/src/z3c/table/value.py +++ b/src/z3c/table/value.py @@ -23,7 +23,7 @@ @zope.interface.implementer(interfaces.IValues) -class ValuesMixin(object): +class ValuesMixin: """Mixin for different value adapters.""" def __init__(self, context, request, table): diff --git a/tox.ini b/tox.ini index 0809b19..9443e4b 100644 --- a/tox.ini +++ b/tox.ini @@ -4,14 +4,11 @@ minversion = 3.18 envlist = lint - py27 - py35 - py36 py37 py38 py39 py310 - pypy + py311 pypy3 coverage @@ -26,15 +23,26 @@ extras = [testenv:lint] basepython = python3 skip_install = true +commands = + isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py + flake8 src setup.py + check-manifest + check-python-versions deps = - flake8 check-manifest check-python-versions >= 0.19.1 wheel + flake8 + isort + +[testenv:isort-apply] +basepython = python3 +skip_install = true +commands_pre = +deps = + isort commands = - flake8 src setup.py - check-manifest - check-python-versions + isort {toxinidir}/src {toxinidir}/setup.py [] [testenv:coverage] basepython = python3 @@ -42,16 +50,14 @@ allowlist_externals = mkdir deps = coverage - coverage-python-version commands = mkdir -p {toxinidir}/parts/htmlcov coverage run -m zope.testrunner --test-path=src {posargs:-vc} - coverage html - coverage report -m --fail-under=96 + coverage html --ignore-errors + coverage report --ignore-errors --show-missing --fail-under=96 [coverage:run] branch = True -plugins = coverage_python_version source = z3c.table [coverage:report]