From 0a0a05c4ce7dfb87a06b0649882a999fd43d7a4f Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Thu, 24 Oct 2024 09:17:50 +0200 Subject: [PATCH] Configuring for pure-python --- .github/workflows/pre-commit.yml | 33 ++++++ .github/workflows/tests.yml | 28 +++-- .meta.toml | 2 +- .pre-commit-config.yaml | 28 +++++ MANIFEST.in | 1 + docs/conf.py | 121 ++++++++++---------- grokwiki/setup.py | 6 +- grokwiki/src/grokwiki/page.py | 8 +- pyproject.toml | 22 ++++ setup.cfg | 2 - setup.py | 10 +- src/grok/components.py | 1 + src/grok/ftests/test_grok_functional.py | 10 +- src/grok/testing.py | 2 +- src/grok/tests/container/container_model.py | 1 + src/grok/tests/security/permissions.py | 4 +- src/grok/tests/test_grok.py | 4 +- tox.ini | 56 +++------ 18 files changed, 203 insertions(+), 136 deletions(-) create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000..0f06e163 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,33 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/pure-python +name: pre-commit + +on: + pull_request: + push: + branches: + - master + # Allow to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + FORCE_COLOR: 1 + +jobs: + pre-commit: + name: linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --all-files --show-diff-on-failure + env: + PRE_COMMIT_COLOR: always + - uses: pre-commit-ci/lite-action@v1.0.2 + if: always() + with: + msg: Apply pre-commit code formatting diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8b3fccab..50629dd6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,20 +17,19 @@ jobs: fail-fast: false matrix: os: - - ["ubuntu", "ubuntu-20.04"] + - ["ubuntu", "ubuntu-latest"] config: # [Python version, tox env] - - ["3.9", "release-check"] - - ["3.9", "lint"] - - ["3.7", "py37"] - - ["3.8", "py38"] - - ["3.9", "py39"] - - ["3.10", "py310"] - - ["3.11", "py311"] - - ["3.12", "py312"] + - ["3.11", "release-check"] + - ["3.8", "py38"] + - ["3.9", "py39"] + - ["3.10", "py310"] + - ["3.11", "py311"] + - ["3.12", "py312"] + - ["3.13", "py313"] - ["pypy-3.10", "pypy3"] - - ["3.9", "docs"] - - ["3.9", "coverage"] + - ["3.11", "docs"] + - ["3.11", "coverage"] 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 @@ -41,8 +40,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.config[0] }} + allow-prereleases: true - name: Pip cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }} @@ -54,7 +54,11 @@ jobs: python -m pip install --upgrade pip pip install tox - name: Test + if: ${{ !startsWith(runner.os, 'Mac') }} run: tox -e ${{ matrix.config[1] }} + - name: Test (macOS) + if: ${{ startsWith(runner.os, 'Mac') }} + run: tox -e ${{ matrix.config[1] }}-universal2 - name: Coverage if: matrix.config[1] == 'coverage' run: | diff --git a/.meta.toml b/.meta.toml index 97befccf..be089926 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,7 +2,7 @@ # https://github.com/zopefoundation/meta/tree/master/config/pure-python [meta] template = "pure-python" -commit-id = "9e626d3b" +commit-id = "f317618e" [python] with-windows = false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..7ab398cc --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/pure-python +minimum_pre_commit_version: '3.6' +repos: + - repo: https://github.com/pycqa/isort + rev: "5.13.2" + hooks: + - id: isort + - repo: https://github.com/hhatto/autopep8 + rev: "v2.3.1" + hooks: + - id: autopep8 + args: [--in-place, --aggressive, --aggressive] + - repo: https://github.com/asottile/pyupgrade + rev: v3.17.0 + hooks: + - id: pyupgrade + args: [--py38-plus] + - repo: https://github.com/isidentical/teyit + rev: 0.4.3 + hooks: + - id: teyit + - repo: https://github.com/PyCQA/flake8 + rev: "7.1.1" + hooks: + - id: flake8 + additional_dependencies: + - flake8-debugger == 4.1.2 diff --git a/MANIFEST.in b/MANIFEST.in index 917fddaa..769bb422 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,7 @@ include *.rst include *.txt include buildout.cfg include tox.ini +include .pre-commit-config.yaml recursive-include docs *.py recursive-include docs *.rst diff --git a/docs/conf.py b/docs/conf.py index d493d0ea..a3f38b03 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,30 +1,28 @@ -# -*- coding: utf-8 -*- # -# grok documentation build configuration file, created by -# sphinx-quickstart on Thu Jan 13 10:38:13 2011. +# grok documentation build configuration file, created by sphinx-quickstart on +# Thu Jan 13 10:38:13 2011. # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its containing +# dir. # # Note that not all possible configuration values are present in this # autogenerated file. # -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os +# All configuration values have a default; values that are commented out serve +# to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) -# -- General configuration ----------------------------------------------------- +# -- General configuration ----------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.coverage', @@ -32,7 +30,7 @@ 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode', - ] +] # Order autodoc generated docs in source code order. autodoc_member_order = 'alphabetical' @@ -44,14 +42,14 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. -project = u'grok' -copyright = u'2006-2011, The Zope Foundation and Grok developers and community' +project = 'grok' +copyright = '2006-2011, The Zope Foundation and Grok developers and community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -66,40 +64,41 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] -# -- Options for HTML output --------------------------------------------------- +# -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. @@ -108,26 +107,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -136,95 +135,95 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. # htmlhelp_basename = 'grokdoc' -# -- Options for LaTeX output -------------------------------------------------- +# -- Options for LaTeX output -------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). +# (source start file, target name, title, author, documentclass [howto/manual]) latex_documents = [ - ('index', 'grok.tex', u'grok Documentation', - u'The Grok developers and community', 'manual'), + ('index', 'grok.tex', 'grok Documentation', + 'The Grok developers and community', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True -# -- Options for manual page output -------------------------------------------- +# -- Options for manual page output -------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'grok', u'grok Documentation', - [u'The Grok developers and community'], 1) + ('index', 'grok', 'grok Documentation', + ['The Grok developers and community'], 1) ] diff --git a/grokwiki/setup.py b/grokwiki/setup.py index 799d8d0d..b40b0f9a 100644 --- a/grokwiki/setup.py +++ b/grokwiki/setup.py @@ -1,4 +1,6 @@ -from setuptools import setup, find_packages +from setuptools import find_packages +from setuptools import setup + setup( name='grokwiki', @@ -25,5 +27,5 @@ [console_scripts] interactive_debug_prompt = grokcore.startup.startup:interactive_debug_prompt [paste.app_factory] - main = grokcore.startup:application_factory""", + main = grokcore.startup:application_factory""", # noqa E501 line too long ) diff --git a/grokwiki/src/grokwiki/page.py b/grokwiki/src/grokwiki/page.py index 72e83e41..30853c63 100644 --- a/grokwiki/src/grokwiki/page.py +++ b/grokwiki/src/grokwiki/page.py @@ -14,19 +14,21 @@ """The grok demo wiki """ import re -import grok from z3c.flashmessage.interfaces import IMessageReceiver from zope import component -LINK_PATTERN = re.compile('\[\[(.*?)\]\]') +import grok + + +LINK_PATTERN = re.compile('\\[\\[(.*?)\\]\\]') find_wiki_links = LINK_PATTERN.findall class WikiPage(grok.Model): def __init__(self): - self.text = u"GROK EMPTY WIKI PAGE. FILL!" + self.text = "GROK EMPTY WIKI PAGE. FILL!" def update(self, text): links = find_wiki_links(text) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..bf881d50 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +# +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/pure-python + +[build-system] +requires = ["setuptools < 74"] +build-backend = "setuptools.build_meta" + +[tool.coverage.run] +branch = true +source = ["grok"] +omit = ["src/grok/tests/*/*_fixture.py"] + +[tool.coverage.report] +fail_under = 91 +precision = 2 +ignore_errors = true +show_missing = true +exclude_lines = ["pragma: no cover", "pragma: nocover", "except ImportError:", "raise NotImplementedError", "if __name__ == '__main__':", "self.fail", "raise AssertionError", "raise unittest.Skip"] + +[tool.coverage.html] +directory = "parts/htmlcov" diff --git a/setup.cfg b/setup.cfg index 4e648d9f..8a449c77 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,5 @@ # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python -[bdist_wheel] -universal = 0 [flake8] doctests = 1 diff --git a/setup.py b/setup.py index 7ce0a8b4..4d8c45fe 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*rnames): read('README.rst') + '\n' + read('CHANGES.rst') - ) +) tests_require = [ 'zope.app.wsgi[test]', @@ -21,7 +21,7 @@ def read(*rnames): 'zope.testbrowser', 'zope.testing', 'zope.testrunner', - ] +] setup( name='grok', @@ -43,12 +43,12 @@ def read(*rnames): 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope :: 3', 'Programming Language :: Python :: 3', - '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 :: 3.12', + 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], @@ -56,7 +56,7 @@ def read(*rnames): package_dir={'': 'src'}, include_package_data=True, zip_safe=False, - python_requires='>=3.7', + python_requires='>=3.8', install_requires=[ 'grokcore.annotation >= 1.6', 'grokcore.catalog >= 2.1', @@ -107,7 +107,7 @@ def read(*rnames): 'zope.securitypolicy', 'zope.site', 'zope.traversing', - ], + ], extras_require={ 'test': tests_require, 'docs': ['Sphinx'] diff --git a/src/grok/components.py b/src/grok/components.py index 079448ef..6e35eca1 100644 --- a/src/grok/components.py +++ b/src/grok/components.py @@ -282,6 +282,7 @@ class Page(ViewSupportMixin, grokcore.layout.Page): class LayoutAwareFormPage(grokcore.layout.components.LayoutAware): """A mixin to make form aware of layouts. """ + def __call__(self): """Calls update and returns the layout template which calls render. """ diff --git a/src/grok/ftests/test_grok_functional.py b/src/grok/ftests/test_grok_functional.py index d47bcfde..3d352558 100644 --- a/src/grok/ftests/test_grok_functional.py +++ b/src/grok/ftests/test_grok_functional.py @@ -30,9 +30,9 @@ def http_call(method, path, data=None, **kw): if path.startswith('http://localhost'): path = path[len('http://localhost'):] - request_string = '{} {} HTTP/1.1\n'.format(method, path) + request_string = f'{method} {path} HTTP/1.1\n' for key, value in kw.items(): - request_string += '{}: {}\n'.format(key, value) + request_string += f'{key}: {value}\n' if data is not None: request_string += '\r\n' request_string += data @@ -47,7 +47,7 @@ def suiteFromPackage(name): continue if filename == '__init__.py': continue - dottedname = 'grok.ftests.{}.{}'.format(name, filename[:-3]) + dottedname = f'grok.ftests.{name}.{filename[:-3]}' test = doctest.DocTestSuite( dottedname, extraglobs=dict( @@ -60,7 +60,7 @@ def suiteFromPackage(name): doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE + doctest.REPORT_NDIFF) - ) + ) test.layer = layer suite.addTest(test) return suite @@ -80,6 +80,6 @@ def test_suite(): 'traversal', 'url', 'viewlet', - ]: + ]: suite.addTest(suiteFromPackage(name)) return suite diff --git a/src/grok/testing.py b/src/grok/testing.py index e7a517b4..b424f76c 100644 --- a/src/grok/testing.py +++ b/src/grok/testing.py @@ -74,4 +74,4 @@ def warn(message, category=None, stacklevel=1): category.__name__, message, line.strip(), - )) + )) diff --git a/src/grok/tests/container/container_model.py b/src/grok/tests/container/container_model.py index 4c41440a..69733537 100644 --- a/src/grok/tests/container/container_model.py +++ b/src/grok/tests/container/container_model.py @@ -22,5 +22,6 @@ class BoneBag(grok.Container): class Index(grok.View): """A simple view to test whether BoneBag is really registered as a model. """ + def render(self): return "Hello world" diff --git a/src/grok/tests/security/permissions.py b/src/grok/tests/security/permissions.py index bbb961ae..8e776155 100644 --- a/src/grok/tests/security/permissions.py +++ b/src/grok/tests/security/permissions.py @@ -24,7 +24,7 @@ class RoleComprisingTwoPermissionsByName(grok.Role): grok.permissions( 'first permission', 'second permission' - ) + ) class RoleComprisingTwoPermissionsByClass(grok.Role): @@ -32,4 +32,4 @@ class RoleComprisingTwoPermissionsByClass(grok.Role): grok.permissions( FirstPermission, SecondPermission - ) + ) diff --git a/src/grok/tests/test_grok.py b/src/grok/tests/test_grok.py index 4f71ebec..f53742c5 100644 --- a/src/grok/tests/test_grok.py +++ b/src/grok/tests/test_grok.py @@ -35,7 +35,7 @@ def suiteFromPackage(name): continue if filename == '__init__.py': continue - dottedname = 'grok.tests.{}.{}'.format(name, filename[:-3]) + dottedname = f'grok.tests.{name}.{filename[:-3]}' test = doctest.DocTestSuite( dottedname, optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE) @@ -59,6 +59,6 @@ def test_suite(): 'utility', 'viewlet', 'zcml' - ]: + ]: suite.addTest(suiteFromPackage(name)) return suite diff --git a/tox.ini b/tox.ini index d0f41d3a..670b255a 100644 --- a/tox.ini +++ b/tox.ini @@ -5,12 +5,12 @@ minversion = 3.18 envlist = release-check lint - py37 py38 py39 py310 py311 py312 + py313 pypy3 docs coverage @@ -20,18 +20,23 @@ usedevelop = true package = wheel wheel_build_env = .pkg deps = -setenv = - py312: VIRTUALENV_PIP=23.1.2 - py312: PIP_REQUIRE_VIRTUALENV=0 + setuptools < 74 commands = zope-testrunner --tests-pattern ^f?tests$ --test-path=src {posargs:-cv} extras = test + +[testenv:setuptools-latest] +basepython = python3 +deps = + git+https://github.com/pypa/setuptools.git\#egg=setuptools + [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 skip_install = true deps = + setuptools < 74 twine build check-manifest @@ -40,28 +45,19 @@ deps = commands_pre = commands = check-manifest - check-python-versions + check-python-versions --only setup.py,tox.ini,.github/workflows/tests.yml python -m build --sdist --no-isolation twine check dist/* [testenv:lint] +description = This env runs all linters configured in .pre-commit-config.yaml basepython = python3 skip_install = true deps = - isort - flake8 -commands = - isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py - flake8 src setup.py - -[testenv:isort-apply] -basepython = python3 -skip_install = true + pre-commit commands_pre = -deps = - isort commands = - isort {toxinidir}/src {toxinidir}/setup.py [] + pre-commit run --all-files --show-diff-on-failure [testenv:docs] basepython = python3 @@ -77,29 +73,9 @@ basepython = python3 allowlist_externals = mkdir deps = - coverage + coverage[toml] commands = mkdir -p {toxinidir}/parts/htmlcov coverage run -m zope.testrunner --tests-pattern ^f?tests$ --test-path=src [] - coverage html --ignore-errors - coverage report --ignore-errors --show-missing --fail-under=91 - -[coverage:run] -branch = True -source = grok -omit = - src/grok/tests/*/*_fixture.py - -[coverage:report] -precision = 2 -exclude_lines = - pragma: no cover - pragma: nocover - except ImportError: - raise NotImplementedError - if __name__ == '__main__': - self.fail - raise AssertionError - -[coverage:html] -directory = parts/htmlcov + coverage html + coverage report