minor-utils 0.1.0__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.
@@ -0,0 +1,16 @@
1
+ __title__ = "minor_utils"
2
+ __version__ = "0.1.0"
3
+ __author__ = "Gleb Minor"
4
+ __license__ = "MIT"
5
+
6
+ from .formatter import Formatter
7
+ from .errors import MissingStringError, InvalidArgumentError, FormatAlreadyExistsError, AliasAlreadyExistsError, DefaultFormatModificationError
8
+
9
+ __all__ = [
10
+ "Formatter",
11
+ "MissingStringError",
12
+ "InvalidArgumentError",
13
+ "FormatAlreadyExistsError",
14
+ "AliasAlreadyExistsError",
15
+ "DefaultFormatModificationError"
16
+ ]
minor_utils/errors.py ADDED
@@ -0,0 +1,14 @@
1
+ class InvalidArgumentError(ValueError):
2
+ pass
3
+
4
+ class MissingStringError(ValueError):
5
+ pass
6
+
7
+ class FormatAlreadyExistsError(Exception):
8
+ pass
9
+
10
+ class AliasAlreadyExistsError(FormatAlreadyExistsError):
11
+ pass
12
+
13
+ class DefaultFormatModificationError(Exception):
14
+ pass
@@ -0,0 +1,558 @@
1
+ from inspect import signature
2
+
3
+ # Errors
4
+ from .errors import DefaultFormatModificationError, MissingStringError, InvalidArgumentError, FormatAlreadyExistsError, AliasAlreadyExistsError
5
+
6
+ class Formatter:
7
+ #--------(Format functions)--------
8
+
9
+ @staticmethod
10
+ def cap(s):
11
+ """Capitalize the first character of the string and make the rest lowercase."""
12
+ return s.capitalize()
13
+
14
+ @staticmethod
15
+ def up(s):
16
+ """Convert all characters in the string to uppercase."""
17
+ return s.upper()
18
+
19
+ @staticmethod
20
+ def down(s):
21
+ """Convert all characters in the string to lowercase."""
22
+ return s.lower()
23
+
24
+ @staticmethod
25
+ def spike(s):
26
+ """Convert characters in the string to alternating uppercase and lowercase, starting with uppercase."""
27
+ s = s.lower()
28
+ return ''.join(
29
+ char.upper() if i % 2 == 0 else char
30
+ for i, char in enumerate(s))
31
+
32
+ @staticmethod
33
+ def clean(s):
34
+ """Remove extra spaces from the string, leaving only single spaces between words."""
35
+ words = s.split()
36
+ return ' '.join(words)
37
+
38
+ @staticmethod
39
+ def camel_case(s):
40
+ """Convert the string to camelCase."""
41
+ words = s.lower().split()
42
+ return words[0] + ''.join(
43
+ word.capitalize()
44
+ for word in words[1:])
45
+
46
+ @staticmethod
47
+ def snake_case(s):
48
+ """Convert the string to snake_case."""
49
+ words = s.lower().split()
50
+ return '_'.join(words)
51
+
52
+ @staticmethod
53
+ def pascal_case(s):
54
+ """Convert the string to PascalCase."""
55
+ words = s.split()
56
+ return ''.join(
57
+ word.capitalize()
58
+ for word in words)
59
+
60
+ @staticmethod
61
+ def upper_snake_case(s):
62
+ """Convert the string to UPPER_SNAKE_CASE."""
63
+ words = s.upper().split()
64
+ return '_'.join(words)
65
+
66
+ @staticmethod
67
+ def no_spaces(s):
68
+ """Remove all spaces from the string."""
69
+ return s.replace(' ', '')
70
+
71
+ #--------(Formats)--------
72
+
73
+ formats = {
74
+ "capitalize": cap,
75
+ "up": up,
76
+ "down": down,
77
+ "spike": spike,
78
+ "clean": clean,
79
+ "camelCase": camel_case,
80
+ "snake_case": snake_case,
81
+ "PascalCase": pascal_case,
82
+ "UPPER_SNAKE_CASE": upper_snake_case,
83
+ "nospaces": no_spaces
84
+ }
85
+
86
+ default_formats = formats.copy()
87
+ custom_formats = {}
88
+ format_aliases = {}
89
+ enabled_formats = list(formats.keys()) + list(format_aliases.keys())
90
+
91
+ #--------(Formatter function)--------
92
+
93
+ @classmethod
94
+ def get_callable(cls, string_format):
95
+ if string_format in cls.format_aliases:
96
+ func = cls.format_aliases[string_format]
97
+ elif string_format in cls.formats:
98
+ func = cls.formats[string_format]
99
+ else:
100
+ raise InvalidArgumentError(
101
+ f"Invalid string format: '{string_format}'. "
102
+ f"Expected one of: {', '.join(cls.formats)}."
103
+ )
104
+
105
+ if string_format not in cls.enabled_formats:
106
+ raise InvalidArgumentError(
107
+ f"Format '{string_format}' is currently disabled. "
108
+ f"Enabled formats: {', '.join(cls.enabled_formats)}."
109
+ )
110
+
111
+ return func
112
+
113
+ @classmethod
114
+ def formatting(cls,
115
+ string: str,
116
+ string_format: str,
117
+ inverse: bool = False,
118
+ reverse: bool = False
119
+ ) -> str:
120
+
121
+ """
122
+ Format string using specified formatting style.
123
+
124
+ Args:
125
+ string: Input string.
126
+ string_format: Formatting style.
127
+ inverse: Invert character case.
128
+ reverse: Reverse output string.
129
+
130
+ Returns:
131
+ Formatted string.
132
+ """
133
+
134
+ string = cls._validate(string)
135
+
136
+ func = cls.get_callable(string_format)
137
+
138
+ output = func(string)
139
+
140
+ if inverse:
141
+ output = output.swapcase()
142
+
143
+ if reverse:
144
+ output = output[::-1]
145
+
146
+ return output
147
+
148
+ #--------(Pipeline API)--------
149
+ @classmethod
150
+ def pipe(cls, string, *formats, inversed=False, reversed=False):
151
+ """
152
+ Apply multiple formats to a string in sequence.
153
+
154
+ Args:
155
+ string: Input string.
156
+ *formats: Sequence of format names to apply.
157
+ """
158
+
159
+ for fmt in formats:
160
+ string = cls.formatting(string, fmt, inverse=inversed, reverse=reversed)
161
+ return string
162
+
163
+ #--------(Registry API)--------
164
+ @classmethod
165
+ def add_format(cls, func=None, name=None, *aliases):
166
+ def decorator(f):
167
+ cls.validate_format_function(f)
168
+
169
+ format_name = (
170
+ name
171
+ if name is not None
172
+ else f.__name__
173
+ )
174
+
175
+ if cls.is_existing_format(format_name):
176
+ raise FormatAlreadyExistsError(
177
+ f"Unable to add format: format '{format_name}' already exists."
178
+ )
179
+
180
+ if cls.is_existing_alias(format_name):
181
+ raise AliasAlreadyExistsError(
182
+ f"Unable to add format: '{format_name}' is already an alias."
183
+ )
184
+
185
+ if format_name in list(cls.default_formats):
186
+ raise DefaultFormatModificationError(
187
+ f"Unable to add format: '{format_name}' is a default format.")
188
+
189
+ cls.formats[format_name] = f
190
+ cls.custom_formats[format_name] = f
191
+ cls.enabled_formats.append(format_name)
192
+
193
+ if aliases:
194
+ for alias in aliases:
195
+ if cls.is_existing_alias(alias):
196
+ raise AliasAlreadyExistsError(
197
+ f"Unable to add alias: alias '{alias}' already exists."
198
+ )
199
+ cls.format_aliases[alias] = f
200
+ cls.enabled_formats.append(alias)
201
+
202
+ return f
203
+
204
+ if func is None:
205
+ return decorator
206
+
207
+ return decorator(func)
208
+
209
+ @classmethod
210
+ def replace_format(cls, name, func):
211
+ cls.validate_format_function(func)
212
+
213
+ if not cls.is_existing_format(name):
214
+ raise InvalidArgumentError(
215
+ f"Unable to replace format: format '{name}' does not exist."
216
+ )
217
+
218
+ if name in cls.default_formats:
219
+ raise DefaultFormatModificationError(
220
+ f"Unable to replace format: '{name}' is a default format."
221
+ )
222
+
223
+ cls.formats[name] = func
224
+
225
+ @classmethod
226
+ def remove_format(cls, name):
227
+ if not cls.is_existing_format(name):
228
+ raise InvalidArgumentError(
229
+ f"Unable to remove format: format '{name}' does not exist or is alias."
230
+ )
231
+
232
+ cls.del_aliases_of(name)
233
+
234
+ if name in cls.enabled_formats:
235
+ cls.enabled_formats.remove(name)
236
+
237
+ cls.formats.pop(name, None)
238
+ cls.custom_formats.pop(name, None)
239
+
240
+ @classmethod
241
+ def reset_formats(cls):
242
+ cls.clear_aliases()
243
+ cls.clear_custom_formats()
244
+ cls.formats = cls.default_formats.copy()
245
+
246
+ @classmethod
247
+ def restore_format(cls, name):
248
+ if name not in cls.default_formats:
249
+ raise InvalidArgumentError(
250
+ f"Unable to restore format: format '{name}' is not a default format."
251
+ )
252
+
253
+ if name in cls.custom_formats:
254
+ raise FormatAlreadyExistsError(
255
+ f"Unable to restore format: format '{name}' already exists as a custom format."
256
+ )
257
+
258
+ cls.formats[name] = cls.default_formats[name]
259
+ cls.custom_formats.pop(name, None)
260
+ if name not in cls.enabled_formats:
261
+ cls.enabled_formats.append(name)
262
+
263
+ @classmethod
264
+ def clear_formats(cls):
265
+ cls.formats.clear()
266
+ cls.custom_formats.clear()
267
+ cls.enabled_formats.clear()
268
+ cls.format_aliases.clear()
269
+
270
+ @classmethod
271
+ def clear_default_formats(cls):
272
+ for name in list(cls.default_formats):
273
+ cls.del_aliases_of(name)
274
+
275
+ if name in cls.enabled_formats:
276
+ cls.enabled_formats.remove(name)
277
+
278
+ cls.formats.pop(name, None)
279
+
280
+ @classmethod
281
+ def clear_custom_formats(cls):
282
+ for name in list(cls.custom_formats):
283
+ cls.del_aliases_of(name)
284
+
285
+ if name in cls.enabled_formats:
286
+ cls.enabled_formats.remove(name)
287
+
288
+ cls.formats.pop(name, None)
289
+
290
+ cls.custom_formats.clear()
291
+
292
+ @classmethod
293
+ def get_formats(cls):
294
+ return cls.formats
295
+
296
+ @classmethod
297
+ def get_default_formats(cls):
298
+ return {
299
+ name: cls.formats[name]
300
+ for name in cls.formats
301
+ if name in cls.default_formats
302
+ }
303
+
304
+ @classmethod
305
+ def get_custom_formats(cls):
306
+ return cls.custom_formats
307
+
308
+ @classmethod
309
+ def get_builtin_formats(cls):
310
+ return cls.default_formats
311
+
312
+ @classmethod
313
+ def get_format_function(cls, name):
314
+ if not cls.is_existing_format(name):
315
+ raise InvalidArgumentError(
316
+ f"Format '{name}' does not exist. Available formats: {', '.join(cls.formats)}."
317
+ )
318
+
319
+ return cls.formats[name]
320
+
321
+ @classmethod
322
+ def names_of(cls, func):
323
+ names = []
324
+ for name, f in cls.formats.items():
325
+ if f is func:
326
+ names.append(name)
327
+ if names:
328
+ return names
329
+ raise InvalidArgumentError(
330
+ f"Format {func.__name__} not found."
331
+ )
332
+
333
+ @classmethod
334
+ def get_format_info(cls, name):
335
+ if not cls.is_existing_format(name):
336
+ raise InvalidArgumentError(
337
+ f"Format '{name}' does not exist. "
338
+ f"Available formats: {', '.join(cls.formats)}."
339
+ )
340
+
341
+ func = cls.formats[name]
342
+ return {
343
+ "name": name,
344
+ "function": func,
345
+ "doc": func.__doc__
346
+ }
347
+
348
+ @classmethod
349
+ def get_format_doc(cls, name):
350
+ return cls.get_format_function(name).__doc__
351
+
352
+ @classmethod
353
+ def rename_format(cls, old_name, new_name):
354
+ if not cls.is_existing_format(old_name):
355
+ raise InvalidArgumentError(
356
+ f"Unable to rename format: format '{old_name}' does not exist."
357
+ )
358
+
359
+ if cls.is_existing_format(new_name):
360
+ raise FormatAlreadyExistsError(
361
+ f"Unable to rename format: format '{new_name}' already exists."
362
+ )
363
+
364
+ if cls.is_existing_alias(new_name):
365
+ raise FormatAlreadyExistsError(
366
+ f"Unable to rename format: alias '{new_name}' already exists."
367
+ )
368
+
369
+ if new_name in cls.default_formats:
370
+ raise DefaultFormatModificationError(
371
+ f"Unable to replace format: '{new_name}' is a default format."
372
+ )
373
+
374
+ aliases = list(cls.aliases_of(old_name))
375
+
376
+ func = cls.formats.pop(old_name)
377
+
378
+ cls.formats[new_name] = func
379
+
380
+ if old_name in cls.custom_formats:
381
+ cls.custom_formats[new_name] = cls.custom_formats.pop(old_name)
382
+
383
+ if old_name in cls.enabled_formats:
384
+ cls.enabled_formats.remove(old_name)
385
+ cls.enabled_formats.append(new_name)
386
+
387
+ for alias in aliases:
388
+ cls.format_aliases[alias] = func
389
+
390
+ @classmethod
391
+ def has_format(cls, name):
392
+ return cls.is_existing_format(name)
393
+
394
+ @classmethod
395
+ def has_alias(cls, name):
396
+ return cls.is_existing_alias(name)
397
+
398
+ @classmethod
399
+ def disable(cls, name):
400
+ if not cls.is_existing_format(name):
401
+ raise InvalidArgumentError(
402
+ f"Unable to disable format: format '{name}' does not exist."
403
+ )
404
+
405
+ aliases = list(cls.aliases_of(name))
406
+
407
+ for alias in aliases:
408
+ if alias in cls.enabled_formats:
409
+ cls.enabled_formats.remove(alias)
410
+
411
+ if name in cls.enabled_formats:
412
+ cls.enabled_formats.remove(name)
413
+
414
+ @classmethod
415
+ def enable(cls, name):
416
+ if not cls.is_existing_format(name):
417
+ raise InvalidArgumentError(
418
+ f"Unable to enable format: format '{name}' does not exist."
419
+ )
420
+
421
+ aliases = list(cls.aliases_of(name))
422
+ if not cls.is_enabled(name):
423
+ cls.enabled_formats.append(name)
424
+ for alias in aliases:
425
+ if not cls.is_enabled(alias):
426
+ cls.enabled_formats.append(alias)
427
+
428
+ @classmethod
429
+ def toggle(cls, name):
430
+ if not cls.is_existing_format(name):
431
+ raise InvalidArgumentError(
432
+ f"Unable to toggle format: format '{name}' does not exist."
433
+ )
434
+
435
+ aliases = list(cls.aliases_of(name))
436
+ if cls.is_enabled(name):
437
+ cls.enabled_formats.remove(name)
438
+
439
+ for alias in aliases:
440
+ if alias in cls.enabled_formats:
441
+ cls.enabled_formats.remove(alias)
442
+ else:
443
+ cls.enabled_formats.append(name)
444
+
445
+ for alias in aliases:
446
+ if alias not in cls.enabled_formats:
447
+ cls.enabled_formats.append(alias)
448
+
449
+ @classmethod
450
+ def is_enabled(cls, name):
451
+ return name in cls.enabled_formats
452
+
453
+
454
+
455
+ #--------(Validation API)--------
456
+ @classmethod
457
+ def _validate(cls, s):
458
+ s = s.strip()
459
+ if not s:
460
+ raise MissingStringError(
461
+ "Input string is empty or contains only whitespace."
462
+ )
463
+ return s
464
+
465
+ @classmethod
466
+ def is_existing_format(cls, name):
467
+ return name in cls.formats
468
+
469
+ @classmethod
470
+ def is_existing_alias(cls, name):
471
+ return name in cls.format_aliases
472
+
473
+ @classmethod
474
+ def validate_format_function(cls, func):
475
+ sig = signature(func)
476
+
477
+ if len(sig.parameters) != 1:
478
+ raise InvalidArgumentError("Format function must accept exactly one argument.")
479
+
480
+ #--------(Alias API)--------
481
+
482
+ @classmethod
483
+ def alias(cls, existing_name, alias_name):
484
+ if not cls.is_existing_format(existing_name):
485
+ raise InvalidArgumentError(
486
+ f"Unable to create alias: format '{existing_name}' does not exist."
487
+ )
488
+
489
+ if cls.is_existing_alias(alias_name):
490
+ temp = cls.format_aliases[alias_name]
491
+ format_name = next(
492
+ n
493
+ for n, f in cls.formats.items()
494
+ if f is temp
495
+ )
496
+ raise AliasAlreadyExistsError(
497
+ f"Unable to create alias: alias '{alias_name}' already exists for format '{format_name}'."
498
+ )
499
+
500
+ cls.format_aliases[alias_name] = cls.formats[existing_name]
501
+
502
+ if alias_name not in cls.enabled_formats:
503
+ cls.enabled_formats.append(alias_name)
504
+
505
+ @classmethod
506
+ def del_alias(cls, alias_name):
507
+ if not cls.is_existing_alias(alias_name):
508
+ raise InvalidArgumentError(
509
+ f"Unable to delete alias: alias '{alias_name}' does not exist."
510
+ )
511
+
512
+ if alias_name in cls.enabled_formats:
513
+ cls.enabled_formats.remove(alias_name)
514
+
515
+ del cls.format_aliases[alias_name]
516
+
517
+ @classmethod
518
+ def del_aliases_of(cls, existing_name):
519
+ if not cls.is_existing_format(existing_name):
520
+ raise InvalidArgumentError(
521
+ f"Unable to delete alias: format '{existing_name}' does not exist."
522
+ )
523
+
524
+ aliases_to_delete = [
525
+ name
526
+ for name, func in cls.format_aliases.items()
527
+ if func is cls.formats[existing_name]
528
+ ]
529
+
530
+ for alias in aliases_to_delete:
531
+ if alias in cls.enabled_formats:
532
+ cls.enabled_formats.remove(alias)
533
+
534
+ del cls.format_aliases[alias]
535
+
536
+ @classmethod
537
+ def clear_aliases(cls):
538
+ for alias in list(cls.format_aliases):
539
+ if alias in cls.enabled_formats:
540
+ cls.enabled_formats.remove(alias)
541
+ del cls.format_aliases[alias]
542
+
543
+ @classmethod
544
+ def aliases_of(cls, existing_name):
545
+ if not cls.is_existing_format(existing_name):
546
+ raise InvalidArgumentError(
547
+ f"Unable to list aliases: format '{existing_name}' does not exist."
548
+ )
549
+
550
+ return {
551
+ name: func
552
+ for name, func in cls.format_aliases.items()
553
+ if func is cls.formats[existing_name] and name != existing_name
554
+ }
555
+
556
+ @classmethod
557
+ def get_all_aliases(cls):
558
+ return cls.format_aliases
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: minor_utils
3
+ Version: 0.1.0
4
+ Summary: Utils compilation for easier work
5
+ Author-email: CyberAxolotl <minor.gleb@outlook.com>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+
10
+ # minor-utils
11
+
12
+ Utility library for formatting and helper functions.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install minor-utils
18
+
19
+ ## Note
20
+
21
+ The project is NOT final! It will be updated, new content will be added. There might be some bugs and information about the project is incomplete. You can use it now, but be careful. I recommend not to use it right now and wait untill it is finished.
@@ -0,0 +1,7 @@
1
+ minor_utils/__init__.py,sha256=0aL0mbFVCWP8txHe_yoOfsT_UHCPWCfdhXctBfnsFv4,472
2
+ minor_utils/errors.py,sha256=8KfSXCygL-6oixIKkk3cUfJupHgtb1OAoDz_Y3Lrz9M,288
3
+ minor_utils/formatter.py,sha256=8h-ZG9vLTxWYtI-HR8OCU_XX4mJlFOosPN1wcAAJnWU,17085
4
+ minor_utils-0.1.0.dist-info/METADATA,sha256=2VqRwcxTPXq9CFySPDnfnJmY69wbV1Uec92JGqkrTUU,639
5
+ minor_utils-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ minor_utils-0.1.0.dist-info/top_level.txt,sha256=8PmUO9RMANwObmJRtWm-C3SNJkcVtIAsXjV07eH3EQ4,12
7
+ minor_utils-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ minor_utils