absfuyu 2.8.1__py3-none-any.whl → 3.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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (42) hide show
  1. absfuyu/__init__.py +13 -10
  2. absfuyu/__main__.py +55 -38
  3. absfuyu/config/config.json +3 -3
  4. absfuyu/core.py +39 -25
  5. absfuyu/everything.py +4 -5
  6. absfuyu/extensions/__init__.py +3 -2
  7. absfuyu/extensions/dev/__init__.py +162 -19
  8. absfuyu/extensions/dev/password_hash.py +11 -10
  9. absfuyu/extensions/dev/passwordlib.py +256 -0
  10. absfuyu/extensions/dev/pkglib.py +53 -57
  11. absfuyu/extensions/dev/project_starter.py +58 -0
  12. absfuyu/extensions/dev/shutdownizer.py +8 -0
  13. absfuyu/extensions/extra/data_analysis.py +687 -119
  14. absfuyu/fun/__init__.py +88 -118
  15. absfuyu/fun/tarot.py +32 -34
  16. absfuyu/game/tictactoe2.py +90 -78
  17. absfuyu/{collections → general}/__init__.py +14 -12
  18. absfuyu/{collections → general}/content.py +105 -87
  19. absfuyu/{collections → general}/data_extension.py +652 -172
  20. absfuyu/{collections → general}/generator.py +65 -4
  21. absfuyu/{collections → general}/human.py +28 -3
  22. absfuyu/pkg_data/__init__.py +14 -36
  23. absfuyu/pkg_data/chemistry.pkl +0 -0
  24. absfuyu/pkg_data/tarot.pkl +0 -0
  25. absfuyu/tools/converter.py +58 -31
  26. absfuyu/tools/obfuscator.py +4 -4
  27. absfuyu/tools/stats.py +4 -4
  28. absfuyu/tools/web.py +2 -2
  29. absfuyu/util/lunar.py +144 -123
  30. absfuyu/util/path.py +22 -3
  31. absfuyu/util/performance.py +101 -14
  32. absfuyu/version.py +93 -84
  33. {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/METADATA +63 -33
  34. absfuyu-3.1.0.dist-info/RECORD +55 -0
  35. {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/WHEEL +1 -1
  36. absfuyu-3.1.0.dist-info/entry_points.txt +2 -0
  37. absfuyu/pkg_data/chemistry.json +0 -6268
  38. absfuyu/pkg_data/tarot.json +0 -2593
  39. absfuyu-2.8.1.dist-info/RECORD +0 -52
  40. absfuyu-2.8.1.dist-info/entry_points.txt +0 -2
  41. {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/LICENSE +0 -0
  42. {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/top_level.txt +0 -0
@@ -3,21 +3,18 @@ Absfuyu: Content
3
3
  ----------------
4
4
  Handle .txt file
5
5
 
6
- Version: 1.2.4
7
- Date updated: 24/11/2023 (dd/mm/yyyy)
6
+ Version: 1.2.5
7
+ Date updated: 27/11/2023 (dd/mm/yyyy)
8
8
 
9
9
  Usage:
10
10
  ------
11
- >>> from absfuyu.collections.content import ContentLoader
11
+ >>> from absfuyu.general.content import ContentLoader
12
12
  """
13
13
 
14
14
 
15
15
  # Module level
16
16
  ###########################################################################
17
- __all__ = [
18
- "ContentLoader",
19
- "Content", "LoadedContent"
20
- ]
17
+ __all__ = ["ContentLoader", "Content", "LoadedContent"]
21
18
 
22
19
 
23
20
  # Library
@@ -44,20 +41,23 @@ class Content:
44
41
  Data format: list[str, list[str]]
45
42
  where: ``str: data``; ``list[str]: data tags``
46
43
  """
44
+
47
45
  def __init__(self, data: list) -> None:
48
46
  self.data: str = str(data[0])
49
47
  self.tag: list = data[1]
50
48
  # logger.debug(self.__dict__)
49
+
51
50
  def __str__(self) -> str:
52
51
  # return f"{self.data} | {self.tag}"
53
52
  return str(self.data)
53
+
54
54
  def __repr__(self) -> str:
55
55
  return self.__str__()
56
56
 
57
- def unidecoded(self):
57
+ def unidecoded(self) -> "Content":
58
58
  """
59
59
  Convert data through ``unidecode`` package
60
-
60
+
61
61
  Returns
62
62
  -------
63
63
  Content
@@ -68,7 +68,7 @@ class Content:
68
68
  def to_text(self) -> str:
69
69
  """
70
70
  Convert back into text
71
-
71
+
72
72
  Returns
73
73
  -------
74
74
  str
@@ -77,7 +77,7 @@ class Content:
77
77
  # hard code
78
78
  tags = ",".join(self.tag)
79
79
  return f"{self.data}|{tags}"
80
-
80
+
81
81
  def short_form(self, separator: str = ",") -> str:
82
82
  """
83
83
  Short form show only first item when separated by ``separator``
@@ -86,7 +86,7 @@ class Content:
86
86
  ----------
87
87
  separator : str
88
88
  Default: ``","``
89
-
89
+
90
90
  Returns
91
91
  -------
92
92
  str
@@ -95,20 +95,17 @@ class Content:
95
95
  if not separator.startswith(","):
96
96
  logger.debug(f"Separated symbol: {separator}")
97
97
  return self.data.split(separator)[0]
98
-
98
+
99
99
  def handle_address(
100
- self,
101
- address_separator: str = ",",
102
- *,
103
- first_item_not_address: bool = True
104
- ):
100
+ self, address_separator: str = ",", *, first_item_not_address: bool = True
101
+ ) -> "Content":
105
102
  """
106
103
  Handle ``self.data`` as address and then update the address into ``self.tag``
107
104
 
108
105
  Parameters
109
106
  ----------
110
107
  address_separator : str
111
- | Split the address by which character
108
+ | Split the address by which character
112
109
  | (Default: ``","``)
113
110
 
114
111
  first_item_not_address : bool
@@ -132,7 +129,9 @@ class Content:
132
129
  if first_item_not_address:
133
130
  temp = self.data.split(address_separator)
134
131
  else:
135
- logger.debug(f"First item ({self.data.split(address_separator)[0]}) is not part of an address")
132
+ logger.debug(
133
+ f"First item ({self.data.split(address_separator)[0]}) is not part of an address"
134
+ )
136
135
  temp = self.data.split(address_separator)[1:]
137
136
 
138
137
  new_tag = [x.strip().lower() for x in temp]
@@ -143,20 +142,23 @@ class Content:
143
142
 
144
143
  return __class__([self.data, self.tag])
145
144
 
145
+
146
146
  class LoadedContent(List[Content]):
147
147
  """
148
148
  Contain list of ``Content``
149
149
  """
150
+
150
151
  def __str__(self) -> str:
151
152
  # return f"{self.__class__.__name__} - Total: {len(self)}"
152
153
  return f"{self.__class__.__name__}({[x.data for x in self]})"
154
+
153
155
  def __repr__(self) -> str:
154
156
  return self.__str__()
155
-
156
- def apply(self, func):
157
+
158
+ def apply(self, func) -> "LoadedContent":
157
159
  """
158
160
  Apply function to each entry
159
-
161
+
160
162
  :param func: Callable function
161
163
  :type func: Callable
162
164
  :rtype: LoadedContent
@@ -164,7 +166,7 @@ class LoadedContent(List[Content]):
164
166
  return __class__(func(x.data) for x in self)
165
167
 
166
168
  @classmethod
167
- def load_from_json(cls, file: Path):
169
+ def load_from_json(cls, file: Path) -> "LoadedContent":
168
170
  """
169
171
  Use this method to load data from ``.json`` file from ``to_json()`` method
170
172
 
@@ -172,7 +174,7 @@ class LoadedContent(List[Content]):
172
174
  ----------
173
175
  file : Path
174
176
  Path to ``.json`` file
175
-
177
+
176
178
  Returns
177
179
  -------
178
180
  LoadedContent
@@ -190,21 +192,21 @@ class LoadedContent(List[Content]):
190
192
  out = list(set(temp))
191
193
  logger.debug(f"Found {len(out)} {'tags' if len(out) > 1 else 'tag'}")
192
194
  return sorted(out)
193
-
195
+
194
196
  def tag_count(self) -> Counter:
195
197
  """
196
198
  Count number of tags
197
-
199
+
198
200
  :rtype: Counter
199
201
  """
200
202
  temp = chain.from_iterable([x.tag for x in self])
201
203
  logger.debug(temp)
202
204
  return Counter(temp)
203
-
204
- def filter(self, tag: str):
205
+
206
+ def filter(self, tag: str) -> "LoadedContent":
205
207
  """
206
208
  Filter out entry with ``tag``
207
-
209
+
208
210
  :param tag: Tag to filter
209
211
  :type tag: str
210
212
  :rtype: LoadedContent
@@ -214,18 +216,18 @@ class LoadedContent(List[Content]):
214
216
  if tag not in self.tags:
215
217
  # tag = random.choice(self.tags)
216
218
  # logger.debug(f"Tag not exist, changing to a random tag... {tag}")
217
- logger.warning(f"\"{tag}\" tag does not exist")
219
+ logger.warning(f'"{tag}" tag does not exist')
218
220
  _avail_tag = ", ".join(list(dict(self.tag_count().most_common(5)).keys()))
219
221
  raise ValueError(
220
222
  f"Available tags: {_avail_tag},..."
221
223
  f"\nMore tag at: `{self.__class__.__name__}.tags`"
222
224
  )
223
225
  return __class__([x for x in self if tag in x.tag])
224
-
225
- def find(self, keyword: str):
226
+
227
+ def find(self, keyword: str) -> "LoadedContent":
226
228
  """
227
229
  Return all entries that include ``keyword``
228
-
230
+
229
231
  :param keyword: Keyword to find
230
232
  :type keyword: str
231
233
  :rtype: LoadedContent
@@ -237,7 +239,7 @@ class LoadedContent(List[Content]):
237
239
  logger.debug("No result")
238
240
  return temp
239
241
 
240
- def short_form(self, separator: str = ","):
242
+ def short_form(self, separator: str = ",") -> List[str]:
241
243
  """
242
244
  Shows only first item when separated by ``separator`` of ``Content.data``
243
245
 
@@ -245,7 +247,7 @@ class LoadedContent(List[Content]):
245
247
  ----------
246
248
  separator : str
247
249
  Default: ``","``
248
-
250
+
249
251
  Returns
250
252
  -------
251
253
  list[str]
@@ -256,13 +258,13 @@ class LoadedContent(List[Content]):
256
258
  def pick_one(self, tag: Optional[str] = None) -> Content:
257
259
  """
258
260
  Pick a random entry
259
-
261
+
260
262
  Parameters
261
263
  ----------
262
264
  tag : str | None
263
- Pick a random entry with ``tag``
265
+ Pick a random entry with ``tag``
264
266
  (Default: None)
265
-
267
+
266
268
  Returns
267
269
  -------
268
270
  Content
@@ -273,20 +275,17 @@ class LoadedContent(List[Content]):
273
275
  logger.debug(f"Tag: {tag}")
274
276
  return random.choice(temp)
275
277
  return random.choice(self)
276
-
278
+
277
279
  def handle_address(
278
- self,
279
- address_separator: str = ",",
280
- *,
281
- first_item_not_address: bool = True
282
- ):
280
+ self, address_separator: str = ",", *, first_item_not_address: bool = True
281
+ ) -> "LoadedContent":
283
282
  """
284
283
  Execute ``Content.handle_address()`` on every ``self.data`` of ``Content``
285
284
 
286
285
  Parameters
287
286
  ----------
288
287
  address_separator : str
289
- | Split the address by which character
288
+ | Split the address by which character
290
289
  | (Default: ``","``)
291
290
 
292
291
  first_item_not_address : bool
@@ -297,7 +296,7 @@ class LoadedContent(List[Content]):
297
296
  LoadedContent
298
297
  Handled Content
299
298
 
300
-
299
+
301
300
  Example:
302
301
  --------
303
302
  >>> test = "Shop A, 22 ABC Street, DEF District, GHI City"
@@ -307,13 +306,16 @@ class LoadedContent(List[Content]):
307
306
  >>> # After handle_address(first_item_not_address = False)
308
307
  ["22 ABC Street", "DEF District", "GHI City"]
309
308
  """
310
- return __class__([
311
- x.handle_address(
312
- address_separator=address_separator,
313
- first_item_not_address=first_item_not_address
314
- ) for x in self
315
- ])
316
-
309
+ return __class__(
310
+ [
311
+ x.handle_address(
312
+ address_separator=address_separator,
313
+ first_item_not_address=first_item_not_address,
314
+ )
315
+ for x in self
316
+ ]
317
+ )
318
+
317
319
  def to_json(self, no_accent: bool = False) -> str:
318
320
  """
319
321
  Convert data into ``.json`` file
@@ -323,7 +325,7 @@ class LoadedContent(List[Content]):
323
325
  no_accent : bool
324
326
  | when ``True``: convert the data through ``unidecode`` package
325
327
  | (Default: ``False``)
326
-
328
+
327
329
  Returns
328
330
  -------
329
331
  str
@@ -345,7 +347,7 @@ class LoadedContent(List[Content]):
345
347
  no_accent : bool
346
348
  | when ``True``: convert the data through ``unidecode`` package
347
349
  | (Default: ``False``)
348
-
350
+
349
351
  Returns
350
352
  -------
351
353
  str
@@ -358,6 +360,7 @@ class LoadedContent(List[Content]):
358
360
  logger.debug(out)
359
361
  return "\n".join(out)
360
362
 
363
+
361
364
  class ContentLoader:
362
365
  """
363
366
  This load data from ``.txt`` file
@@ -365,16 +368,17 @@ class ContentLoader:
365
368
  Content format:
366
369
  ``<content><split_symbol><tags separated by <tag_separate_symbol>>``
367
370
  """
371
+
368
372
  def __init__(
369
- self,
370
- file_path: Path,
371
- characteristic_detect: bool = True,
372
- tag_dictionary: dict = None,
373
- *,
374
- comment_symbol: str = "#",
375
- split_symbol: str = "|",
376
- tag_separate_symbol: str = ","
377
- ) -> None:
373
+ self,
374
+ file_path: Path,
375
+ characteristic_detect: bool = True,
376
+ tag_dictionary: dict = None,
377
+ *,
378
+ comment_symbol: str = "#",
379
+ split_symbol: str = "|",
380
+ tag_separate_symbol: str = ",",
381
+ ) -> None:
378
382
  """
379
383
  Parameters
380
384
  ----------
@@ -389,7 +393,7 @@ class ContentLoader:
389
393
  custom tag pattern
390
394
  format: ``{"keyword": "tag",...}``
391
395
  example: ``{"apple": "fruit", "orange": "fruit"}``
392
-
396
+
393
397
  comment_symbol : str
394
398
  symbol that `ContentLoader` will ignore
395
399
  (default: ``"#"``)
@@ -422,17 +426,22 @@ class ContentLoader:
422
426
  self.tag_dictionary: dict = tag_dictionary
423
427
 
424
428
  # symbol stuff
425
- assert comment_symbol != split_symbol, "comment_symbol and split_symbol should have different values"
426
- assert tag_separate_symbol != split_symbol, "tag_separate_symbol and split_symbol should have different values"
429
+ assert (
430
+ comment_symbol != split_symbol
431
+ ), "comment_symbol and split_symbol should have different values"
432
+ assert (
433
+ tag_separate_symbol != split_symbol
434
+ ), "tag_separate_symbol and split_symbol should have different values"
427
435
  self.comment_symbol: str = comment_symbol
428
436
  self.split_symbol: str = split_symbol
429
437
  self.tag_separate_symbol: str = tag_separate_symbol
430
-
438
+
431
439
  def __str__(self) -> str:
432
440
  return f"{self.__class__.__name__}({self.__dict__})"
441
+
433
442
  def __repr__(self) -> str:
434
443
  return self.__str__()
435
-
444
+
436
445
  def content_format(self) -> str:
437
446
  """
438
447
  Shows current content format
@@ -469,21 +478,25 @@ class ContentLoader:
469
478
  logger.debug("Loading data...")
470
479
  dat = []
471
480
  check = []
472
-
481
+
473
482
  for i, x in enumerate(data.readlines()):
474
483
  x = x.strip()
475
- if x.startswith(self.comment_symbol) or len(x)==0:
476
- continue # skip comment and empty lines
477
- logger.debug(f"### Loop {i+1} #####################################################################")
478
-
484
+ if x.startswith(self.comment_symbol) or len(x) == 0:
485
+ continue # skip comment and empty lines
486
+ logger.debug(
487
+ f"### Loop {i+1} #####################################################################"
488
+ )
489
+
479
490
  temp = x.split(self.split_symbol)
480
491
  if len(temp) != 2:
481
492
  logger.debug(f"Split len: {len(temp)}")
482
- logger.warning(f"The current entry is missing data or tag: {x[:20]}...")
483
-
493
+ logger.warning(
494
+ f"The current entry is missing data or tag: {x[:20]}..."
495
+ )
496
+
484
497
  temp[0] = temp[0].strip()
485
498
  if temp[0].lower() not in check:
486
- check.append(temp[0].lower()) # check for dupes
499
+ check.append(temp[0].lower()) # check for dupes
487
500
 
488
501
  # tag
489
502
  additional_tags = []
@@ -491,24 +504,29 @@ class ContentLoader:
491
504
  key = k.strip().lower()
492
505
  val = temp[0].lower()
493
506
  regex_pattern = f"[^a-zA-Z0-9]({key})[^a-zA-Z0-9]|^({key})[^a-zA-Z0-9]|[^a-zA-Z0-9]({key})$"
494
- if re.search(regex_pattern, val) is not None or val.startswith(key):
507
+ if re.search(regex_pattern, val) is not None or val.startswith(
508
+ key
509
+ ):
495
510
  # regex has a bug (or idk if it's a bug or not) that
496
511
  # doesn't recognise when there is only one word in the sentence
497
512
  # therefore use `val.startswith(key)` to fix
498
513
  additional_tags.append(v.strip().lower())
499
514
 
500
515
  if self.characteristic_detect:
501
- long_short = (120, 20) # setting
516
+ long_short = (120, 20) # setting
502
517
  if len(temp[0]) > long_short[0]:
503
518
  additional_tags.append("long")
504
519
  if len(temp[0]) < long_short[1]:
505
520
  additional_tags.append("short")
506
521
  if temp[0][-1].startswith("?"):
507
522
  additional_tags.append("question")
508
- if additional_tags: logger.debug(f"Additional tags: {additional_tags}")
523
+ if additional_tags:
524
+ logger.debug(f"Additional tags: {additional_tags}")
509
525
 
510
526
  try:
511
- tags = [tag.strip() for tag in temp[1].strip().lower().split(",")] # separate and strip tags
527
+ tags = [
528
+ tag.strip() for tag in temp[1].strip().lower().split(",")
529
+ ] # separate and strip tags
512
530
  logger.debug(f"Tags: {tags}")
513
531
  except:
514
532
  logger.warning("No tag found in the original string")
@@ -516,18 +534,18 @@ class ContentLoader:
516
534
  tags = additional_tags
517
535
  else:
518
536
  tags = ["unspecified"]
519
- logger.debug("Assigned \"unspecified\" tag")
520
-
537
+ logger.debug('Assigned "unspecified" tag')
538
+
521
539
  tags.extend(additional_tags)
522
540
  final_tags = list(set(tags))
523
541
  logger.debug(f"Final tags: {final_tags} Len: {len(final_tags)}")
524
542
 
525
- dat.append([temp[0], final_tags]) # add everything
543
+ dat.append([temp[0], final_tags]) # add everything
526
544
  else:
527
545
  logger.debug(f"Found duplicates, {x} removed")
528
546
 
529
547
  return dat
530
-
548
+
531
549
  def load_content(self) -> LoadedContent:
532
550
  """
533
551
  Load data into a list of ``Content``
@@ -543,4 +561,4 @@ class ContentLoader:
543
561
  # Run
544
562
  ###########################################################################
545
563
  if __name__ == "__main__":
546
- logger.setLevel(10)
564
+ logger.setLevel(10)