diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4215e22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.coverage +.project +.pydevproject +.Python +.vscode +*.egg-info +*.pyc +*.swo +*.swp +build/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7ef2857 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-merge-conflict + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: mixed-line-ending +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.1 + hooks: + - id: ruff + args: [ --fix ] + - id: ruff-format diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 0000000..bcd6fb7 --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,7 @@ +- id: check-chameleon + name: Check Chameleon templates + description: Check for syntax and accessibility misuse in Chameleon templates. + entry: check-chameleon + language: python + types: [text] + files: \.cpt$ diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..db2f837 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,7 @@ +Change log +========== + +1.0 (unreleased) +---------------- + +- Initial release. diff --git a/README.md b/README.md index 0bfb936..c43b9e0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # pre-commit-check-chameleon -Check chameleon templates for missing HTML attributes via pre-commit +Check for syntax and accessibility misuse in Chameleon templates. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..8670757 --- /dev/null +++ b/setup.py @@ -0,0 +1,24 @@ +from setuptools import find_packages +from setuptools import setup + +version = "1.0.dev0" + +setup( + name="pre-commit-check-chameleon", + version=version, + package_dir={"": "src"}, + packages=find_packages("src"), + include_package_data=True, + zip_safe=False, + install_requires=[ + "lxml", + ], + extras_require={ + "test": [ + "testfixtures", + ], + }, + entry_points={ + "console_scripts": ["check-chameleon = check_chameleon.check_chameleon:main"] + }, +) diff --git a/src/check_chameleon/__init__.py b/src/check_chameleon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/check_chameleon/check_chameleon.py b/src/check_chameleon/check_chameleon.py new file mode 100644 index 0000000..0d812f0 --- /dev/null +++ b/src/check_chameleon/check_chameleon.py @@ -0,0 +1,225 @@ +import argparse +import io +import re +import typing + +import lxml.etree + +NSMAP = { + "xhtml": "http://www.w3.org/1999/xhtml", + "tal": "http://xml.zope.org/namespaces/tal", +} + +DOCTYPE_WRAPPER = """ +]> +{0}""" + +TAL_ATTRIBUTES = "{{{0}}}attributes".format(NSMAP["tal"]) +TAL_CONTENT_XPATH = ( + "./@tal:content|.//*/@tal:content|" ".//*/@tal:replace|.//tal:block/@replace" +) + + +def attribute(node, name): + found = node.attrib.get(name) + if found is not None: + return found + tal_attributes = node.attrib.get(TAL_ATTRIBUTES) + if tal_attributes is not None and name in tal_attributes: + for attr in tal_attributes.split(";"): + attr = attr.strip() + key, value = [el.strip() for el in attr.split(None, 1)] + if name == key: + return value + x_ng_attr_attribute = node.attrib.get("x-ng-attr-{0}".format(name)) + if x_ng_attr_attribute is not None: + return x_ng_attr_attribute + x_ng_attribute = node.attrib.get("x-ng-{0}".format(name)) + if x_ng_attribute is not None: + return x_ng_attribute + return None + + +class Context: + errors: typing.List[str] + checks = None + + def __init__(self, filename: str, a11y_lint_exclude=None): + with open(filename, "r") as stream: + content = stream.read() + self.errors = [] + self.node = None + if " element is missing the href attribute." + " Without the href attribute an anchor represents" + " a placeholder for where a link might otherwise have" + " been placed and is invisible for screen readers." + " If the is used to create interactive clickable" + " elements, consider using the