From a091cbc8d47f43ad197a413fbae07570dab08f62 Mon Sep 17 00:00:00 2001 From: philippe Date: Mon, 24 Mar 2025 12:36:07 -0400 Subject: [PATCH 1/5] Remove stringcase --- dash/development/_py_prop_typing.py | 11 ++++++++--- requirements/install.txt | 1 - 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dash/development/_py_prop_typing.py b/dash/development/_py_prop_typing.py index fcd4c58961..aa3e4fbef7 100644 --- a/dash/development/_py_prop_typing.py +++ b/dash/development/_py_prop_typing.py @@ -3,8 +3,7 @@ import string import textwrap import importlib - -import stringcase +import re shapes = {} @@ -52,9 +51,15 @@ def generate_any(*_): return "typing.Any" +def pascal_case(name: str): + return name[0].upper() + re.sub( + r"[\-_\.\s]([a-z])", lambda match: match.group(1).upper(), name[1:] + ) + + def generate_shape(type_info, component_name: str, prop_name: str): props = [] - name = stringcase.pascalcase(prop_name) + name = pascal_case(prop_name) for prop_key, prop_type in type_info["value"].items(): typed = get_prop_typing( diff --git a/requirements/install.txt b/requirements/install.txt index 8a02fa781a..65fccc279d 100644 --- a/requirements/install.txt +++ b/requirements/install.txt @@ -7,4 +7,3 @@ requests retrying nest-asyncio setuptools -stringcase>=1.2.0 From 7ed468366d4c90267b6ac6d1db90d09741542957 Mon Sep 17 00:00:00 2001 From: philippe Date: Mon, 24 Mar 2025 13:55:19 -0400 Subject: [PATCH 2/5] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf289c5b23..688a5c7ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [UNRELEASED] + +## Fixed + +- [#3239](https://github.com/plotly/dash/pull/3239) Remove stringcase dependency, fix [#3238](https://github.com/plotly/dash/issues/3238) + ## [3.0.0] - 2025-03-17 ## Added From ea16723239ca728d252e047b291a15d2b52410f6 Mon Sep 17 00:00:00 2001 From: philippe Date: Mon, 24 Mar 2025 15:10:33 -0400 Subject: [PATCH 3/5] Improved pascal_case + tests --- dash/_utils.py | 13 +++++++++++++ dash/development/_py_prop_typing.py | 9 ++------- tests/unit/library/test_utils.py | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/dash/_utils.py b/dash/_utils.py index 38ff0c39ac..424c6bad41 100644 --- a/dash/_utils.py +++ b/dash/_utils.py @@ -11,6 +11,8 @@ import secrets import string import inspect +import re + from html import escape from functools import wraps from typing import Union @@ -302,3 +304,14 @@ def get_caller_name(): return s.frame.f_locals.get("__name__", "__main__") return "__main__" + + +def pascal_case(name: str | None): + s = re.sub(r"\s", "_", str(name)) + # Replace leading `_` + s = re.sub("^[_]+", "", s) + if not s: + return s + return s[0].upper() + re.sub( + r"[\-_\.]+([a-z])", lambda match: match.group(1).upper(), s[1:] + ) diff --git a/dash/development/_py_prop_typing.py b/dash/development/_py_prop_typing.py index aa3e4fbef7..1c3f673977 100644 --- a/dash/development/_py_prop_typing.py +++ b/dash/development/_py_prop_typing.py @@ -3,7 +3,8 @@ import string import textwrap import importlib -import re + +from .._utils import pascal_case shapes = {} @@ -51,12 +52,6 @@ def generate_any(*_): return "typing.Any" -def pascal_case(name: str): - return name[0].upper() + re.sub( - r"[\-_\.\s]([a-z])", lambda match: match.group(1).upper(), name[1:] - ) - - def generate_shape(type_info, component_name: str, prop_name: str): props = [] name = pascal_case(prop_name) diff --git a/tests/unit/library/test_utils.py b/tests/unit/library/test_utils.py index f643442dd4..cb677e8355 100644 --- a/tests/unit/library/test_utils.py +++ b/tests/unit/library/test_utils.py @@ -58,3 +58,19 @@ def test_ddut001_attribute_dict(): a.x = 4 assert err.value.args == ("Object is final: No new keys may be added.", "x") assert "x" not in a + + +@pytest.mark.parametrize( + "value,expected", + [ + ("foo_bar", "FooBar"), + ("", ""), + ("fooBarFoo", "FooBarFoo"), + ("foo bar", "FooBar"), + ("foo-bar", "FooBar"), + ("__private_prop", "PrivateProp"), + ("double__middle___triple", "DoubleMiddleTriple"), + ], +) +def test_ddut002_pascal_case(value, expected): + assert utils.pascal_case(value) == expected From a4d9eb3fab3433a7b92d8b7cb4bad53c16db1cac Mon Sep 17 00:00:00 2001 From: philippe Date: Mon, 24 Mar 2025 15:12:05 -0400 Subject: [PATCH 4/5] Set py.typed partial --- dash/py.typed | 1 + 1 file changed, 1 insertion(+) diff --git a/dash/py.typed b/dash/py.typed index e69de29bb2..b648ac9233 100644 --- a/dash/py.typed +++ b/dash/py.typed @@ -0,0 +1 @@ +partial From 217915847470d001e472dd4ad6bcac9aaa3ab0c6 Mon Sep 17 00:00:00 2001 From: philippe Date: Mon, 24 Mar 2025 15:41:05 -0400 Subject: [PATCH 5/5] Fix old typing --- dash/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/_utils.py b/dash/_utils.py index 424c6bad41..f1056d0130 100644 --- a/dash/_utils.py +++ b/dash/_utils.py @@ -306,7 +306,7 @@ def get_caller_name(): return "__main__" -def pascal_case(name: str | None): +def pascal_case(name: Union[str, None]): s = re.sub(r"\s", "_", str(name)) # Replace leading `_` s = re.sub("^[_]+", "", s)