tagify 0.0.1__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.
tagify-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 xelA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tagify-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.1
2
+ Name: tagify
3
+ Version: 0.0.1
4
+ Summary: Parser that converts strings with variables and functions to more advance strings
5
+ Author-email: AlexFlipnote <root@alexflipnote.dev>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/xelA/tagify
8
+ Keywords: discord,tag,parser,template
9
+ Requires-Python: >=3.11.0
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Provides-Extra: dev
13
+ Requires-Dist: pyright; extra == "dev"
14
+ Requires-Dist: flake8; extra == "dev"
15
+ Requires-Dist: toml; extra == "dev"
16
+ Provides-Extra: maintainer
17
+ Requires-Dist: twine; extra == "maintainer"
18
+ Requires-Dist: wheel; extra == "maintainer"
19
+ Requires-Dist: build; extra == "maintainer"
20
+
21
+ # tagify
22
+ Parser that converts strings with variables and functions to more advance strings
23
+
24
+ Style is a bit inspired by jinja2 format, but not 100% the same.
25
+
26
+ This project is very early development, I might use it or not, but I'm not sure.
27
+
28
+ ## Requirements
29
+ - Python >=3.11
30
+
31
+ ## Install
32
+ ```
33
+ pip install tagify
34
+ ```
tagify-0.0.1/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # tagify
2
+ Parser that converts strings with variables and functions to more advance strings
3
+
4
+ Style is a bit inspired by jinja2 format, but not 100% the same.
5
+
6
+ This project is very early development, I might use it or not, but I'm not sure.
7
+
8
+ ## Requirements
9
+ - Python >=3.11
10
+
11
+ ## Install
12
+ ```
13
+ pip install tagify
14
+ ```
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.pdm.source]
6
+ name = "public"
7
+ url = "https://github.com/xelA/tagify"
8
+
9
+ [project]
10
+ name = "tagify"
11
+ description = "Parser that converts strings with variables and functions to more advance strings"
12
+ requires-python = ">=3.11.0"
13
+ license = {text = "MIT"}
14
+
15
+ dynamic = ["version"]
16
+ readme = "README.md"
17
+ keywords = ["discord", "tag", "parser", "template"]
18
+ authors = [{name = "AlexFlipnote", email = "root@alexflipnote.dev"}]
19
+
20
+ dependencies = []
21
+
22
+ [project.urls]
23
+ Repository = "https://github.com/xelA/tagify"
24
+
25
+ [project.optional-dependencies]
26
+ dev = ["pyright", "flake8", "toml"]
27
+ maintainer = ["twine", "wheel", "build"]
28
+
29
+ [tool.setuptools]
30
+ packages = [
31
+ "tagify",
32
+ ]
33
+
34
+ [tool.setuptools.dynamic]
35
+ version = {attr = "tagify.__version__"}
36
+ readme = {file = ["README.md"]}
37
+
38
+ [tool.flake8]
39
+ max-line-length = 128
40
+ ignore = [
41
+ "D210", "D400", "D401", "D100", "D202", "D413", "D107",
42
+ "D101", "D103", "D102", "E121", "D205", "D209", "D105",
43
+ "E252", "W605", "W504", "E128", "E124", "E999", "W504"
44
+ ]
45
+
46
+ [tool.pyright]
47
+ reportOptionalOperand = "none"
48
+ reportOptionalSubscript = "none"
49
+ reportOptionalMemberAccess = "none"
50
+ reportUnnecessaryTypeIgnoreComment = "warning"
51
+ typeCheckingMode = "basic"
52
+ pythonVersion = "3.11"
53
+
54
+ include = [
55
+ "tagify",
56
+ ]
57
+
58
+ exclude = [
59
+ "**/__pycache__", "**/*.pyc", "*.egg-info",
60
+ ".venv", "build", "docs", "dist",
61
+ ]
tagify-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ __version__ = "0.0.1"
2
+
3
+ # flake8: noqa
4
+ from .parser import *
@@ -0,0 +1,145 @@
1
+ import re
2
+
3
+ from typing import Any, Callable
4
+
5
+ # RegEx patterns
6
+ _re_placeholder = re.compile(r"{\s*([\w\d\.]+|\w+\(.+?\))\s*}")
7
+ _re_blocks = re.compile(r"{% elif (.+?) %}|{% else %}")
8
+ _re_terms = re.compile(r"(\s&&\s|\s\|\|\s)")
9
+ _re_match = re.compile(r"([\w\(\),]+)\s*(==|!=)\s*(.+)")
10
+ _re_variables = re.compile(r"{% set ([\w\d]+)\s*=\s*(.*) %}")
11
+ _re_conditions = re.compile(r"{% elif (.+?) %}")
12
+ _re_conditional_pattern = re.compile(
13
+ r"{% if (.+?) %}(.+?){% endif %}",
14
+ flags=re.DOTALL
15
+ )
16
+
17
+ __all__ = (
18
+ "TemplateParser",
19
+ )
20
+
21
+
22
+ class TemplateParser:
23
+ def __init__(
24
+ self,
25
+ context: dict | None = None,
26
+ functions: dict | None = None,
27
+ *,
28
+ conditionals: bool = True,
29
+ ):
30
+ self.context: dict[str, Any] = context or {}
31
+ self.functions: dict[str, Callable] = functions or {}
32
+
33
+ self._conditionals = conditionals
34
+
35
+ def render(self, template: str) -> str:
36
+ """Render the template with placeholders, conditionals, and function calls."""
37
+ template = self._process_variables(template) # Replace variables
38
+
39
+ if self._conditionals:
40
+ template = self._process_conditionals(template) # Process if/else/elif blocks
41
+
42
+ template = self._process_placeholders(template) # Replace placeholders and function calls
43
+ return template.strip() # Remove any trailing whitespace
44
+
45
+ def _process_variables(self, template: str) -> str:
46
+ """Replace all variables in the template with their values."""
47
+ for match in _re_variables.finditer(template):
48
+ key, value = match.groups()
49
+ self.context[key.strip()] = value.strip()
50
+
51
+ return _re_variables.sub("", template)
52
+
53
+ def _parse_placeholder(self, key: str) -> str:
54
+ """Evaluate placeholders or function calls."""
55
+ safe_unused = "{" + str(key) + "}"
56
+ # Check if the placeholder is a function call
57
+ if "(" in key and key.endswith(")"):
58
+ func_name, args = self._parse_function_call(key)
59
+ if func_name in self.functions:
60
+ try:
61
+ return str(self.functions[func_name](*args))
62
+ except Exception as e:
63
+ return f"[ FUNC_ERR:{func_name}: {e} ]"
64
+ return safe_unused # Return unmodified if function not found
65
+
66
+ # Split the key by dots and try to access each part as an attribute
67
+ parts = key.split(".")
68
+ value = self.context.get(parts[0], safe_unused) # Start with the base context
69
+
70
+ try:
71
+ for part in parts[1:]:
72
+ value = value[part] # Dig into each nested dict value
73
+ except KeyError:
74
+ return safe_unused # Return as-is if any part is inaccessible
75
+
76
+ return str(value)
77
+
78
+ def _process_placeholders(self, template: str) -> str:
79
+ """Replace all placeholders in the template with their values."""
80
+ return _re_placeholder.sub(
81
+ lambda m: self._parse_placeholder(m.group(1)),
82
+ template
83
+ )
84
+
85
+ def _process_conditionals(self, template: str) -> str:
86
+ """Handle if, elif, else conditionals in the template."""
87
+ return _re_conditional_pattern.sub(
88
+ self._evaluate_conditional_block,
89
+ template
90
+ )
91
+
92
+ def _evaluate_conditional_block(self, match: re.Match) -> str:
93
+ """Evaluate if, elif, and else conditions and return the appropriate block."""
94
+ condition, content = match.groups()
95
+ blocks = _re_blocks.split(content)
96
+ conditions = [condition] + _re_conditions.findall(content)
97
+
98
+ for cond, block in zip(conditions, blocks):
99
+ if self._evaluate_condition(cond.strip()):
100
+ return block.strip()
101
+
102
+ return blocks[-1].strip() if "{% else %}" in content else ""
103
+
104
+ def _evaluate_condition(self, condition: str) -> bool:
105
+ """Evaluate conditions safely without using eval."""
106
+ # Split by logical operators and evaluate each subcondition
107
+ terms = _re_terms.split(condition)
108
+ result = self._evaluate_comparison(terms[0].strip())
109
+
110
+ for i in range(1, len(terms), 2):
111
+ operator = terms[i].strip()
112
+ next_term = self._evaluate_comparison(terms[i + 1].strip())
113
+
114
+ if operator == "&&":
115
+ result = result and next_term
116
+ elif operator == "||":
117
+ result = result or next_term
118
+
119
+ return result
120
+
121
+ def _evaluate_comparison(self, term: str) -> bool:
122
+ """Evaluate a single comparison expression like 'user == "Alice"'."""
123
+ match = _re_match.match(term)
124
+ if not match:
125
+ return False
126
+
127
+ left, operator, right = match.groups()
128
+ left_value = str(self.context.get(left, left))
129
+ right_value = right.strip('"').strip("'")
130
+
131
+ if left_value.isdigit() and right_value.isdigit():
132
+ left_value = int(left_value)
133
+ right_value = int(right_value)
134
+
135
+ if operator == "==":
136
+ return left_value == right_value
137
+ elif operator == "!=":
138
+ return left_value != right_value
139
+
140
+ def _parse_function_call(self, func_string: str) -> tuple[str, list[str]]:
141
+ """Parse function calls like 'func_name(arg1, arg2)'."""
142
+ func_name = func_string.split("(", 1)[0]
143
+ args_string = func_string[len(func_name) + 1:-1] # Remove function name and parentheses
144
+ args = [arg.strip().strip('"').strip("'") for arg in args_string.split(",")]
145
+ return func_name, args
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.1
2
+ Name: tagify
3
+ Version: 0.0.1
4
+ Summary: Parser that converts strings with variables and functions to more advance strings
5
+ Author-email: AlexFlipnote <root@alexflipnote.dev>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/xelA/tagify
8
+ Keywords: discord,tag,parser,template
9
+ Requires-Python: >=3.11.0
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Provides-Extra: dev
13
+ Requires-Dist: pyright; extra == "dev"
14
+ Requires-Dist: flake8; extra == "dev"
15
+ Requires-Dist: toml; extra == "dev"
16
+ Provides-Extra: maintainer
17
+ Requires-Dist: twine; extra == "maintainer"
18
+ Requires-Dist: wheel; extra == "maintainer"
19
+ Requires-Dist: build; extra == "maintainer"
20
+
21
+ # tagify
22
+ Parser that converts strings with variables and functions to more advance strings
23
+
24
+ Style is a bit inspired by jinja2 format, but not 100% the same.
25
+
26
+ This project is very early development, I might use it or not, but I'm not sure.
27
+
28
+ ## Requirements
29
+ - Python >=3.11
30
+
31
+ ## Install
32
+ ```
33
+ pip install tagify
34
+ ```
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ tagify/__init__.py
5
+ tagify/parser.py
6
+ tagify.egg-info/PKG-INFO
7
+ tagify.egg-info/SOURCES.txt
8
+ tagify.egg-info/dependency_links.txt
9
+ tagify.egg-info/requires.txt
10
+ tagify.egg-info/top_level.txt
@@ -0,0 +1,10 @@
1
+
2
+ [dev]
3
+ pyright
4
+ flake8
5
+ toml
6
+
7
+ [maintainer]
8
+ twine
9
+ wheel
10
+ build
@@ -0,0 +1 @@
1
+ tagify