omlish 0.0.0.dev302__py3-none-any.whl → 0.0.0.dev303__py3-none-any.whl

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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev302'
2
- __revision__ = '0b4f820252a0f7a3e020da512ead1e112d150cd2'
1
+ __version__ = '0.0.0.dev303'
2
+ __revision__ = '8c675f0f9a5c459eddf54867184cd1da05cff6ac'
3
3
 
4
4
 
5
5
  #
@@ -35,7 +35,7 @@ from .base import SimpleBootstrap
35
35
 
36
36
 
37
37
  BOOTSTRAP_TYPES_BY_NAME: ta.Mapping[str, ta.Type[Bootstrap]] = { # noqa
38
- lang.snake_case(cls.__name__[:-len('Bootstrap')]): cls
38
+ lang.snake_case(*lang.split_string_casing(cls.__name__[:-len('Bootstrap')])): cls
39
39
  for cls in lang.deep_subclasses(Bootstrap, concrete_only=True)
40
40
  }
41
41
 
omlish/lang/__init__.py CHANGED
@@ -23,6 +23,33 @@ from .cached.property import ( # noqa
23
23
  cached_property,
24
24
  )
25
25
 
26
+ from .casing import ( # noqa
27
+ StringCasingError,
28
+ ImproperStringCasingError,
29
+ UnknownStringCasingError,
30
+ AmbiguousStringCasingError,
31
+
32
+ StringCasing,
33
+ CamelCase,
34
+ LowCamelCase,
35
+ SnakeCase,
36
+ UpSnakeCase,
37
+
38
+ STRING_CASINGS,
39
+ CAMEL_CASE,
40
+ LOW_CAMEL_CASE,
41
+ SNAKE_CASE,
42
+ UP_SNAKE_CASE,
43
+
44
+ camel_case,
45
+ low_camel_case,
46
+ snake_case,
47
+ up_snake_case,
48
+
49
+ get_string_casing,
50
+ split_string_casing,
51
+ )
52
+
26
53
  from .classes.abstract import ( # noqa
27
54
  Abstract,
28
55
  AbstractTypeError,
@@ -260,7 +287,6 @@ from .strings import ( # noqa
260
287
  BOOL_STRINGS,
261
288
  BOOL_TRUE_STRINGS,
262
289
  STRING_BOOL_VALUES,
263
- camel_case,
264
290
  find_any,
265
291
  indent_lines,
266
292
  is_dunder,
@@ -272,7 +298,6 @@ from .strings import ( # noqa
272
298
  prefix_lines,
273
299
  replace_many,
274
300
  rfind_any,
275
- snake_case,
276
301
  strip_prefix,
277
302
  strip_suffix,
278
303
  )
omlish/lang/casing.py ADDED
@@ -0,0 +1,192 @@
1
+ import abc
2
+ import re
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ class StringCasingError(Exception):
10
+ pass
11
+
12
+
13
+ class ImproperStringCasingError(StringCasingError):
14
+ pass
15
+
16
+
17
+ class UnknownStringCasingError(StringCasingError):
18
+ pass
19
+
20
+
21
+ class AmbiguousStringCasingError(StringCasingError):
22
+ pass
23
+
24
+
25
+ def _check_all_lowercase(*ps: str) -> None:
26
+ for p in ps:
27
+ if p.lower() != p:
28
+ raise ImproperStringCasingError(p)
29
+
30
+
31
+ ##
32
+
33
+
34
+ class StringCasing(abc.ABC):
35
+ @abc.abstractmethod
36
+ def match(self, s: str) -> bool:
37
+ raise NotImplementedError
38
+
39
+ @abc.abstractmethod
40
+ def split(self, s: str) -> list[str]:
41
+ """Returns lowercase."""
42
+
43
+ raise NotImplementedError
44
+
45
+ @abc.abstractmethod
46
+ def join(self, *parts: str) -> str:
47
+ """Expects lowercase."""
48
+
49
+ raise NotImplementedError
50
+
51
+
52
+ #
53
+
54
+
55
+ class CamelCase(StringCasing):
56
+ """FooBarBaz"""
57
+
58
+ _PAT: ta.ClassVar[re.Pattern] = re.compile(r'[A-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)*')
59
+ _SPLIT_PAT: ta.ClassVar[re.Pattern] = re.compile(r'[A-Z][a-z0-9]*')
60
+
61
+ def match(self, s: str) -> bool:
62
+ return bool(self._PAT.fullmatch(s))
63
+
64
+ def split(self, s: str) -> list[str]:
65
+ if not self.match(s):
66
+ raise ImproperStringCasingError(f'Not valid CamelCase: {s!r}')
67
+ return [m.group(0).lower() for m in self._SPLIT_PAT.finditer(s)]
68
+
69
+ def join(self, *parts: str) -> str:
70
+ _check_all_lowercase(*parts)
71
+ return ''.join(p.capitalize() for p in parts)
72
+
73
+
74
+ #
75
+
76
+
77
+ class LowCamelCase(StringCasing):
78
+ """fooBarBaz"""
79
+
80
+ _MATCH_PAT: ta.ClassVar[re.Pattern] = re.compile(r'[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*')
81
+ _SPLIT_PAT: ta.ClassVar[re.Pattern] = re.compile(r'^[a-z0-9]+')
82
+ _UPPER_PAT: ta.ClassVar[re.Pattern] = re.compile(r'[A-Z][a-z0-9]*')
83
+
84
+ def match(self, s: str) -> bool:
85
+ return bool(self._MATCH_PAT.fullmatch(s))
86
+
87
+ def split(self, s: str) -> list[str]:
88
+ if not self.match(s):
89
+ raise ImproperStringCasingError(f'Not valid lowCamelCase: {s!r}')
90
+ parts: list[str] = []
91
+ m0 = self._SPLIT_PAT.match(s)
92
+ if m0:
93
+ parts.append(m0.group(0))
94
+ start = m0.end()
95
+ else:
96
+ start = 0
97
+ for m in self._UPPER_PAT.finditer(s, pos=start):
98
+ parts.append(m.group(0))
99
+ return [p.lower() for p in parts]
100
+
101
+ def join(self, *parts: str) -> str:
102
+ _check_all_lowercase(*parts)
103
+ if not parts:
104
+ return ''
105
+ first, *rest = parts
106
+ return first.lower() + ''.join(p.capitalize() for p in rest)
107
+
108
+
109
+ #
110
+
111
+
112
+ class SnakeCase(StringCasing):
113
+ """foo_bar_baz"""
114
+
115
+ _PAT: ta.ClassVar[re.Pattern] = re.compile(r'[a-z0-9]+(?:_[a-z0-9]+)*')
116
+
117
+ def match(self, s: str) -> bool:
118
+ return bool(self._PAT.fullmatch(s))
119
+
120
+ def split(self, s: str) -> list[str]:
121
+ if not self.match(s):
122
+ raise ImproperStringCasingError(f'Not valid snake_case: {s!r}')
123
+ return s.split('_')
124
+
125
+ def join(self, *parts: str) -> str:
126
+ _check_all_lowercase(*parts)
127
+ return '_'.join(p.lower() for p in parts)
128
+
129
+
130
+ #
131
+
132
+
133
+ class UpSnakeCase(StringCasing):
134
+ """FOO_BAR_BAZ"""
135
+
136
+ _PAT: ta.ClassVar[re.Pattern] = re.compile(r'[A-Z0-9]+(?:_[A-Z0-9]+)*')
137
+
138
+ def match(self, s: str) -> bool:
139
+ return bool(self._PAT.fullmatch(s))
140
+
141
+ def split(self, s: str) -> list[str]:
142
+ if not self.match(s):
143
+ raise ImproperStringCasingError(f'Not valid UPPER_SNAKE_CASE: {s!r}')
144
+ return [part.lower() for part in s.split('_')]
145
+
146
+ def join(self, *parts: str) -> str:
147
+ _check_all_lowercase(*parts)
148
+ return '_'.join(p.upper() for p in parts)
149
+
150
+
151
+ ##
152
+
153
+
154
+ STRING_CASINGS = [
155
+ (CAMEL_CASE := CamelCase()),
156
+ (LOW_CAMEL_CASE := LowCamelCase()),
157
+ (SNAKE_CASE := SnakeCase()),
158
+ (UP_SNAKE_CASE := UpSnakeCase()),
159
+ ]
160
+
161
+
162
+ camel_case = CAMEL_CASE.join
163
+ low_camel_case = LOW_CAMEL_CASE.join
164
+ snake_case = SNAKE_CASE.join
165
+ up_snake_case = UP_SNAKE_CASE.join
166
+
167
+
168
+ ##
169
+
170
+
171
+ def get_string_casing(s: str, casings: ta.Iterable[StringCasing] | None = None) -> StringCasing:
172
+ if casings is None:
173
+ casings = STRING_CASINGS
174
+ cs = [c for c in casings if c.match(s)]
175
+ if not cs:
176
+ raise UnknownStringCasingError
177
+ if len(cs) != 1:
178
+ raise AmbiguousStringCasingError
179
+ [c] = cs
180
+ return c
181
+
182
+
183
+ def split_string_casing(s: str, casings: ta.Iterable[StringCasing] | None = None) -> list[str]:
184
+ if casings is None:
185
+ casings = STRING_CASINGS
186
+ ts = {tuple(c.split(s)) for c in casings if c.match(s)}
187
+ if not ts:
188
+ raise UnknownStringCasingError
189
+ if len(ts) != 1:
190
+ raise AmbiguousStringCasingError
191
+ [t] = ts
192
+ return list(t)
omlish/lang/strings.py CHANGED
@@ -84,23 +84,6 @@ def rfind_any(
84
84
  ##
85
85
 
86
86
 
87
- def camel_case(name: str, *, lower: bool = False) -> str:
88
- if not name:
89
- return ''
90
- s = ''.join(map(str.capitalize, name.split('_'))) # noqa
91
- if lower:
92
- s = s[0].lower() + s[1:]
93
- return s
94
-
95
-
96
- def snake_case(name: str) -> str:
97
- uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
98
- return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
99
-
100
-
101
- ##
102
-
103
-
104
87
  def is_dunder(name: str) -> bool:
105
88
  return (
106
89
  name[:2] == name[-2:] == '__' and
omlish/marshal/naming.py CHANGED
@@ -5,6 +5,7 @@ TODO:
5
5
  """
6
6
  import enum
7
7
 
8
+ from .. import check
8
9
  from .. import lang
9
10
  from .base import Option
10
11
 
@@ -16,11 +17,22 @@ class Naming(Option, enum.Enum):
16
17
 
17
18
 
18
19
  def translate_name(n: str, e: Naming) -> str:
20
+ check.non_empty_str(n)
21
+ check.not_equal(set(n), {'_'})
22
+
23
+ n1 = n.lstrip('_')
24
+ pfx = '_' * (len(n) - len(n1))
25
+ n2 = n1.rstrip('_')
26
+ sfx = '_' * (len(n1) - len(n2))
27
+ ps = lang.split_string_casing(n2)
28
+
19
29
  if e is Naming.SNAKE:
20
- return lang.snake_case(n)
21
- if e is Naming.CAMEL:
22
- return lang.camel_case(n)
23
- if e is Naming.LOW_CAMEL:
24
- r = lang.camel_case(n)
25
- return (r[0].lower() + r[1:]) if r else r
26
- raise ValueError(e)
30
+ r = lang.snake_case(*ps)
31
+ elif e is Naming.CAMEL:
32
+ r = lang.camel_case(*ps)
33
+ elif e is Naming.LOW_CAMEL:
34
+ r = lang.low_camel_case(*ps)
35
+ else:
36
+ raise ValueError(e)
37
+
38
+ return pfx + r + sfx
@@ -104,7 +104,7 @@ def _is_actual_number(x: ta.Any) -> bool:
104
104
 
105
105
 
106
106
  def node_type(n: Node) -> str:
107
- return lang.snake_case(type(n).__name__)
107
+ return lang.snake_case(*lang.split_string_casing(type(n).__name__))
108
108
 
109
109
 
110
110
  ##
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev302
3
+ Version: 0.0.0.dev303
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=pjGUyLHaoWpPqRP3jz2u1fC1qoRc2lvrEcpU_Ax2tdg,8253
2
- omlish/__about__.py,sha256=VEC7wFGaSoAMF2E62PWgQVju9B2RuX6O49UlGrjLBRo,3478
2
+ omlish/__about__.py,sha256=ZtA9R7gsXvmQ5vqquGdnyiA7g3hwkTA_jGG19gRDrkI,3478
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -132,7 +132,7 @@ omlish/bootstrap/__init__.py,sha256=-Rtsg7uPQNhh1dIT9nqrz96XlqizwoLnWf-FwOEstJI,
132
132
  omlish/bootstrap/__main__.py,sha256=4jCwsaogp0FrJjJZ85hzF4-WqluPeheHbfeoKynKvNs,194
133
133
  omlish/bootstrap/base.py,sha256=d8hqn4hp1XMMi5PgcJBQXPKmW47epu8CxBlqDZiRZb4,1073
134
134
  omlish/bootstrap/diag.py,sha256=iemH0nQEHEDzyZztvd_ygGGVpRpgn5UG6naxeQTvXp0,5347
135
- omlish/bootstrap/harness.py,sha256=VW8YP-yENGyXIuJ8GL_xintArF13nafwpz-iAghPt34,1967
135
+ omlish/bootstrap/harness.py,sha256=BMFNTYG03V74WFW5zd6aaxIb6uzGeldiR3VX2alQwlU,1994
136
136
  omlish/bootstrap/main.py,sha256=yZhOHDDlj4xB5a89dRdT8z58FsqqnpoBg1-tvY2CJe4,5903
137
137
  omlish/bootstrap/marshal.py,sha256=mH6KVQILEG7Qb_mULNRe8DGDSZb99mQr9jve4mhqUfc,481
138
138
  omlish/bootstrap/sys.py,sha256=0F0uThMsYdjqUtzrYHr4Xsh_MjscxgWl149i_3tDOqo,8787
@@ -464,8 +464,9 @@ omlish/iterators/iterators.py,sha256=RxW35yQ5ed8vBQ22IqpDXFx-i5JiLQdp7-pkMZXhJJ8
464
464
  omlish/iterators/recipes.py,sha256=wOwOZg-zWG9Zc3wcAxJFSe2rtavVBYwZOfG09qYEx_4,472
465
465
  omlish/iterators/tools.py,sha256=c4hArZEVV8y9_dFfmRwakusv1cWJLT4MkTkGRjnGN5U,2556
466
466
  omlish/iterators/unique.py,sha256=Nw0pSaNEcHAkve0ugfLPvJcirDOn9ECyC5wIL8JlJKI,1395
467
- omlish/lang/__init__.py,sha256=KCzbT9IMWi2TNldAlDZ6b8Rr-7hyV9Ne6kIKJLXNAxg,5315
467
+ omlish/lang/__init__.py,sha256=1m2Kc4CBk_C_B7ydPcGWTdCY00JRMrGtX_RlFvyVTo8,5729
468
468
  omlish/lang/attrs.py,sha256=i7euRF81uNF8QDmUVXSK_BtqLGshaMi4VVdUnMjiMwg,5050
469
+ omlish/lang/casing.py,sha256=hMnWMBv-yP4tr8Rc9_An9IgcAsbtVcEb2D_7n8TaFgw,4636
469
470
  omlish/lang/clsdct.py,sha256=HAGIvBSbCefzRjXriwYSBLO7QHKRv2UsE78jixOb-fA,1828
470
471
  omlish/lang/collections.py,sha256=LVm0Sory60IXyFzYhhO8BZAWy_z_pjiA-meXNlSJP7o,2465
471
472
  omlish/lang/comparison.py,sha256=MOwEG0Yny-jBPHO9kQto9FSRyeNpQW24UABsghkrHxY,1356
@@ -483,7 +484,7 @@ omlish/lang/outcomes.py,sha256=mpFy_VoM-b74L1aCFsjsZVUHx_icZ1AHMOKeVesjOp4,8628
483
484
  omlish/lang/params.py,sha256=QmNVBfJsfxjDG5ilDPgHV7sK4UwRztkSQdLTo0umb8I,6648
484
485
  omlish/lang/resolving.py,sha256=ei9LDyJexsMMHB9z8diUkNmynWhd_da7h7TqrMYM6lA,1611
485
486
  omlish/lang/resources.py,sha256=WKkAddC3ctMK1bvGw-elGe8ZxAj2IaUTKVSu2nfgHTo,2839
486
- omlish/lang/strings.py,sha256=egdv8PxLNG40-5V93agP5j2rBUDIsahCx048zV7uEbU,4690
487
+ omlish/lang/strings.py,sha256=kJmRFd1D36xXcjd9MdB12BCwF_-MVhNr-TpWj7hMi_4,4252
487
488
  omlish/lang/sys.py,sha256=b4qOPiJZQru_mbb04FNfOjYWUxlV2becZOoc-yya_rQ,411
488
489
  omlish/lang/typing.py,sha256=Zdad9Zv0sa-hIaUXPrzPidT7sDVpRcussAI7D-j-I1c,3296
489
490
  omlish/lang/cached/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -550,7 +551,7 @@ omlish/marshal/base.py,sha256=Q0ZRsz5z0NTI6PeWPc9mdMstJryDDbeIAdpKH9-SDps,11427
550
551
  omlish/marshal/errors.py,sha256=g5XJyTHd__8lfwQ4KwgK-E5WR6MoNTMrqKP2U_QRQQQ,307
551
552
  omlish/marshal/factories.py,sha256=Q926jSVjaQLEmStnHLhm_c_vqEysN1LnDCwAsFLIzXw,2970
552
553
  omlish/marshal/global_.py,sha256=sdBC6PbP77iUAYZ9GIYyAFrbmg14bp6iU3f1l5tpOSE,1378
553
- omlish/marshal/naming.py,sha256=lIklR_Od4x1ghltAgOzqcKhHs-leeSv2YmFhCHO7GIs,613
554
+ omlish/marshal/naming.py,sha256=7jQ204u_Kpc3-OGr-ctUHSv997DdWYRLh643qLHJhks,852
554
555
  omlish/marshal/proxy.py,sha256=puKJpwPpuDlMOIrKMcLTRLJyMiL6n_Xs-p59AuDEymA,543
555
556
  omlish/marshal/registries.py,sha256=FvC6qXHCizNB2QmU_N3orxW7iqfGYkiUXYYdTRWS6HA,2353
556
557
  omlish/marshal/standard.py,sha256=TADR75Hd9VSUpuN_Tnu74Bzmvby42P2bzwFwUBC0l-Q,4283
@@ -679,7 +680,7 @@ omlish/specs/jmespath/functions.py,sha256=YnuwlgkcbUJWlqOvSpN3LGXZpF0fpImKa--FLk
679
680
  omlish/specs/jmespath/lexer.py,sha256=WGxkwQe_dcHWcJcGg9q6K-8_Q0oRdWkw09dYGFNTHbk,12639
680
681
  omlish/specs/jmespath/parser.py,sha256=yfkydotVR4LBhrUTsptL_kLYDoGZrRN9zSEs_76kvZM,24441
681
682
  omlish/specs/jmespath/scope.py,sha256=UyDsl9rv_c8DCjJBuVIA2ESu1jrgYvuwEKiaJDQKnT0,1590
682
- omlish/specs/jmespath/visitor.py,sha256=4jCK_WDf7mBwxAZY2AKAcLOJICQZCSL7Gny8-IplicA,16565
683
+ omlish/specs/jmespath/visitor.py,sha256=HVro_6aBGL0CMBy8NRy6vJzWgwsHGB1qJXldX8H7wSg,16592
683
684
  omlish/specs/jsonrpc/__init__.py,sha256=QQwr-jkgvwr1ZMlNwl5W1TuHcxx8RuzQVFwWwNhp5sM,515
684
685
  omlish/specs/jsonrpc/errors.py,sha256=-Zgmlo6bV6J8w5f8h9axQgLquIFBHDgIwcpufEH5NsE,707
685
686
  omlish/specs/jsonrpc/marshal.py,sha256=HM736piPGnBZrg8CMLDX-L5fZpegyF6l6JUjzLoSDtk,1852
@@ -843,9 +844,9 @@ omlish/typedvalues/holder.py,sha256=ZTnHiw-K38ciOBLEdwgrltr7Xp8jjEs_0Lp69DH-G-o,
843
844
  omlish/typedvalues/marshal.py,sha256=hWHRLcrGav7lvXJDtb9bNI0ickl4SKPQ6F4BbTpqw3A,4219
844
845
  omlish/typedvalues/reflect.py,sha256=Ih1YgU-srUjsvBn_P7C66f73_VCvcwqE3ffeBnZBgt4,674
845
846
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
846
- omlish-0.0.0.dev302.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
847
- omlish-0.0.0.dev302.dist-info/METADATA,sha256=ARPvC1-WLWPuw1QpgF2xijl-9JRU8BHBzFVsFadjAfg,4416
848
- omlish-0.0.0.dev302.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
849
- omlish-0.0.0.dev302.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
850
- omlish-0.0.0.dev302.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
851
- omlish-0.0.0.dev302.dist-info/RECORD,,
847
+ omlish-0.0.0.dev303.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
848
+ omlish-0.0.0.dev303.dist-info/METADATA,sha256=Oe8ylkuWV_TTJtgQkuyG97boKvpRe_Z0O8At2xQ97SU,4416
849
+ omlish-0.0.0.dev303.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
850
+ omlish-0.0.0.dev303.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
851
+ omlish-0.0.0.dev303.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
852
+ omlish-0.0.0.dev303.dist-info/RECORD,,