Deploy site
This commit is contained in:
@ -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",
|
||||
)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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,
|
||||
)
|
@ -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
|
@ -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]
|
@ -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)
|
@ -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)
|
@ -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)
|
Reference in New Issue
Block a user