invesytoolbox 0.0.22__tar.gz → 0.0.23__tar.gz

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.
Files changed (29) hide show
  1. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/HISTORY.md +9 -0
  2. {invesytoolbox-0.0.22/src/invesytoolbox.egg-info → invesytoolbox-0.0.23}/PKG-INFO +10 -1
  3. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/setup.cfg +1 -1
  4. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_data.py +92 -19
  5. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_date_time.py +11 -1
  6. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_html.py +1 -1
  7. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_text_name.py +50 -2
  8. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23/src/invesytoolbox.egg-info}/PKG-INFO +10 -1
  9. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_data.py +50 -9
  10. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_text_name.py +33 -0
  11. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/LICENSE.txt +0 -0
  12. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/README.md +0 -0
  13. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/setup.py +0 -0
  14. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/__init__.py +0 -0
  15. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_email_phone.py +0 -0
  16. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_locales.py +0 -0
  17. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_restricted_python.py +0 -0
  18. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_security.py +0 -0
  19. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox/itb_www.py +0 -0
  20. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox.egg-info/SOURCES.txt +0 -0
  21. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox.egg-info/dependency_links.txt +0 -0
  22. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox.egg-info/requires.txt +0 -0
  23. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/invesytoolbox.egg-info/top_level.txt +0 -0
  24. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/__init__.py +0 -0
  25. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_date_time.py +0 -0
  26. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_email_phone.py +0 -0
  27. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_html.py +0 -0
  28. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_locales.py +0 -0
  29. {invesytoolbox-0.0.22 → invesytoolbox-0.0.23}/src/tests/test_www.py +0 -0
@@ -1,4 +1,13 @@
1
1
  # History
2
+ ## 0.0.23 (2024-07-22)
3
+ * **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
4
+ * **value_2datatype**:
5
+ - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
6
+ - 'pendulum' is now an option for the parameter
7
+ - default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
8
+ - **dict_2datatypes**
9
+ - **dictlist_2datatypes**
10
+
2
11
  ## 0.0.22 (2023-10-14)
3
12
  * **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
4
13
  * **get_locale**: locale can be only the language, without the country.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: invesytoolbox
3
- Version: 0.0.22
3
+ Version: 0.0.23
4
4
  Summary: Tools for Python scripts or terminal
5
5
  Home-page: https://gitlab.com/Rastaf/invesytoolbox
6
6
  Author: Georg Pfolz
@@ -44,6 +44,15 @@ That's also why all date and time functions also take into account the old DateT
44
44
  The documentation can be found [here](https://rastaf.gitlab.io/invesytoolbox/).
45
45
 
46
46
  # History
47
+ ## 0.0.23 (2024-07-22)
48
+ * **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
49
+ * **value_2datatype**:
50
+ - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
51
+ - 'pendulum' is now an option for the parameter
52
+ - default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
53
+ - **dict_2datatypes**
54
+ - **dictlist_2datatypes**
55
+
47
56
  ## 0.0.22 (2023-10-14)
48
57
  * **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
49
58
  * **get_locale**: locale can be only the language, without the country.
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = invesytoolbox
3
- version = 0.0.22
3
+ version = 0.0.23
4
4
  author = Georg Pfolz
5
5
  author_email = georg.pfolz@invesy.at
6
6
  description = Tools for Python scripts or terminal
@@ -8,6 +8,7 @@ from typing import Union, List, Dict, Any, Optional
8
8
  import DateTime
9
9
  import datetime
10
10
  import vobject
11
+ import pendulum
11
12
  from dateutil.parser import parse
12
13
  from itb_date_time import convert_datetime, str_to_date, str_to_DT, get_dateformat
13
14
  from itb_email_phone import process_phonenumber
@@ -38,9 +39,7 @@ def dict_from_dict_list(
38
39
  single_value: Optional[bool] = None,
39
40
  include_key: Optional[bool] = False
40
41
  ) -> dict:
41
- """Create a dictionary from a list of dictionaries
42
-
43
- .. note:: The annotated return type is not true if a single_value argument is given.
42
+ """ Create a dictionary from a list of dictionaries
44
43
 
45
44
  :param dictList: the list of dictionaries to be converted
46
45
  :param key: the key of the dictionaries to use as key for the new dictionary.
@@ -70,7 +69,7 @@ def create_vcard(
70
69
  data: Dict[str, str],
71
70
  returning: str = 'vcard'
72
71
  ) -> str:
73
- """create a vCard from a dictionary
72
+ """ create a vCard from a dictionary
74
73
 
75
74
  .. note:: The serialized vcard has Windows line-breaks, which is fine, I guess
76
75
  .. note:: colons in notes are escaped in vobject
@@ -134,7 +133,7 @@ def dict_2unicode(
134
133
  d: Dict[Union[str, bytes], Any],
135
134
  encoding: str = 'utf-8'
136
135
  ) -> dict:
137
- """ Converts all keys and values in a dictionary from bytes to unicode
136
+ """ Convert all keys and values in a dictionary from bytes to unicode
138
137
 
139
138
  All keys and values are changed from bytes to unicode, if applicable.
140
139
  Other data types are left unchaged, including other compound data types.
@@ -156,9 +155,27 @@ def dict_2datatypes(
156
155
  convert_keys: bool = False,
157
156
  convert_to_unicode: bool = False,
158
157
  dt: str = 'datetime',
159
- fmt: str = '%d.%m.%Y'
158
+ fmt: str = None
160
159
  ) -> Dict[Any, Any]:
161
- """ convert all data of a dictionary to specific (json metadata) or guessed types """
160
+ """ Convert all data of a dictionary to specific (json metadata) or guessed types
161
+
162
+ :param d: dictionary
163
+ :param metadata: dictionary containing types for the keys:
164
+ - boolean
165
+ - int
166
+ - float
167
+ - date
168
+ - datetime: date and time
169
+ - time: conversion or not based on dt
170
+ :param convert_keys: convert keys to unicode
171
+ :param convert_to_unicode: convert all bytes to unicode in the process
172
+ :param dt: - datetime or dt
173
+ - DateTime or DT
174
+ - ignore or string
175
+ - pendulum
176
+ - default: datetime, because it's Python's default
177
+ :param fmt: format for datetime parsing
178
+ """
162
179
 
163
180
  if convert_keys:
164
181
  return {
@@ -197,15 +214,35 @@ def dictlist_2datatypes(
197
214
  convert_keys: bool = False,
198
215
  convert_to_unicode: bool = False,
199
216
  dt: str = 'datetime',
200
- fmt: str = '%d.%m.%Y'
217
+ fmt: str = None
201
218
  ) -> list:
219
+ """ Convert all data of a list of dictionaries to specific (json metadata) or guessed types
220
+
221
+ :param dictList: list of dictionaries
222
+ :param metadata: dictionary containing types for the keys:
223
+ - boolean
224
+ - int
225
+ - float
226
+ - date
227
+ - datetime: date and time
228
+ - time: conversion or not based on dt
229
+ :param convert_keys: convert keys to unicode
230
+ :param convert_to_unicode: convert all bytes to unicode in the process
231
+ :param dt: - datetime or dt
232
+ - DateTime or DT
233
+ - ignore or string
234
+ - pendulum
235
+ - default: datetime, because it's Python's default
236
+ :param fmt: format for datetime parsing
237
+ """
202
238
  return [
203
239
  dict_2datatypes(
204
240
  d=dic,
205
241
  metadata=metadata,
206
242
  convert_keys=convert_keys,
207
243
  convert_to_unicode=convert_to_unicode,
208
- dt=dt
244
+ dt=dt,
245
+ fmt=fmt
209
246
  )
210
247
  for dic
211
248
  in dictlist
@@ -258,17 +295,18 @@ def value_2datatype(
258
295
  convert_to_unicode: bool = False,
259
296
  encoding: str = 'utf-8',
260
297
  dt: str = 'datetime',
261
- fmt: str = '%d.%m.%Y', # only for datetime
262
- UTC: bool = False
298
+ fmt: str = None,
299
+ timezone: str = 'local'
263
300
  ) -> Union[str, int, float]:
264
301
  """ Convert a value to a datatype
265
302
 
266
303
  this function has two modes:
267
304
 
268
- 1. it is provided a type for the conversion
305
+ 1. a type is provided for the conversion
269
306
  2. it makes educated guesses in converting a string
270
307
 
271
308
  :param value:
309
+ :param typ: type for the conversion
272
310
  :param metadata: dictionary containing types for the keys:
273
311
  - boolean
274
312
  - int
@@ -281,7 +319,10 @@ def value_2datatype(
281
319
  :param encoding: if bytes are present, use this encoding to convert them to unicode
282
320
  :param dt: - datetime or dt
283
321
  - DateTime or DT
322
+ - ignore or string
323
+ - pendulum
284
324
  - default: datetime, because it's Python's default
325
+ :param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
285
326
  """
286
327
  if typ:
287
328
  if typ == 'boolean':
@@ -311,6 +352,17 @@ def value_2datatype(
311
352
  value,
312
353
  datefmt='international'
313
354
  ) + 0.5 # to avoid day-shifting due to timezones, use 12am
355
+ elif dt == 'pendulum':
356
+ if fmt:
357
+ p_dt = pendulum.from_format(value, fmt)
358
+ else:
359
+ p_dt = pendulum.parse(value)
360
+
361
+ if p_dt.tzinfo == 'UTC' and timezone == 'local':
362
+ return p_dt.in_tz(pendulum.local_timezone())
363
+ elif timezone:
364
+ return p_dt.in_tz(timezone)
365
+ return p_dt.date()
314
366
  elif dt in ('ignore', 'string'):
315
367
  return value
316
368
  else:
@@ -327,6 +379,17 @@ def value_2datatype(
327
379
  )
328
380
  except DateTime.interfaces.SyntaxError:
329
381
  return value
382
+ elif dt == 'pendulum':
383
+ if fmt:
384
+ p_dt = pendulum.from_format(value, fmt)
385
+ else:
386
+ p_dt = pendulum.parse(value)
387
+
388
+ if p_dt.tzinfo == 'UTC' and timezone == 'local':
389
+ return p_dt.in_tz(pendulum.local_timezone())
390
+ elif timezone:
391
+ return p_dt.in_tz(timezone)
392
+ return p_dt
330
393
  elif dt in ('ignore', 'string'):
331
394
  return value
332
395
  else:
@@ -339,7 +402,6 @@ def value_2datatype(
339
402
  return value # DT: return the string
340
403
 
341
404
  else:
342
-
343
405
  if isinstance(value, (bool, int, float)):
344
406
  return value
345
407
 
@@ -373,12 +435,7 @@ def value_2datatype(
373
435
  datestring=value,
374
436
  checkonly=True
375
437
  ):
376
- if ':' in value:
377
- return convert_datetime(
378
- date=value,
379
- convert_to=dt
380
- )
381
- elif dt in ('datetime', 'dt'):
438
+ if dt in ('datetime', 'dt'):
382
439
  return str_to_date(
383
440
  datestring=value
384
441
  )
@@ -386,6 +443,22 @@ def value_2datatype(
386
443
  return str_to_DT(
387
444
  datestring=value
388
445
  )
446
+ elif dt == 'pendulum':
447
+ if fmt:
448
+ p_dt = pendulum.from_format(value, fmt)
449
+ else:
450
+ p_dt = pendulum.parse(value)
451
+
452
+ if p_dt.tzinfo == 'UTC' and timezone == 'local':
453
+ return p_dt.in_tz(pendulum.local_timezone())
454
+ elif timezone:
455
+ return p_dt.in_tz(timezone)
456
+ return p_dt
457
+ else:
458
+ return convert_datetime(
459
+ date=value,
460
+ convert_to=dt
461
+ )
389
462
 
390
463
  except Exception:
391
464
  # in case it's a subset of datetime_chars but not a date or datetime or time
@@ -37,6 +37,8 @@ def is_valid_datetime_string(
37
37
  a conflict with 4-digit year values, it is considered invalid
38
38
 
39
39
  :param returning: can return a tuple
40
+
41
+ .. note:: This does not work with ISO8601 strings! (returns False)
40
42
  """
41
43
  return str_to_dt(
42
44
  datestring=datestring,
@@ -57,6 +59,14 @@ def get_dateformat(
57
59
  :todo: times!
58
60
  :raises ValueError: if an invalid datetime or DateTime string is provided
59
61
  """
62
+ # first let's check for ISO8601 strings
63
+ if checkonly:
64
+ try:
65
+ pendulum.parse(datestring)
66
+ return True
67
+ except pendulum.parsing.exceptions.ParserError:
68
+ pass # don't return False, because it may be a valid date string
69
+
60
70
  timefmt = ''
61
71
  if ' ' in datestring:
62
72
  try:
@@ -225,7 +235,7 @@ def str_to_pendulum(
225
235
  if not fmt:
226
236
  return pendulum.parse(datestring)
227
237
  else:
228
- return pendulum.datetime.strptime(datestring, fmt)
238
+ return pendulum.from_format(datestring, fmt)
229
239
 
230
240
 
231
241
  def str_to_date(
@@ -15,7 +15,7 @@ def prettify_html(
15
15
 
16
16
  .. note:: This function is needed for Zope Python Scripts
17
17
  because even if bs4 can be imported, prettify throws
18
- an Unauthorized error in restricted Python
18
+ an Unauthorized error in Restricted Python
19
19
  """
20
20
 
21
21
  return BeautifulSoup(html, "html.parser").prettify()
@@ -14,7 +14,7 @@ from nameparser import HumanName
14
14
  d = gender.Detector(case_sensitive=False)
15
15
 
16
16
 
17
- char_nb_map = {
17
+ CHAR_NB_MAP = {
18
18
  'a': ('a', '4', '@'),
19
19
  'b': ('b', '6', '8', 'I3', '13', '!3'),
20
20
  'c': ('c', '('),
@@ -43,6 +43,54 @@ char_nb_map = {
43
43
  'z': ('z', '2', '7_')
44
44
  }
45
45
 
46
+ # regex pattern
47
+ LANG_PUNCT_SPACES = {
48
+ 'fr': {
49
+ 'regex': r'([!?:;%])',
50
+ 'narrow': '\u202f\\1',
51
+ 'normal': r' \1'
52
+ },
53
+ 'es': {
54
+ 'regex': r'([¿¡])',
55
+ 'narrow': '\\1\u202f',
56
+ 'normal': r'\1 '
57
+ },
58
+ 'ro': {
59
+ 'regex': r'([!?:;])',
60
+ 'narrow': '\u202f\\1',
61
+ 'normal': r' \1'
62
+ }
63
+ }
64
+
65
+ def adjust_spaces_on_punctuation(
66
+ text: str,
67
+ language: str,
68
+ space: str = 'narrow'
69
+ ):
70
+ """
71
+ Adjust spaces before or after punctuation marks according to the language.
72
+ By default the narrow space (\u202f) is used for adjustments.
73
+ """
74
+ # Step 1: Remove existing spaces before the punctuation
75
+ text = re.sub(r'\s+([!?:;%\),.])', r'\1', text)
76
+
77
+ lang_data = LANG_PUNCT_SPACES.get(language)
78
+
79
+ if lang_data:
80
+ # Step 2: Add a narrow space (\u202f) before the punctuation
81
+ try:
82
+ text = re.sub(
83
+ lang_data['regex'],
84
+ lang_data[space],
85
+ text
86
+ )
87
+ except re.error as e:
88
+ raise Exception(f"Error in regex for language {language}: {e}")
89
+ else:
90
+ # Original behavior for other languages
91
+ text = re.sub(r'\s+([,.!?;:\)])', r'\1', text)
92
+ return text
93
+
46
94
 
47
95
  def and_list(
48
96
  elements: list,
@@ -202,7 +250,7 @@ def leet(
202
250
  if not c.isalnum():
203
251
  continue # without counting
204
252
 
205
- c = random.choice(char_nb_map[c])
253
+ c = random.choice(CHAR_NB_MAP[c])
206
254
 
207
255
  if not random.randrange(change_uppercase):
208
256
  c = c.upper()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: invesytoolbox
3
- Version: 0.0.22
3
+ Version: 0.0.23
4
4
  Summary: Tools for Python scripts or terminal
5
5
  Home-page: https://gitlab.com/Rastaf/invesytoolbox
6
6
  Author: Georg Pfolz
@@ -44,6 +44,15 @@ That's also why all date and time functions also take into account the old DateT
44
44
  The documentation can be found [here](https://rastaf.gitlab.io/invesytoolbox/).
45
45
 
46
46
  # History
47
+ ## 0.0.23 (2024-07-22)
48
+ * **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
49
+ * **value_2datatype**:
50
+ - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
51
+ - 'pendulum' is now an option for the parameter
52
+ - default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
53
+ - **dict_2datatypes**
54
+ - **dictlist_2datatypes**
55
+
47
56
  ## 0.0.22 (2023-10-14)
48
57
  * **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
49
58
  * **get_locale**: locale can be only the language, without the country.
@@ -8,18 +8,21 @@ import sys
8
8
  import unittest
9
9
  import datetime
10
10
  import DateTime
11
+ import pendulum
11
12
  from dateutil.parser import parse
12
13
 
13
14
  sys.path.append(".")
14
15
 
15
- from itb_data import \
16
- any_2boolean, \
17
- create_vcard, \
18
- dict_2unicode, \
19
- dict_2datatypes, \
20
- dict_from_dict_list, \
21
- dictlist_2datatypes, \
22
- sort_dictlist
16
+ from itb_data import (
17
+ any_2boolean,
18
+ create_vcard,
19
+ dict_2unicode,
20
+ dict_2datatypes,
21
+ dict_from_dict_list,
22
+ dictlist_2datatypes,
23
+ sort_dictlist,
24
+ value_2datatype
25
+ )
23
26
 
24
27
  dict_list = [
25
28
  {'a': 1, 'b': 2, 'c': 5},
@@ -154,6 +157,9 @@ END:VCARD
154
157
 
155
158
 
156
159
  class TestData(unittest.TestCase):
160
+ # verbose, show complete diff
161
+ maxDiff = None
162
+
157
163
  def test_any_2boolean(self):
158
164
  for a, b in test_boolean.items():
159
165
  self.assertEqual(
@@ -260,7 +266,28 @@ class TestData(unittest.TestCase):
260
266
  )
261
267
 
262
268
  def test_value_2datatype(self):
263
- pass # this is already tested extensively in test_dict_2datatypes
269
+ # this is already tested extensively in test_dict_2datatypes
270
+ # so we just test pendulum here
271
+ print('test_value_2datatype: this should result in pendulum datetimes')
272
+ value = '01.04.2022 10:32'
273
+ fmt = 'DD.MM.YYYY HH:mm'
274
+
275
+ p = value_2datatype(
276
+ value=value,
277
+ typ='datetime',
278
+ dt='pendulum',
279
+ fmt=fmt
280
+ )
281
+ print(value, '→', p, p.tzinfo)
282
+
283
+ value = '2024-04-01T10:32:00'
284
+
285
+ p = value_2datatype(
286
+ value=value,
287
+ dt='pendulum'
288
+ )
289
+ print(value, '→', p, p.tzinfo)
290
+
264
291
 
265
292
  def test_sort_dictlist(self):
266
293
  sorted_dict_list = sort_dictlist(
@@ -297,6 +324,10 @@ class TestData(unittest.TestCase):
297
324
  )
298
325
 
299
326
  def test_dictlist_2datatypes(self):
327
+ # print('dict_for_testing', dict_for_testing)
328
+ # print('dict_for_testing_datatyped_unicoded', dict_for_testing_datatyped_unicoded)
329
+
330
+
300
331
  dictlist_for_testing = [
301
332
  dict_for_testing,
302
333
  dict_for_testing,
@@ -323,6 +354,16 @@ class TestData(unittest.TestCase):
323
354
  dictlist_tested_datatyped
324
355
  )
325
356
 
357
+ dictlist_pendulum = dictlist_2datatypes(
358
+ dictlist=dictlist_for_testing,
359
+ convert_keys=True,
360
+ convert_to_unicode=True,
361
+ dt='pendulum',
362
+ fmt='DD.MM.YYYY'
363
+ )
364
+
365
+ print('dictlist_pendulum', dictlist_pendulum)
366
+
326
367
 
327
368
  if __name__ == '__main__':
328
369
  unittest.main()
@@ -11,6 +11,7 @@ import random
11
11
  sys.path.append(".")
12
12
 
13
13
  from itb_text_name import (
14
+ adjust_spaces_on_punctuation,
14
15
  and_list,
15
16
  capitalize_name,
16
17
  get_gender,
@@ -295,8 +296,40 @@ reference_names_sorted = [
295
296
 
296
297
  lower_text = 'das ist ein Beispiel-Text, der kapitalisiert werden kann.'
297
298
 
299
+ # Language Texts (for adjusting spaces on the indentation)
300
+ punct_texts = {
301
+ 'de': "Hallo Welt ! Wie geht es Ihnen ? Ich hoffe, Sie haben einen schönen Tag : Das Wetter ist herrlich . Wussten Sie, dass 75 % aller Statistiken erfunden sind?",
302
+ 'en': "Hello world ! How are you today ? I hope you're having a great day : The weather is lovely . Did you know that 80 % of all statistics are made up?",
303
+ 'fr': "Bonjour le monde! Comment allez-vous aujourd'hui? J'espère que vous passez une bonne journée: Le temps est magnifique . Saviez-vous que 85% des statistiques sont inventées?",
304
+ 'es': "¡Hola mundo! ¿Cómo estás hoy ? Espero que tengas un buen día : El clima es hermoso. ¿Sabías que el 90 % de las estadísticas son inventadas ?",
305
+ 'ro': "Salut lume! Cum ești azi? Sper că ai o zi bună: Vremea este minunată.Știai că 95 % din statistici sunt inventate?"
306
+ }
307
+
308
+ punct_texts_corrected = {
309
+ 'de': "Hallo Welt! Wie geht es Ihnen? Ich hoffe, Sie haben einen schönen Tag: Das Wetter ist herrlich. Wussten Sie, dass 75% aller Statistiken erfunden sind?",
310
+ 'en': "Hello world! How are you today? I hope you're having a great day: The weather is lovely. Did you know that 80% of all statistics are made up?",
311
+ 'fr': "Bonjour le monde\u202f! Comment allez-vous aujourd'hui\u202f? J'espère que vous passez une bonne journée\u202f: Le temps est magnifique. Saviez-vous que 85\u202f% des statistiques sont inventées\u202f?",
312
+ 'es': "¡\u202fHola mundo! ¿\u202fCómo estás hoy? Espero que tengas un buen día: El clima es hermoso. ¿\u202fSabías que el 90% de las estadísticas son inventadas?",
313
+ 'ro': "Salut lume\u202f! Cum ești azi\u202f? Sper că ai o zi bună\u202f: Vremea este minunată.Știai că 95% din statistici sunt inventate\u202f?"
314
+ }
298
315
 
299
316
  class TestTextName(unittest.TestCase):
317
+ def test_adjust_spaces_on_punctuation(self):
318
+ for lang, text in punct_texts.items():
319
+ print(f'Language: {lang}')
320
+ try:
321
+ corrected_text = adjust_spaces_on_punctuation(
322
+ text = text,
323
+ language = lang
324
+ )
325
+ except Exception as e:
326
+ print(f'Error: {e}')
327
+ raise AssertionError(f'Error: {e}')
328
+ self.assertEqual(
329
+ corrected_text,
330
+ punct_texts_corrected[lang]
331
+ )
332
+
300
333
  def test_and_list(self):
301
334
  a_list = [1, 'Georg', 'Haus', True]
302
335
  correct_str = '1, Georg, Haus and True'
File without changes
File without changes