Skip to content

Commit

Permalink
chore
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-yin committed Aug 26, 2024
0 parents commit f880fb2
Show file tree
Hide file tree
Showing 23 changed files with 594 additions and 0 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
release:
types: [published]

permissions:
contents: read

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install poetry
- name: Build package
run: poetry build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
50 changes: 50 additions & 0 deletions .github/workflows/runtests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Runs tests

on:
pull_request:
push:
branches:
- main

jobs:

code-style-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: pip
- name: Install dependencies
run: |
pip install -r requirements-dev.txt
- name: Check code style
run: |
pre-commit run --all-files
runtests:
runs-on: ubuntu-latest
env:
CHANNELS_REDIS: redis://localhost:6379/0
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10' ]
services:
redis:
image: redis
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r requirements-dev.txt
- name: Run tests
run: |
tox
52 changes: 52 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
*.pyc
*.egg
*.egg-info

docs/build
dist
build

*.lock

*.sqlite3
*.db

*.DS_Store

.cache
__pycache__
.mypy_cache/
.pytest_cache/
.vscode/
.coverage
docs/build

node_modules/

*.bak

logs
*log
npm-debug.log*

# Translations
# *.mo
*.pot

# Django media/static dirs
media/
static/dist/
static/dev/

.ipython/
.env

celerybeat.pid
celerybeat-schedule

# Common typos
:w
'
.tox

/venv/
24 changes: 24 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
repos:
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 23.10.1
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
- id: flake8
additional_dependencies:
- flake8-bugbear
- flake8-comprehensions
- flake8-no-pep420
- flake8-print
- flake8-tidy-imports
- flake8-typing-imports
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
hooks:
- id: mypy
14 changes: 14 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 2

build:
os: ubuntu-22.04
tools:
python: "3.12"

# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/source/conf.py

python:
install:
- requirements: docs/requirements.txt
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
build:
poetry build

publish:
poetry publish

# poetry config repositories.testpypi https://test.pypi.org/legacy/
publish-test:
poetry publish -r testpypi
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# README
22 changes: 22 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[tool.poetry]
name = "django-template-simplify"
version = "1.0.1"
description = "A set of tools to help simplify your Django templates."
authors = ["Michael Yin <[email protected]>"]
license = "MIT"
homepage = "https://github.com/rails-inspire-django/django-template-simplify"
readme = "README.md"
packages = [{ include = "template_simplify", from = "src" }]

[tool.poetry.urls]
Changelog = "https://github.com/rails-inspire-django/django-template-simplify/releases"

[tool.poetry.dependencies]
python = ">=3.8"
django = ">=3.0"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["setuptools", "poetry_core>=1.0"]
build-backend = "poetry.core.masonry.api"
11 changes: 11 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pre-commit==2.9.2
tox==4.11.3
tox-gh-actions==3.1.3

django==4.2 # for local tests
typing_extensions
pytest
pytest-django
pytest-xdist
pytest-mock
jinja2
22 changes: 22 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[flake8]
ignore = E203, E266, E501, W503, E231, E701, B950, B907
max-line-length = 88
max-complexity = 18
select = B,C,E,F,W,T4,B9

[isort]
profile = black

[mypy]
python_version = 3.10
check_untyped_defs = False
ignore_missing_imports = True
warn_unused_ignores = False
warn_redundant_casts = False
warn_unused_configs = False

[mypy-*.tests.*]
ignore_errors = True

[mypy-*.migrations.*]
ignore_errors = True
5 changes: 5 additions & 0 deletions src/template_simplify/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .templatetags.template_simplify import dom_id

__all__ = [
"dom_id",
]
Empty file.
138 changes: 138 additions & 0 deletions src/template_simplify/templatetags/template_simplify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import re
from typing import Any, Optional

from django import template
from django.db.models.base import Model
from django.template import Node, TemplateSyntaxError

register = template.Library()


@register.simple_tag
def dom_id(instance: Any, prefix: Optional[str] = "") -> str:
"""
Generate a unique identifier for a Django model instance, class, or even Python object.
Args:
instance (Any): The instance or class for which the identifier is generated.
prefix (Optional[str]): An optional prefix to prepend to the identifier. Defaults to an empty string.
Returns:
str: The generated identifier.
Raises:
Exception: If the model instance does not have either the `to_key` or `pk` attribute.
Note:
- If `instance` is a Django model instance, the identifier is generated based on the `to_key` or `pk` attribute.
- If `instance` is a Django model class, the identifier is generated as `new_<class_name>`.
- If `instance` is neither a model instance nor a model class, the identifier is generated based on the `to_key`
attribute if available, otherwise it uses the string representation of the instance.
- The `prefix` argument can be used to prepend a prefix to the generated identifier.
"""
if not isinstance(instance, type) and isinstance(instance, Model):
# Django model instance
if hasattr(instance, "to_key") and getattr(instance, "to_key"): # noqa: B009
identifier = f"{instance.__class__.__name__.lower()}_{instance.to_key}"
elif hasattr(instance, "pk") and getattr(instance, "pk"): # noqa: B009
identifier = f"{instance.__class__.__name__.lower()}_{instance.pk}"
else:
raise Exception(
f"Model instance must have either to_key or pk attribute {instance}"
)
elif isinstance(instance, type) and issubclass(instance, Model):
# Django model class
identifier = f"new_{instance.__name__.lower()}"
else:
if hasattr(instance, "to_key") and getattr(instance, "to_key"): # noqa: B009
# Developer can still use to_key property to generate the identifier
identifier = f"{instance.to_key}"
else:
# Use the string representation
identifier = str(instance)

if prefix:
identifier = f"{prefix}_{identifier}"

return identifier


ATTRIBUTE_RE = re.compile(
r"""
(?P<attr>
[@\w:_\.\/-]+
)
(?P<sign>
\+?=
)
(?P<value>
['"]? # start quote
[^"']*
['"]? # end quote
)
""",
re.VERBOSE | re.UNICODE,
)


VALUE_RE = re.compile(
r"""
['"] # start quote (required)
(?P<value>
[^"']* # match any character except quotes
)
['"] # end quote (required)
""",
re.VERBOSE | re.UNICODE,
)


@register.tag
def class_names(parser, token):
error_msg = f"{token.split_contents()[0]!r} tag requires " "a list of css classes"
try:
bits = token.split_contents()
tag_name = bits[0] # noqa
attr_list = bits[1:]
except ValueError as exc:
raise TemplateSyntaxError(error_msg) from exc

css_ls = []
css_dict = {}
for pair in attr_list:
attribute_match = ATTRIBUTE_RE.match(pair) or VALUE_RE.match(pair)

if attribute_match:
dct = attribute_match.groupdict()
attr = dct.get("attr", None)
# sign = dct.get("sign", None)
value = parser.compile_filter(dct["value"])
if attr:
css_dict[attr] = value
else:
css_ls.append(value)
else:
raise TemplateSyntaxError("class_names found supported token: " + f"{pair}")

return ClassNamesNode(css_ls=css_ls, css_dict=css_dict)


class ClassNamesNode(Node):
def __init__(self, css_ls, css_dict):
self.css_ls = css_ls
self.css_dict = css_dict

def render(self, context):
final_css = []

# for common css classes
for value in self.css_ls:
final_css.append(value.token)

# for conditionals
for attr, expression in self.css_dict.items():
real_value = expression.resolve(context)
if real_value:
final_css.append(attr)

return " ".join(final_css)
Empty file added tests/__init__.py
Empty file.
Loading

0 comments on commit f880fb2

Please sign in to comment.