From 4455be957c777f9543e1aec70b24ec875b6ec1ce Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 14 Feb 2021 10:53:07 +0200 Subject: [PATCH] Add constant name convention config Notes: - Add screaming snake case scheme - Downgrade sphinx there is some issue with 3.5.0 - Closes #407 --- docs/api/xml-nodes.rst | 1 - docs/examples/config.sample.xml | 3 +- setup.cfg | 2 +- tests/formats/dataclass/test_filters.py | 2 +- tests/models/test_config.py | 2 ++ tests/utils/test_text.py | 11 +++++++ xsdata/formats/dataclass/filters.py | 16 +++++++-- xsdata/models/config.py | 43 ++++++++++++++++--------- xsdata/utils/text.py | 5 +++ 9 files changed, 63 insertions(+), 22 deletions(-) diff --git a/docs/api/xml-nodes.rst b/docs/api/xml-nodes.rst index 35d2e0289..1fd9c9c4f 100644 --- a/docs/api/xml-nodes.rst +++ b/docs/api/xml-nodes.rst @@ -14,7 +14,6 @@ for models and their fields. XmlNode ElementNode - AnyTypeNode WildcardNode UnionNode PrimitiveNode diff --git a/docs/examples/config.sample.xml b/docs/examples/config.sample.xml index 1322448fc..ecd2aebbc 100644 --- a/docs/examples/config.sample.xml +++ b/docs/examples/config.sample.xml @@ -1,5 +1,5 @@ - + generated pydata @@ -10,6 +10,7 @@ + diff --git a/setup.cfg b/setup.cfg index 985c5f75a..82f9a5b41 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,7 +58,7 @@ dev = pytest-cov tox docs = - sphinx + sphinx==3.4.3 sphinx-autobuild sphinx-autodoc-typehints sphinx-copybutton diff --git a/tests/formats/dataclass/test_filters.py b/tests/formats/dataclass/test_filters.py index 31f5ebd35..3b99831b7 100644 --- a/tests/formats/dataclass/test_filters.py +++ b/tests/formats/dataclass/test_filters.py @@ -56,7 +56,7 @@ def test_constant_name(self): self.assertEqual("NONE_VALUE", self.filters.constant_name("None", "cls")) self.assertEqual("BR_EAK_VALUE", self.filters.constant_name("BrEak", "cls")) self.assertEqual("VALUE_1", self.filters.constant_name("1", "cls")) - self.assertEqual("BANG", self.filters.constant_name("boom", "cls")) + self.assertEqual("Bang", self.filters.constant_name("boom", "cls")) def test_module_name(self): self.filters.module_aliases["http://github.com/tefra/xsdata"] = "xsdata" diff --git a/tests/models/test_config.py b/tests/models/test_config.py index a03966334..58ef8e73b 100644 --- a/tests/models/test_config.py +++ b/tests/models/test_config.py @@ -31,6 +31,7 @@ def test_create(self): " \n" ' \n' ' \n' + ' \n' ' \n' ' \n' " \n" @@ -79,6 +80,7 @@ def test_read(self): " \n" ' \n' ' \n' + ' \n' ' \n' ' \n' " \n" diff --git a/tests/utils/test_text.py b/tests/utils/test_text.py index f5582d081..c8c825279 100644 --- a/tests/utils/test_text.py +++ b/tests/utils/test_text.py @@ -6,6 +6,7 @@ from xsdata.utils.text import mixed_pascal_case from xsdata.utils.text import mixed_snake_case from xsdata.utils.text import pascal_case +from xsdata.utils.text import screaming_snake_case from xsdata.utils.text import snake_case from xsdata.utils.text import split_words @@ -21,6 +22,16 @@ def test_snake_case(self): self.assertEqual("user_name", snake_case("user_name")) self.assertEqual("suser_name", snake_case("SUserNAME")) + def test_screaming_snake_case(self): + self.assertEqual("P00P", screaming_snake_case("p00p")) + self.assertEqual("USERNAME", screaming_snake_case("USERName")) + self.assertEqual("USER_NAME", screaming_snake_case("UserNAME")) + self.assertEqual("USER_NAME", screaming_snake_case("USER_name")) + self.assertEqual("USER_NAME", screaming_snake_case("USER-NAME")) + self.assertEqual("USER_NAME", screaming_snake_case("User_Name")) + self.assertEqual("USER_NAME", screaming_snake_case("user_name")) + self.assertEqual("SUSER_NAME", screaming_snake_case("SUserNAME")) + def test_pascal_case(self): self.assertEqual("P00P", pascal_case("p00p")) self.assertEqual("Username", pascal_case("USERName")) diff --git a/xsdata/formats/dataclass/filters.py b/xsdata/formats/dataclass/filters.py index f43e8fcd0..bd85adc55 100644 --- a/xsdata/formats/dataclass/filters.py +++ b/xsdata/formats/dataclass/filters.py @@ -40,11 +40,13 @@ class Filters: class_case: Callable = field(default=text.pascal_case) field_case: Callable = field(default=text.snake_case) + constant_case: Callable = field(default=text.screaming_snake_case) package_case: Callable = field(default=text.snake_case) module_case: Callable = field(default=text.snake_case) class_safe_prefix: str = field(default="type") field_safe_prefix: str = field(default="value") + constant_safe_prefix: str = field(default="value") package_safe_prefix: str = field(default="pkg") module_safe_prefix: str = field(default="mod") @@ -97,9 +99,10 @@ def field_name(self, name: str, class_name: str) -> str: Provide the class name as context for the naming schemes. """ - return self.field_aliases.get(name) or self._field_name(name, class_name) + alias = self.field_aliases.get(name) + if alias: + return alias - def _field_name(self, name: str, class_name: str) -> str: safe_name = utils.safe_snake(name, self.field_safe_prefix) return self.field_case(safe_name, class_name=class_name) @@ -110,7 +113,12 @@ def constant_name(self, name: str, class_name: str) -> str: Provide the class name as context for the naming schemes. """ - return self.field_name(name, class_name).upper() + alias = self.field_aliases.get(name) + if alias: + return alias + + safe_name = utils.safe_snake(name, self.constant_safe_prefix) + return self.constant_case(safe_name, class_name=class_name) def module_name(self, name: str) -> str: """Convert the given string to a module name according to the selected @@ -538,10 +546,12 @@ def index_aliases(aliases: List[GeneratorAlias]) -> Dict: module_aliases=index_aliases(config.aliases.module_name), class_case=config.conventions.class_name.case, field_case=config.conventions.field_name.case, + constant_case=config.conventions.constant_name.case, package_case=config.conventions.package_name.case, module_case=config.conventions.module_name.case, class_safe_prefix=config.conventions.class_name.safe_prefix, field_safe_prefix=config.conventions.field_name.safe_prefix, + constant_safe_prefix=config.conventions.constant_name.safe_prefix, package_safe_prefix=config.conventions.package_name.safe_prefix, module_safe_prefix=config.conventions.module_name.safe_prefix, docstring_style=config.output.docstring_style, diff --git a/xsdata/models/config.py b/xsdata/models/config.py index 350b27646..89ed7ca16 100644 --- a/xsdata/models/config.py +++ b/xsdata/models/config.py @@ -52,30 +52,39 @@ class NameCase(Enum): when it encounters non alphanumerical characters or when an upper case letter follows a lower case letter. - ========= ========= ========= ========== ========= =========== ============ - Original Pascal Camel Snake Mixed Mixed Snake Mixed Pascal - ========= ========= ========= ========== ========= =========== ============ - p00p P00P p00P p00p p00p p00p P00p - USERName Username username username USERName USERName USERName - UserNAME UserName userName user_name UserNAME User_NAME UserNAME - USER_name UserName userName user_name USERname USER_name USERname - USER-NAME UserName userName user_name USERNAME USER_NAME USERNAME - User_Name UserName userName user_name UserName User_Name UserName - user_name UserName userName user_name username user_name Username - SUserNAME SuserName suserName suser_name SUserNAME SUser_NAME SUserNAME - ========= ========= ========= ========== ========= =========== ============ + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | Original | Pascal | Camel | Snake | Screaming Snake | Mixed | Mixed Snake | Mixed Pascal | + +===========+===========+===========+============+=================+===========+=============+==============+ + | p00p | P00P | p00P | p00p | P00P | p00p | p00p | P00p | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | USERName | Username | username | username | USERNAME | USERName | USERName | USERName | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | UserNAME | UserName | userName | user_name | USER_NAME | UserNAME | User_NAME | UserNAME | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | USER_name | UserName | userName | user_name | USER_NAME | USERname | USER_name | USERname | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | USER-NAME | UserName | userName | user_name | USER_NAME | USERNAME | USER_NAME | USERNAME | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | User_Name | UserName | userName | user_name | USER_NAME | UserName | User_Name | UserName | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | user_name | UserName | userName | user_name | USER_NAME | username | user_name | Username | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ + | SUserNAME | SuserName | suserName | suser_name | SUSER_NAME | SUserNAME | SUser_NAME | SUserNAME | + +-----------+-----------+-----------+------------+-----------------+-----------+-------------+--------------+ :cvar PASCAL: pascalCase :cvar CAMEL: camelCase :cvar SNAKE: snakeCase + :cvar SCREAMING_SNAKE: :cvar MIXED: mixedCase - :cvar MIXED_SNAKE: mixedSnakeCase - :cvar MIXED_PASCAL: mixedPascalCase - """ + :cvar MIXED_SNAKE: + :cvar MIXED_PASCAL: + """ # noqa PASCAL = "pascalCase" CAMEL = "camelCase" SNAKE = "snakeCase" + SCREAMING_SNAKE = "screamingSnakeCase" MIXED = "mixedCase" MIXED_SNAKE = "mixedSnakeCase" MIXED_PASCAL = "mixedPascalCase" @@ -93,6 +102,7 @@ def callback(self) -> Callable: "pascalCase": text.pascal_case, "camelCase": text.camel_case, "snakeCase": text.snake_case, + "screamingSnakeCase": text.screaming_snake_case, "mixedCase": text.mixed_case, "mixedSnakeCase": text.mixed_snake_case, "mixedPascalCase": text.mixed_pascal_case, @@ -174,6 +184,9 @@ class GeneratorConventions: field_name: NameConvention = element( default_factory=lambda: NameConvention(NameCase.SNAKE, "value") ) + constant_name: NameConvention = element( + default_factory=lambda: NameConvention(NameCase.SCREAMING_SNAKE, "value") + ) module_name: NameConvention = element( default_factory=lambda: NameConvention(NameCase.SNAKE, "mod") ) diff --git a/xsdata/utils/text.py b/xsdata/utils/text.py index 768c4619d..88f4892f7 100644 --- a/xsdata/utils/text.py +++ b/xsdata/utils/text.py @@ -62,6 +62,11 @@ def snake_case(string: str, **kwargs: Any) -> str: return "_".join(map(str.lower, split_words(string))) +def screaming_snake_case(string: str, **kwargs: Any) -> str: + """Convert the given string to screaming snake case.""" + return snake_case(string, **kwargs).upper() + + def kebab_case(string: str, **kwargs: Any) -> str: """Convert the given string to kebab case.""" return "-".join(split_words(string))