jinja2-indent 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.1
2
+ Name: jinja2-indent
3
+ Version: 0.1.0
4
+ Summary: A Jinja2 extension for managing indentation in templates.
5
+ License: MIT
6
+ Author: Mihail Mishakin
7
+ Author-email: x896321475@gmail.com
8
+ Maintainer: Mihail Mishakin
9
+ Maintainer-email: x896321475@gmail.com
10
+ Requires-Python: >=3.9,<4.0
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Requires-Dist: jinja2 (>=3.0.0,<4.0.0)
23
+ Requires-Dist: jinja2-simple-tags (>=0.6.1,<0.7.0)
24
+ Project-URL: repository, https://github.com/dldevinc/jinja2-indent
25
+ Description-Content-Type: text/markdown
26
+
27
+ # jinja2-indent
28
+
29
+ This Jinja2 extension adjusts the indentation of block content to a specified width.
30
+
31
+ [![PyPI](https://img.shields.io/pypi/v/jinja2-indent.svg)](https://pypi.org/project/jinja2-indent/)
32
+ [![Build Status](https://github.com/dldevinc/jinja2-indent/actions/workflows/tests.yml/badge.svg)](https://github.com/dldevinc/jinja2-indent)
33
+ [![Software license](https://img.shields.io/pypi/l/jinja2-indent.svg)](https://pypi.org/project/jinja2-indent/)
34
+
35
+ ## Compatibility
36
+
37
+ - `python` >= 3.9
38
+
39
+ ## Installation
40
+
41
+ Install the latest release with pip:
42
+
43
+ ```shell
44
+ pip install jinja2-indent
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ The `{% indent %}` tag provided by this extension allows you to adjust the indentation level of the content inside the tag block. You can specify the desired width (in spaces), and the extension will reformat the content accordingly. This is particularly useful for aligning nested or indented structures in templates.
50
+
51
+ ### Example
52
+
53
+ The following example demonstrates how to increase the indentation of a block of text:
54
+
55
+ ```python
56
+ from jinja2 import Environment
57
+ from jinja2_indent import IndentExtension
58
+
59
+ env = Environment(extensions=[IndentExtension])
60
+
61
+ template = env.from_string("""
62
+ root:
63
+ {% indent 4 %}
64
+ - name: a
65
+ value: 1
66
+
67
+ - name: b
68
+ value: 2
69
+
70
+ - name: c
71
+ value: 3
72
+ {% endindent %}
73
+ """)
74
+
75
+ output = template.render()
76
+ print(output)
77
+ ```
78
+
79
+ ```
80
+ root:
81
+ - name: a
82
+ value: 1
83
+
84
+ - name: b
85
+ value: 2
86
+
87
+ - name: c
88
+ value: 3
89
+ ```
90
+
91
+ The following example demonstrates how to remove unnecessary indentation from a block of text:
92
+
93
+ ```python
94
+ from jinja2 import Environment
95
+ from jinja2_indent import IndentExtension
96
+
97
+ env = Environment(extensions=[IndentExtension])
98
+
99
+ template = env.from_string("""
100
+ - name: a
101
+ value: 1
102
+
103
+ {% indent 0 %}
104
+ - name: b
105
+ value: 2
106
+ {% endindent %}
107
+
108
+ - name: c
109
+ value: 3
110
+ """)
111
+
112
+ output = template.render()
113
+ print(output)
114
+ ```
115
+
116
+ ```
117
+ - name: a
118
+ value: 1
119
+
120
+ - name: b
121
+ value: 2
122
+
123
+ - name: c
124
+ value: 3
125
+ ```
126
+
@@ -0,0 +1,99 @@
1
+ # jinja2-indent
2
+
3
+ This Jinja2 extension adjusts the indentation of block content to a specified width.
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/jinja2-indent.svg)](https://pypi.org/project/jinja2-indent/)
6
+ [![Build Status](https://github.com/dldevinc/jinja2-indent/actions/workflows/tests.yml/badge.svg)](https://github.com/dldevinc/jinja2-indent)
7
+ [![Software license](https://img.shields.io/pypi/l/jinja2-indent.svg)](https://pypi.org/project/jinja2-indent/)
8
+
9
+ ## Compatibility
10
+
11
+ - `python` >= 3.9
12
+
13
+ ## Installation
14
+
15
+ Install the latest release with pip:
16
+
17
+ ```shell
18
+ pip install jinja2-indent
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ The `{% indent %}` tag provided by this extension allows you to adjust the indentation level of the content inside the tag block. You can specify the desired width (in spaces), and the extension will reformat the content accordingly. This is particularly useful for aligning nested or indented structures in templates.
24
+
25
+ ### Example
26
+
27
+ The following example demonstrates how to increase the indentation of a block of text:
28
+
29
+ ```python
30
+ from jinja2 import Environment
31
+ from jinja2_indent import IndentExtension
32
+
33
+ env = Environment(extensions=[IndentExtension])
34
+
35
+ template = env.from_string("""
36
+ root:
37
+ {% indent 4 %}
38
+ - name: a
39
+ value: 1
40
+
41
+ - name: b
42
+ value: 2
43
+
44
+ - name: c
45
+ value: 3
46
+ {% endindent %}
47
+ """)
48
+
49
+ output = template.render()
50
+ print(output)
51
+ ```
52
+
53
+ ```
54
+ root:
55
+ - name: a
56
+ value: 1
57
+
58
+ - name: b
59
+ value: 2
60
+
61
+ - name: c
62
+ value: 3
63
+ ```
64
+
65
+ The following example demonstrates how to remove unnecessary indentation from a block of text:
66
+
67
+ ```python
68
+ from jinja2 import Environment
69
+ from jinja2_indent import IndentExtension
70
+
71
+ env = Environment(extensions=[IndentExtension])
72
+
73
+ template = env.from_string("""
74
+ - name: a
75
+ value: 1
76
+
77
+ {% indent 0 %}
78
+ - name: b
79
+ value: 2
80
+ {% endindent %}
81
+
82
+ - name: c
83
+ value: 3
84
+ """)
85
+
86
+ output = template.render()
87
+ print(output)
88
+ ```
89
+
90
+ ```
91
+ - name: a
92
+ value: 1
93
+
94
+ - name: b
95
+ value: 2
96
+
97
+ - name: c
98
+ value: 3
99
+ ```
@@ -0,0 +1 @@
1
+ from .extension import IndentExtension
@@ -0,0 +1,132 @@
1
+ import itertools
2
+ import re
3
+ from typing import Generator
4
+
5
+ from jinja2_simple_tags import ContainerTag
6
+
7
+ re_initial_newline = re.compile(r"^\r?\n")
8
+ re_leading_spaces = re.compile(r"^([\s\t]*)(.*)")
9
+
10
+
11
+ class IndentExtension(ContainerTag):
12
+ """
13
+ A Jinja2 extension for managing indentation in templates.
14
+ """
15
+
16
+ tags = {"indent"}
17
+
18
+ def render(self, width: int, caller=None):
19
+ """
20
+ Render the content with adjusted indentation.
21
+
22
+ Parameters:
23
+ - width (int): Target indentation width.
24
+ - caller (callable): A callable that provides the content to process.
25
+
26
+ Returns:
27
+ - str: The content with adjusted indentation.
28
+ """
29
+ content = caller()
30
+
31
+ # Strip
32
+ content = re_initial_newline.sub("", content)
33
+
34
+ # Split lines and expand tabs to spaces
35
+ lines = split_and_expand_tabs(content)
36
+
37
+ # Get current indentation level
38
+ current_indentation = get_indentation(lines)
39
+
40
+ # Calculate the shift needed for the desired indentation
41
+ shift = max(-current_indentation, width - current_indentation)
42
+
43
+ # Adjust lines and join them back together
44
+ adjusted_lines = adjust_lines(lines, shift)
45
+ return "\n".join(adjusted_lines)
46
+
47
+
48
+ def split_and_expand_tabs(text: str, tabsize: int = 4) -> list[str]:
49
+ """
50
+ Splits the given text into lines, preserving leading whitespace
51
+ and replacing tabs with spaces.
52
+
53
+ Parameters:
54
+ - text (str): The input text to be split into lines.
55
+ - tabsize (int, optional): The number of spaces to replace
56
+ each tab character with. Default is 4.
57
+
58
+ Returns:
59
+ - list[str]: A list of lines with tabs replaced by the specified number
60
+ of spaces, while maintaining the leading whitespace of each line.
61
+ """
62
+ lines = []
63
+ for line in text.splitlines():
64
+ match = re_leading_spaces.match(line)
65
+
66
+ # Extract the leading whitespace characters (spaces and tabs)
67
+ leading_spaces = match.group(1)
68
+
69
+ # Extract the remaining content of the line
70
+ content = match.group(2)
71
+
72
+ # Replace all tabs with spaces, according to the specified tabsize
73
+ lines.append(leading_spaces.expandtabs(tabsize) + content)
74
+
75
+ return lines
76
+
77
+
78
+ def get_indentation(lines: list[str]) -> int:
79
+ """
80
+ Calculate the minimum indentation (number of leading spaces)
81
+ of non-whitespace lines in a list of strings.
82
+
83
+ Args:
84
+ lines (list[str]): A list of strings to analyze for indentation.
85
+
86
+ Returns:
87
+ int: The minimum indentation of the non-whitespace lines.
88
+ Returns 0 if all lines are empty or contain only whitespace.
89
+ """
90
+ if not lines:
91
+ return 0
92
+
93
+ indentation = float("inf")
94
+
95
+ for line in lines:
96
+ # Calculate the number of leading spaces in the current line
97
+ line_indentation = sum(1 for _ in itertools.takewhile(str.isspace, line))
98
+
99
+ # Skip whitespace-only lines
100
+ if line_indentation == len(line):
101
+ continue
102
+
103
+ indentation = min(indentation, line_indentation)
104
+
105
+ return indentation if indentation != float("inf") else 0
106
+
107
+
108
+ def adjust_lines(lines: list[str], shift: int) -> Generator[str, None, None]:
109
+ """
110
+ Adjust the indentation of each line by adding or removing spaces.
111
+
112
+ Parameters:
113
+ - lines (list[str]): A list of strings to adjust.
114
+ - shift (int): The number of spaces to add (if positive) or remove (if negative).
115
+
116
+ Yields:
117
+ - str: The adjusted lines with modified indentation.
118
+ """
119
+ if not lines:
120
+ return
121
+
122
+ if not shift:
123
+ yield from lines
124
+ return
125
+
126
+ for line in lines:
127
+ if not line:
128
+ yield line
129
+ elif shift > 0:
130
+ yield " " * shift + line
131
+ else:
132
+ yield line[-shift:]
@@ -0,0 +1,88 @@
1
+ [tool.poetry]
2
+ name = "jinja2-indent"
3
+ version = "0.1.0"
4
+ description = "A Jinja2 extension for managing indentation in templates."
5
+ authors = ["Mihail Mishakin <x896321475@gmail.com>"]
6
+ maintainers = ["Mihail Mishakin <x896321475@gmail.com>"]
7
+ readme = "README.md"
8
+ license = "MIT"
9
+ classifiers = [
10
+ "Operating System :: OS Independent",
11
+ "Development Status :: 3 - Alpha",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3 :: Only",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ ]
22
+ packages = [
23
+ { include = "jinja2_indent" },
24
+ ]
25
+
26
+ [tool.poetry.urls]
27
+ repository = "https://github.com/dldevinc/jinja2-indent"
28
+
29
+ [tool.poetry.dependencies]
30
+ python = "^3.9"
31
+ jinja2 = "^3.0.0"
32
+ jinja2-simple-tags = "^0.6.1"
33
+
34
+ [tool.poetry.group.pytest]
35
+ optional = true
36
+
37
+ [tool.poetry.group.pytest.dependencies]
38
+ pytest = "^8.3.4"
39
+ pytest-xdist = "^3.6.1"
40
+ pytest-cov = "*"
41
+
42
+ [tool.isort]
43
+ extend_skip = [".md", ".json"]
44
+ line_length = 90
45
+ known_first_party = ["jinja2_indent"]
46
+ multi_line_output = 5
47
+
48
+ [tool.flake8]
49
+ max-line-length = 90
50
+ extend-exclude = [
51
+ "tests",
52
+ ]
53
+ per-file-ignores = [
54
+ "__init__.py: F401", # imported but unused
55
+ "apps.py: F401", # imported but unused
56
+ ]
57
+ extend-ignore = [
58
+ # "E501", # line too long
59
+ "E128", # continuation line under-indented for visual indent
60
+ ]
61
+
62
+ [tool.pytest.ini_options]
63
+ addopts = "--showlocals -rf -s"
64
+ norecursedirs = [
65
+ "*.egg",
66
+ ".*",
67
+ "_darcs",
68
+ "build",
69
+ "CVS",
70
+ "dist",
71
+ "node_modules",
72
+ "venv",
73
+ ".venv",
74
+ "{arch}",
75
+ "__pycache__",
76
+ ]
77
+
78
+ [tool.coverage.run]
79
+ source = [
80
+ "jinja2_indent/",
81
+ ]
82
+ omit = [
83
+ "*/tests/*",
84
+ ]
85
+
86
+ [build-system]
87
+ requires = ["poetry-core"]
88
+ build-backend = "poetry.core.masonry.api"