Deploy site

This commit is contained in:
Gitea Actions
2025-06-10 03:00:57 +02:00
commit 70bff17031
2329 changed files with 367195 additions and 0 deletions

View File

@ -0,0 +1,21 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
from ._elasticsearch import ElasticsearchIndexNameSanitizer
from ._excel import sanitize_excel_sheet_name, validate_excel_sheet_name
from ._javascript import JavaScriptVarNameSanitizer, sanitize_js_var_name, validate_js_var_name
from ._python import PythonVarNameSanitizer, sanitize_python_var_name, validate_python_var_name
__all__ = (
"ElasticsearchIndexNameSanitizer",
"JavaScriptVarNameSanitizer",
"PythonVarNameSanitizer",
"sanitize_excel_sheet_name",
"sanitize_js_var_name",
"sanitize_python_var_name",
"validate_excel_sheet_name",
"validate_js_var_name",
"validate_python_var_name",
)

View File

@ -0,0 +1,94 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import abc
import re
from re import Pattern
from typing import Final
from pathvalidate.error import ErrorReason, ValidationError
from typepy import is_null_string
from ._interface import NameSanitizer
def _preprocess(name: str) -> str:
return name.strip()
class VarNameSanitizer(NameSanitizer):
@property
@abc.abstractmethod
def _invalid_var_name_head_re(self) -> Pattern[str]: # pragma: no cover
pass
@property
@abc.abstractmethod
def _invalid_var_name_re(self) -> Pattern[str]: # pragma: no cover
pass
def validate(self) -> None:
self._validate(self._value)
def sanitize(self, replacement_text: str = "") -> str:
var_name = self._invalid_var_name_re.sub(replacement_text, self._str)
# delete invalid char(s) in the beginning of the variable name
is_require_remove_head: Final = any(
[
is_null_string(replacement_text),
self._invalid_var_name_head_re.search(replacement_text) is not None,
]
)
if is_require_remove_head:
var_name = self._invalid_var_name_head_re.sub("", var_name)
else:
match = self._invalid_var_name_head_re.search(var_name)
if match is not None:
var_name = match.end() * replacement_text + self._invalid_var_name_head_re.sub(
"", var_name
)
if not var_name:
return ""
try:
self._validate(var_name)
except ValidationError as e:
if e.reason == ErrorReason.RESERVED_NAME and e.reusable_name is False:
var_name += "_"
return var_name
def _validate(self, value: str) -> None:
self._validate_null_string(value)
unicode_var_name: Final = _preprocess(value)
if self._is_reserved_keyword(unicode_var_name):
raise ValidationError(
description=f"{unicode_var_name:s} is a reserved keyword by python",
reason=ErrorReason.RESERVED_NAME,
reusable_name=False,
reserved_name=unicode_var_name,
)
match = self._invalid_var_name_re.search(unicode_var_name)
if match is not None:
raise ValidationError(
description="invalid char found in the variable name: '{}'".format(
re.escape(match.group())
),
reason=ErrorReason.INVALID_CHARACTER,
)
match = self._invalid_var_name_head_re.search(unicode_var_name)
if match is not None:
raise ValidationError(
description="the first character of the variable name is invalid: '{}'".format(
re.escape(match.group())
),
reason=ErrorReason.INVALID_CHARACTER,
)

View File

@ -0,0 +1,28 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import re
from re import Pattern
from typing import Final
from ._base import VarNameSanitizer
class ElasticsearchIndexNameSanitizer(VarNameSanitizer):
__RE_INVALID_INDEX_NAME: Final[Pattern[str]] = re.compile(
"[" + re.escape('\\/*?"<>|,"') + r"\s]+"
)
__RE_INVALID_INDEX_NAME_HEAD: Final[Pattern[str]] = re.compile("^[_]+")
@property
def reserved_keywords(self) -> list[str]:
return []
@property
def _invalid_var_name_head_re(self) -> Pattern[str]:
return self.__RE_INVALID_INDEX_NAME_HEAD
@property
def _invalid_var_name_re(self) -> Pattern[str]:
return self.__RE_INVALID_INDEX_NAME

View File

@ -0,0 +1,78 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import re
from typing import Final
from pathvalidate import validate_pathtype
from pathvalidate.error import ErrorReason, ValidationError
from ._base import _preprocess
__MAX_SHEET_NAME_LEN: Final = 31
__INVALID_EXCEL_CHARS: Final = "[]:*?/\\"
__RE_INVALID_EXCEL_SHEET_NAME: Final = re.compile(
f"[{re.escape(__INVALID_EXCEL_CHARS):s}]", re.UNICODE
)
def validate_excel_sheet_name(sheet_name: str) -> None:
"""
:param str sheet_name: Excel sheet name to validate.
:raises pathvalidate.ValidationError (ErrorReason.INVALID_CHARACTER):
If the ``sheet_name`` includes invalid char(s):
|invalid_excel_sheet_chars|.
:raises pathvalidate.ValidationError (ErrorReason.INVALID_LENGTH):
If the ``sheet_name`` is longer than 31 characters.
"""
validate_pathtype(sheet_name)
if len(sheet_name) > __MAX_SHEET_NAME_LEN:
raise ValidationError(
description="sheet name is too long: expected<={:d}, actual={:d}".format(
__MAX_SHEET_NAME_LEN, len(sheet_name)
),
reason=ErrorReason.INVALID_LENGTH,
)
unicode_sheet_name = _preprocess(sheet_name)
match = __RE_INVALID_EXCEL_SHEET_NAME.search(unicode_sheet_name)
if match is not None:
raise ValidationError(
description="invalid char found in the sheet name: '{:s}'".format(
re.escape(match.group())
),
reason=ErrorReason.INVALID_CHARACTER,
)
def sanitize_excel_sheet_name(sheet_name: str, replacement_text: str = "") -> str:
"""
Replace invalid characters for an Excel sheet name within
the ``sheet_name`` with the ``replacement_text``.
Invalid characters are as follows:
|invalid_excel_sheet_chars|.
The ``sheet_name`` truncate to 31 characters
(max sheet name length of Excel) from the head, if the length
of the name is exceed 31 characters.
:param str sheet_name: Excel sheet name to sanitize.
:param str replacement_text: Replacement text.
:return: A replacement string.
:rtype: str
:raises ValueError: If the ``sheet_name`` is an invalid sheet name.
"""
try:
unicode_sheet_name = _preprocess(sheet_name)
except AttributeError as e:
raise ValueError(e)
modify_sheet_name = __RE_INVALID_EXCEL_SHEET_NAME.sub(replacement_text, unicode_sheet_name)
return modify_sheet_name[:__MAX_SHEET_NAME_LEN]

View File

@ -0,0 +1,38 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import abc
from pathvalidate import validate_pathtype
class NameSanitizer(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def reserved_keywords(self) -> list[str]: # pragma: no cover
pass
@abc.abstractmethod
def validate(self) -> None: # pragma: no cover
pass
@abc.abstractmethod
def sanitize(self, replacement_text: str = "") -> str: # pragma: no cover
pass
@property
def _str(self) -> str:
return str(self._value)
def __init__(self, value: str) -> None:
self._validate_null_string(value)
self._value = value.strip()
def _is_reserved_keyword(self, value: str) -> bool:
return value in self.reserved_keywords
@staticmethod
def _validate_null_string(text: str) -> None:
validate_pathtype(text)

View File

@ -0,0 +1,144 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import re
from re import Pattern
from typing import Final
from ._base import VarNameSanitizer
class JavaScriptVarNameSanitizer(VarNameSanitizer):
__JS_RESERVED_KEYWORDS_ES6: Final = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"yield",
]
__JS_RESERVED_KEYWORDS_FUTURE: Final = [
"enum",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"await",
"abstract",
"boolean",
"byte",
"char",
"double",
"final",
"float",
"goto",
"int",
"long",
"native",
"short",
"synchronized",
"throws",
"transient",
"volatile",
]
__JS_BUILTIN_CONSTANTS: Final = ["null", "true", "false"]
__RE_INVALID_VAR_NAME: Final = re.compile("[^a-zA-Z0-9_$]")
__RE_INVALID_VAR_NAME_HEAD: Final = re.compile("^[^a-zA-Z$]+")
@property
def reserved_keywords(self) -> list[str]:
return (
self.__JS_RESERVED_KEYWORDS_ES6
+ self.__JS_RESERVED_KEYWORDS_FUTURE
+ self.__JS_BUILTIN_CONSTANTS
)
@property
def _invalid_var_name_head_re(self) -> Pattern[str]:
return self.__RE_INVALID_VAR_NAME_HEAD
@property
def _invalid_var_name_re(self) -> Pattern[str]:
return self.__RE_INVALID_VAR_NAME
def validate_js_var_name(var_name: str) -> None:
"""
:param str var_name: Name to validate.
:raises pathvalidate.ValidationError (ErrorReason.INVALID_CHARACTER):
If the ``var_name`` is invalid as a JavaScript identifier.
:raises pathvalidate.ValidationError (ErrorReason.RESERVED_NAME):
If the ``var_name`` is equals to
`JavaScript reserved keywords
<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords>`__.
.. note::
Currently, not supported unicode variable names.
"""
JavaScriptVarNameSanitizer(var_name).validate()
def sanitize_js_var_name(var_name: str, replacement_text: str = "") -> str:
"""
Make a valid JavaScript variable name from ``var_name``.
To make a valid name:
- Replace invalid characters for a JavaScript variable name within
the ``var_name`` with the ``replacement_text``
- Delete invalid chars for the beginning of the variable name
- Append underscore (``"_"``) at the tail of the name if sanitized name
is one of the JavaScript reserved names
:JavaScriptstr filename: Name to sanitize.
:param str replacement_text: Replacement text.
:return: A replacement string.
:rtype: str
:raises ValueError: If ``var_name`` or ``replacement_text`` is invalid.
:Example:
:ref:`example-sanitize-var-name`
.. note::
Currently, not supported Unicode variable names.
.. seealso::
:py:func:`.validate_js_var_name`
"""
return JavaScriptVarNameSanitizer(var_name).sanitize(replacement_text)

View File

@ -0,0 +1,118 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import re
from re import Pattern
from typing import Final
from ._base import VarNameSanitizer
class PythonVarNameSanitizer(VarNameSanitizer):
__PYTHON_RESERVED_KEYWORDS: Final = [
"and",
"del",
"from",
"not",
"while",
"as",
"elif",
"global",
"or",
"with",
"assert",
"else",
"if",
"pass",
"yield",
"break",
"except",
"import",
"print",
"class",
"exec",
"in",
"raise",
"continue",
"finally",
"is",
"return",
"def",
"for",
"lambda",
"try",
]
__PYTHON_BUILTIN_CONSTANTS: Final = [
"False",
"True",
"None",
"NotImplemented",
"Ellipsis",
"__debug__",
]
__RE_INVALID_VAR_NAME: Final = re.compile("[^a-zA-Z0-9_]")
__RE_INVALID_VAR_NAME_HEAD: Final = re.compile("^[^a-zA-Z]+")
@property
def reserved_keywords(self) -> list[str]:
return self.__PYTHON_RESERVED_KEYWORDS + self.__PYTHON_BUILTIN_CONSTANTS
@property
def _invalid_var_name_head_re(self) -> Pattern[str]:
return self.__RE_INVALID_VAR_NAME_HEAD
@property
def _invalid_var_name_re(self) -> Pattern[str]:
return self.__RE_INVALID_VAR_NAME
def validate_python_var_name(var_name: str) -> None:
"""
:param str var_name: Name to validate.
:raises pathvalidate.ValidationError (ErrorReason.INVALID_CHARACTER):
If the ``var_name`` is invalid as
`Python identifier
<https://docs.python.org/3/reference/lexical_analysis.html#identifiers>`__.
:raises pathvalidate.ValidationError (ErrorReason.RESERVED_NAME):
If the ``var_name`` is equals to
`Python reserved keywords
<https://docs.python.org/3/reference/lexical_analysis.html#keywords>`__
or
`Python built-in constants
<https://docs.python.org/3/library/constants.html>`__.
:Example:
:ref:`example-validate-var-name`
"""
PythonVarNameSanitizer(var_name).validate()
def sanitize_python_var_name(var_name: str, replacement_text: str = "") -> str:
"""
Make a valid Python variable name from ``var_name``.
To make a valid name:
- Replace invalid characters for a Python variable name within
the ``var_name`` with the ``replacement_text``
- Delete invalid chars for the beginning of the variable name
- Append underscore (``"_"``) at the tail of the name if sanitized name
is one of the Python reserved names
:param str filename: Name to sanitize.
:param str replacement_text: Replacement text.
:return: A replacement string.
:rtype: str
:raises ValueError: If ``var_name`` or ``replacement_text`` is invalid.
:Example:
:ref:`example-sanitize-var-name`
.. seealso::
:py:func:`.validate_python_var_name`
"""
return PythonVarNameSanitizer(var_name).sanitize(replacement_text)