data-manipulation 0.50__tar.gz → 0.53__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 (110) hide show
  1. {data_manipulation-0.50 → data_manipulation-0.53}/.gitignore +6 -5
  2. data_manipulation-0.53/CHANGELOG.md +81 -0
  3. {data_manipulation-0.50 → data_manipulation-0.53}/PKG-INFO +6 -1
  4. {data_manipulation-0.50 → data_manipulation-0.53}/README.md +5 -0
  5. data_manipulation-0.53/RELEASE.md +84 -0
  6. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/__init__.py +0 -2
  7. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/_version.py +3 -3
  8. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/base.py +33 -45
  9. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/beautifulsoup_.py +8 -8
  10. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/boto3_.py +5 -1
  11. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/cryptography_.py +11 -2
  12. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/django_.py +7 -3
  13. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/geopandas_.py +11 -5
  14. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/kerberos_.py +5 -1
  15. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/mysql_connector_python_.py +5 -1
  16. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/pandas_.py +80 -67
  17. data_manipulation-0.53/data_manipulation/polars_.py +87 -0
  18. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/postgres_.py +14 -2
  19. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/pyspark_.py +44 -30
  20. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/smtplib_.py +5 -1
  21. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/sqlalchemy_.py +18 -8
  22. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation.egg-info/PKG-INFO +6 -1
  23. data_manipulation-0.53/data_manipulation.egg-info/SOURCES.txt +48 -0
  24. data_manipulation-0.53/docs/api.md +59 -0
  25. data_manipulation-0.53/docs/changelog.md +81 -0
  26. data_manipulation-0.53/docs/commands.md +10 -0
  27. data_manipulation-0.50/docs/_build/html/_sources/getting-started.rst.txt → data_manipulation-0.53/docs/getting-started.md +1 -2
  28. data_manipulation-0.53/docs/hooks.py +10 -0
  29. data_manipulation-0.53/docs/index.md +13 -0
  30. data_manipulation-0.53/mkdocs.yml +25 -0
  31. {data_manipulation-0.50 → data_manipulation-0.53}/setup.sh +13 -10
  32. data_manipulation-0.50/data_manipulation/flask_.py +0 -31
  33. data_manipulation-0.50/data_manipulation/prometheus_.py +0 -0
  34. data_manipulation-0.50/data_manipulation.egg-info/SOURCES.txt +0 -98
  35. data_manipulation-0.50/docs/.nojekyll +0 -0
  36. data_manipulation-0.50/docs/Makefile +0 -153
  37. data_manipulation-0.50/docs/_build/doctrees/commands.doctree +0 -0
  38. data_manipulation-0.50/docs/_build/doctrees/data_manipulation.doctree +0 -0
  39. data_manipulation-0.50/docs/_build/doctrees/environment.pickle +0 -0
  40. data_manipulation-0.50/docs/_build/doctrees/getting-started.doctree +0 -0
  41. data_manipulation-0.50/docs/_build/doctrees/index.doctree +0 -0
  42. data_manipulation-0.50/docs/_build/doctrees/modules.doctree +0 -0
  43. data_manipulation-0.50/docs/_build/html/.buildinfo +0 -4
  44. data_manipulation-0.50/docs/_build/html/.nojekyll +0 -0
  45. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/base.html +0 -576
  46. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/beautifulsoup_.html +0 -184
  47. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/boto3_.html +0 -268
  48. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/cryptography_.html +0 -225
  49. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/django_.html +0 -258
  50. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/geopandas_.html +0 -159
  51. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/kerberos_.html +0 -170
  52. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/mysql_connector_python_.html +0 -310
  53. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/openldap_.html +0 -172
  54. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/pandas_.html +0 -867
  55. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/pyspark_.html +0 -551
  56. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/smtplib_.html +0 -163
  57. data_manipulation-0.50/docs/_build/html/_modules/data_manipulation/sqlalchemy_.html +0 -274
  58. data_manipulation-0.50/docs/_build/html/_modules/index.html +0 -89
  59. data_manipulation-0.50/docs/_build/html/_sources/commands.rst.txt +0 -10
  60. data_manipulation-0.50/docs/_build/html/_sources/data_manipulation.rst.txt +0 -141
  61. data_manipulation-0.50/docs/_build/html/_sources/index.rst.txt +0 -28
  62. data_manipulation-0.50/docs/_build/html/_sources/modules.rst.txt +0 -7
  63. data_manipulation-0.50/docs/_build/html/_static/basic.css +0 -906
  64. data_manipulation-0.50/docs/_build/html/_static/doctools.js +0 -149
  65. data_manipulation-0.50/docs/_build/html/_static/documentation_options.js +0 -13
  66. data_manipulation-0.50/docs/_build/html/_static/file.png +0 -0
  67. data_manipulation-0.50/docs/_build/html/_static/language_data.js +0 -192
  68. data_manipulation-0.50/docs/_build/html/_static/minus.png +0 -0
  69. data_manipulation-0.50/docs/_build/html/_static/nature.css +0 -245
  70. data_manipulation-0.50/docs/_build/html/_static/plus.png +0 -0
  71. data_manipulation-0.50/docs/_build/html/_static/pygments.css +0 -75
  72. data_manipulation-0.50/docs/_build/html/_static/searchtools.js +0 -635
  73. data_manipulation-0.50/docs/_build/html/_static/sphinx_highlight.js +0 -154
  74. data_manipulation-0.50/docs/_build/html/commands.html +0 -128
  75. data_manipulation-0.50/docs/_build/html/data_manipulation.html +0 -2299
  76. data_manipulation-0.50/docs/_build/html/genindex.html +0 -509
  77. data_manipulation-0.50/docs/_build/html/getting-started.html +0 -113
  78. data_manipulation-0.50/docs/_build/html/index.html +0 -155
  79. data_manipulation-0.50/docs/_build/html/modules.html +0 -206
  80. data_manipulation-0.50/docs/_build/html/objects.inv +0 -0
  81. data_manipulation-0.50/docs/_build/html/py-modindex.html +0 -171
  82. data_manipulation-0.50/docs/_build/html/search.html +0 -98
  83. data_manipulation-0.50/docs/_build/html/searchindex.js +0 -1
  84. data_manipulation-0.50/docs/commands.rst +0 -10
  85. data_manipulation-0.50/docs/conf.py +0 -273
  86. data_manipulation-0.50/docs/data_manipulation.rst +0 -141
  87. data_manipulation-0.50/docs/getting-started.rst +0 -6
  88. data_manipulation-0.50/docs/index.html +0 -1
  89. data_manipulation-0.50/docs/index.rst +0 -28
  90. data_manipulation-0.50/docs/make.bat +0 -190
  91. data_manipulation-0.50/docs/modules.rst +0 -7
  92. {data_manipulation-0.50 → data_manipulation-0.53}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  93. {data_manipulation-0.50 → data_manipulation-0.53}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  94. {data_manipulation-0.50 → data_manipulation-0.53}/CODE_OF_CONDUCT.md +0 -0
  95. {data_manipulation-0.50 → data_manipulation-0.53}/CONTRIBUTING.md +0 -0
  96. {data_manipulation-0.50 → data_manipulation-0.53}/LICENSE +0 -0
  97. {data_manipulation-0.50 → data_manipulation-0.53}/Makefile +0 -0
  98. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation/openldap_.py +0 -0
  99. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation.egg-info/dependency_links.txt +0 -0
  100. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation.egg-info/requires.txt +0 -0
  101. {data_manipulation-0.50 → data_manipulation-0.53}/data_manipulation.egg-info/top_level.txt +0 -0
  102. {data_manipulation-0.50 → data_manipulation-0.53}/models/.gitkeep +0 -0
  103. {data_manipulation-0.50 → data_manipulation-0.53}/notebooks/.gitkeep +0 -0
  104. {data_manipulation-0.50 → data_manipulation-0.53}/pyproject.toml +0 -0
  105. {data_manipulation-0.50 → data_manipulation-0.53}/references/.gitkeep +0 -0
  106. {data_manipulation-0.50 → data_manipulation-0.53}/reports/.gitkeep +0 -0
  107. {data_manipulation-0.50 → data_manipulation-0.53}/reports/figures/.gitkeep +0 -0
  108. {data_manipulation-0.50 → data_manipulation-0.53}/setup.cfg +0 -0
  109. {data_manipulation-0.50 → data_manipulation-0.53}/test_environment.py +0 -0
  110. {data_manipulation-0.50 → data_manipulation-0.53}/tox.ini +0 -0
@@ -51,15 +51,15 @@ coverage.xml
51
51
  # Django stuff:
52
52
  *.log
53
53
 
54
- # Sphinx documentation
55
- # docs/_build/
56
- !docs/build
54
+ # MkDocs documentation
55
+ /site/
57
56
 
58
57
  # PyBuilder
59
58
  target/
60
59
 
61
60
  # DotEnv configuration
62
61
  .env
62
+ .pypirc
63
63
 
64
64
  # Database
65
65
  *.db
@@ -93,7 +93,8 @@ target/
93
93
  .mypy_cache/
94
94
 
95
95
  # custom
96
+ .opencode
97
+ .specstory
96
98
  build.sh
97
- install_packages.sh
98
99
  docs/README*
99
- .opencode
100
+ install_packages.sh
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Removed
6
+
7
+ - **`pandas_.clean_none()`** — function has been removed.
8
+ Migrate to pandas built-ins:
9
+ ```python
10
+ df = df.replace(r"^\s*$", np.nan, regex=True)
11
+ df = df.where(pd.notnull(df), None)
12
+ ```
13
+
14
+ - **`pandas_.config_pandas_display()`** — function has been removed.
15
+ Set display options directly:
16
+ ```python
17
+ pd.set_option("display.max_columns", 500)
18
+ pd.set_option("display.max_colwidth", 500)
19
+ pd.set_option("display.expand_frame_repr", True)
20
+ ```
21
+
22
+ - **`prometheus_` module** — was an empty placeholder; removed from
23
+ the public API and from `__init__` imports.
24
+
25
+ ### Changed
26
+
27
+ - **`base.get_none_variation()`** — now returns a `frozenset` of 7
28
+ lowercase canonical strings instead of a 72-item list of case
29
+ permutations. Use `.casefold()` for membership checks:
30
+ ```python
31
+ # before
32
+ if value in get_none_variation():
33
+ ...
34
+ # after
35
+ if isinstance(value, str) and value.casefold() in get_none_variation():
36
+ ...
37
+ ```
38
+
39
+ - **`pyspark_.group_count()`** — the `percent` column is now computed
40
+ as a native Spark column expression instead of a Python UDF.
41
+ Behaviour is identical; performance improves on large datasets.
42
+
43
+ - **`pyspark_.column_into_list()`** — raises `ValueError` (with the
44
+ list of available columns) when the requested column does not exist.
45
+ Previously returned `None` silently.
46
+
47
+ - **`pandas_.print_dataframe_overview()`** — now returns a
48
+ `Dict[str, Dict[str, Any]]` of per-column stats (`unique`,
49
+ `null_count`, `value_counts`). Still prints to stdout as before;
50
+ existing call sites are unaffected.
51
+
52
+ - **`pandas_.compare_dataframes()`** — now returns a comparison
53
+ summary dict (`same_length`, `df1_length`, `df2_length`, `columns`).
54
+ Still prints to stdout as before; existing call sites are unaffected.
55
+
56
+ - **`pyspark_` module** — all `print()` calls replaced with
57
+ `logger.info()` / `logger.debug()`. Output is now routed through
58
+ the standard logging pipeline and can be silenced or redirected.
59
+
60
+ - **`sqlalchemy_.create_sqlalchemy_engine()`** — duplicate
61
+ `if/elif` timeout branches (both setting the same value) consolidated
62
+ into a single condition.
63
+
64
+ - **`django_.django_validate_phone()`** — now has an explicit
65
+ `return None` on failure instead of a bare `return`.
66
+
67
+ ### Fixed (previous pass — reference)
68
+
69
+ - `pyspark_.columns_statistics()` — always-true condition in empty-column
70
+ detection (third clause was truthy for any non-empty string).
71
+ - `pandas_.dtypes_dictionary()` — `.iteritems()` removed in pandas 2.0;
72
+ replaced with `.items()`.
73
+ - Bare `except:` replaced with `except Exception:` in `pandas_` and
74
+ `django_`.
75
+ - `base.string_boolean_to_int()` — removed (used `distutils`, dropped
76
+ in Python 3.12).
77
+ - `flask_.py` — deleted (was entirely commented-out dead code).
78
+ - `beautifulsoup_.preprocess()` — fixed mixed tabs/spaces and invalid
79
+ `\s` escape sequences.
80
+ - All modules now guard third-party imports with `try/except`; the
81
+ package imports cleanly even when optional dependencies are missing.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data_manipulation
3
- Version: 0.50
3
+ Version: 0.53
4
4
  Summary: Powerful data manipulation
5
5
  Author-email: Shawn Ng <shawn.coding.acc@gmail.com>
6
6
  License-Expression: BSD-3-Clause
@@ -43,6 +43,7 @@ Dynamic: license-file
43
43
  - [Where to get it](#where-to-get-it)
44
44
  - [Dependencies](#dependencies)
45
45
  - [License](#license)
46
+ - [Maintainer Release](#maintainer-release)
46
47
  - [Getting Help](#getting-help)
47
48
  - [Discussion and Development](#discussion-and-development)
48
49
  - [Contributing to data manipulation](#contributing-to-data-manipulation)
@@ -69,6 +70,10 @@ The dependencies will be installed automatically along with this package, using
69
70
 
70
71
  [BSD 3](LICENSE)
71
72
 
73
+ ## Maintainer Release
74
+
75
+ Release and deployment steps are documented in [RELEASE.md](RELEASE.md).
76
+
72
77
  ## Getting Help
73
78
 
74
79
  For usage questions, the best place to go to is StackOverflow.
@@ -5,6 +5,7 @@
5
5
  - [Where to get it](#where-to-get-it)
6
6
  - [Dependencies](#dependencies)
7
7
  - [License](#license)
8
+ - [Maintainer Release](#maintainer-release)
8
9
  - [Getting Help](#getting-help)
9
10
  - [Discussion and Development](#discussion-and-development)
10
11
  - [Contributing to data manipulation](#contributing-to-data-manipulation)
@@ -31,6 +32,10 @@ The dependencies will be installed automatically along with this package, using
31
32
 
32
33
  [BSD 3](LICENSE)
33
34
 
35
+ ## Maintainer Release
36
+
37
+ Release and deployment steps are documented in [RELEASE.md](RELEASE.md).
38
+
34
39
  ## Getting Help
35
40
 
36
41
  For usage questions, the best place to go to is StackOverflow.
@@ -0,0 +1,84 @@
1
+ # Release Guide
2
+
3
+ This guide is for maintainers publishing documentation to GitHub Pages and
4
+ package artifacts to PyPI.
5
+
6
+ ## Preconditions
7
+
8
+ - Work from a clean `master` branch.
9
+ - Release versions are derived from git tags by `setuptools-scm`.
10
+ - Never build or upload release artifacts from a dirty worktree.
11
+ - Keep PyPI credentials out of git.
12
+
13
+ Check the release state:
14
+
15
+ ```bash
16
+ git checkout master
17
+ git pull --ff-only origin master
18
+ git status
19
+ ```
20
+
21
+ `git status` should report no changes before continuing.
22
+
23
+ ## Tag The Release
24
+
25
+ Use the next release version, for example `0.50`:
26
+
27
+ ```bash
28
+ git tag -a 0.50 -m "Release 0.50"
29
+ git push origin master
30
+ git push origin 0.50
31
+ ```
32
+
33
+ The tag must point at the exact commit being released.
34
+
35
+ ## Publish Documentation
36
+
37
+ Build the MkDocs documentation:
38
+
39
+ ```bash
40
+ mkdocs build
41
+ ```
42
+
43
+ Publish the generated HTML to GitHub Pages:
44
+
45
+ ```bash
46
+ mkdocs gh-deploy
47
+ ```
48
+
49
+ This publishes the contents of `site/` to the `gh-pages` branch.
50
+
51
+ ## Publish To PyPI
52
+
53
+ Build and validate the distribution artifacts:
54
+
55
+ ```bash
56
+ ./setup.sh cleanup
57
+ ```
58
+
59
+ Upload the artifacts:
60
+
61
+ ```bash
62
+ ./setup.sh upload
63
+ ```
64
+
65
+ When prompted by `twine`, enter the PyPI API token. The token should start with
66
+ `pypi-`. It will not be displayed while typing or pasting.
67
+
68
+ ## Verify The Release
69
+
70
+ Install the published package in a fresh environment and confirm the version:
71
+
72
+ ```bash
73
+ python3 -m pip install --upgrade data-manipulation
74
+ python3 -c "import data_manipulation; print(data_manipulation.__version__)"
75
+ ```
76
+
77
+ ## Security Notes
78
+
79
+ - Do not commit PyPI API tokens, passwords, or `.pypirc` files with credentials.
80
+ - Do not add tokens to shell history as command-line arguments.
81
+ - Prefer entering the PyPI token at the `twine` prompt or using a local secret
82
+ store.
83
+ - If a token is accidentally committed or exposed, revoke it in PyPI immediately
84
+ and create a new one.
@@ -4,14 +4,12 @@ from data_manipulation import (
4
4
  boto3_,
5
5
  cryptography_,
6
6
  django_,
7
- flask_,
8
7
  geopandas_,
9
8
  kerberos_,
10
9
  mysql_connector_python_,
11
10
  openldap_,
12
11
  pandas_,
13
12
  postgres_,
14
- prometheus_,
15
13
  pyspark_,
16
14
  smtplib_,
17
15
  sqlalchemy_,
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.50'
22
- __version_tuple__ = version_tuple = (0, 50)
21
+ __version__ = version = '0.53'
22
+ __version_tuple__ = version_tuple = (0, 53)
23
23
 
24
- __commit_id__ = commit_id = 'g3a0e189a0'
24
+ __commit_id__ = commit_id = 'g2446c213a'
@@ -7,7 +7,11 @@ from pathlib import Path
7
7
  from typing import Any, Dict, List, Optional, Tuple, Union
8
8
  from urllib.parse import urlencode, urlparse, urlunparse
9
9
 
10
- from loguru import logger
10
+ try:
11
+ from loguru import logger
12
+ except ImportError:
13
+ import logging
14
+ logger = logging.getLogger(__name__)
11
15
 
12
16
 
13
17
  def deprecated(func):
@@ -82,27 +86,24 @@ def get_string_case_combination(str_: str) -> List[str]:
82
86
  return list(map("".join, itertools.product(*zip(str_.upper(), str_.lower()))))
83
87
 
84
88
 
85
- def get_none_variation() -> List[Union[None, str]]:
86
- """Returns a list of common variations of None/null values.
89
+ def get_none_variation() -> frozenset:
90
+ """Returns a frozenset of lowercase None/null string representations.
91
+
92
+ Use with .casefold() for case-insensitive membership tests:
93
+ item.casefold() in get_none_variation()
87
94
 
88
95
  Returns:
89
- list: List containing None and various string representations of null values.
96
+ frozenset: Frozenset of lowercase null string representations.
90
97
 
91
98
  Examples:
92
- >>> get_none_variation()
93
- [None, 'NONE', 'NONe', 'NOnE', 'NOne', 'NoNE', 'NoNe', 'NonE', 'None', 'nONE', 'nONe', 'nOnE', 'nOne', 'noNE', 'noNe', 'nonE', 'none', 'NULL', 'NULl', 'NUlL', 'NUll', 'NuLL', 'NuLl', 'NulL', 'Null', 'nULL', 'nULl', 'nUlL', 'nUll', 'nuLL', 'nuLl', 'nulL', 'null', 'NA', 'Na', 'nA', 'na', 'N.A', 'N.a', 'N.A', 'N.a', 'n.A', 'n.a', 'n.A', 'n.a', 'N.A.', 'N.A.', 'N.a.', 'N.a.', 'N.A.', 'N.A.', 'N.a.', 'N.a.', 'n.A.', 'n.A.', 'n.a.', 'n.a.', 'n.A.', 'n.A.', 'n.a.', 'n.a.', 'NAN', 'NAn', 'NaN', 'Nan', 'nAN', 'nAn', 'naN', 'nan', 'NIL', 'NIl', 'NiL', 'Nil', 'nIL', 'nIl', 'niL', 'nil']
99
+ >>> "none" in get_none_variation()
100
+ True
101
+ >>> "NONE".casefold() in get_none_variation()
102
+ True
103
+ >>> "valid".casefold() in get_none_variation()
104
+ False
94
105
  """
95
- variations = (
96
- [None]
97
- + get_string_case_combination("none")
98
- + get_string_case_combination("null")
99
- + get_string_case_combination("na")
100
- + get_string_case_combination("n.a")
101
- + get_string_case_combination("n.a.")
102
- + get_string_case_combination("nan")
103
- + get_string_case_combination("nil")
104
- )
105
- return variations
106
+ return frozenset({"none", "null", "na", "n.a", "n.a.", "nan", "nil"})
106
107
 
107
108
 
108
109
  def get_country_name_variation() -> dict:
@@ -172,35 +173,17 @@ def list_tuple_without_none(list_tuple: Union[List, Tuple]) -> Union[List, Tuple
172
173
 
173
174
  none_variations = get_none_variation()
174
175
 
175
- if isinstance(list_tuple, list):
176
- return [item for item in list_tuple if item and item not in none_variations]
177
- return tuple(item for item in list_tuple if item and item not in none_variations)
178
-
179
-
180
- @deprecated
181
- def string_boolean_to_int(boolean_str_rep: str) -> int:
182
- """Converts string boolean representations to integers.
183
-
184
- Args:
185
- boolean_str_rep (str): String representation of boolean value.
176
+ def _keep(item: Any) -> bool:
177
+ if not item:
178
+ return False
179
+ if isinstance(item, str):
180
+ return item.casefold() not in none_variations
181
+ return True
186
182
 
187
- Returns:
188
- int: 1 for true values, 0 for false values.
189
-
190
- Examples:
191
- >>> string_boolean_to_int("true")
192
- 1
193
- >>> string_boolean_to_int("True")
194
- 1
195
- """
196
- try:
197
- from distutils.util import strtobool
183
+ if isinstance(list_tuple, list):
184
+ return [item for item in list_tuple if _keep(item)]
185
+ return tuple(item for item in list_tuple if _keep(item))
198
186
 
199
- return strtobool(string_str_to_str(boolean_str_rep))
200
- except ValueError as e:
201
- raise ValueError(
202
- f"Invalid boolean string representation: {boolean_str_rep}"
203
- ) from e
204
187
 
205
188
 
206
189
  def string_dlt_to_dlt(dlt_str_rep: str) -> Union[Dict, List, Tuple]:
@@ -417,9 +400,14 @@ def parse_ps_aux(ps_aux_commands: str) -> List[List[str]]:
417
400
  check=True,
418
401
  )
419
402
  lines = output.stdout.strip().split("\n")
420
- if not lines:
403
+ if not lines or not lines[0]:
421
404
  return []
422
405
 
406
+ if "PID" not in lines[0] and "USER" not in lines[0]:
407
+ logger.warning(
408
+ "ps output header looks unexpected; "
409
+ "column split may be incorrect"
410
+ )
423
411
  n_columns = len(lines[0].split()) - 1
424
412
  return [line.split(None, n_columns) for line in lines if line]
425
413
  except subprocess.SubprocessError as e:
@@ -1,7 +1,11 @@
1
1
  import re
2
2
  from typing import TYPE_CHECKING, Optional
3
3
 
4
- from loguru import logger
4
+ try:
5
+ from loguru import logger
6
+ except ImportError:
7
+ import logging
8
+ logger = logging.getLogger(__name__)
5
9
 
6
10
  if TYPE_CHECKING:
7
11
  from bs4 import BeautifulSoup
@@ -31,15 +35,11 @@ def preprocess(html: str) -> Optional[str]:
31
35
  Reference: https://stackoverflow.com/questions/23241641
32
36
  """
33
37
 
34
- # remove leading and trailing whitespaces
35
- pattern = re.compile("(^[\s]+)|([\s]+$)", re.MULTILINE)
38
+ pattern = re.compile(r"(^[\s]+)|([\s]+$)", re.MULTILINE)
36
39
  html = re.sub(pattern, "", html)
37
- # convert newlines to spaces, this preserves newline delimiters
38
40
  html = re.sub("\n", " ", html)
39
- # remove whitespaces before opening tags
40
- html = re.sub("[\s]+<", "<", html)
41
- # remove whitespaces after closing tags
42
- html = re.sub(">[\s]+", ">", html)
41
+ html = re.sub(r"[\s]+<", "<", html)
42
+ html = re.sub(r">[\s]+", ">", html)
43
43
  return html
44
44
 
45
45
 
@@ -1,7 +1,11 @@
1
1
  import os
2
2
  from typing import Any, Dict, List, Optional, Union
3
3
 
4
- from loguru import logger
4
+ try:
5
+ from loguru import logger
6
+ except ImportError:
7
+ import logging
8
+ logger = logging.getLogger(__name__)
5
9
 
6
10
  # Constants - now serving as defaults
7
11
  DEFAULT_MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024 # 10MB
@@ -2,8 +2,17 @@ import os
2
2
  from pathlib import Path
3
3
  from typing import Union
4
4
 
5
- from cryptography.fernet import Fernet, InvalidToken
6
- from loguru import logger
5
+ try:
6
+ from cryptography.fernet import Fernet, InvalidToken
7
+ HAS_CRYPTOGRAPHY = True
8
+ except ImportError:
9
+ HAS_CRYPTOGRAPHY = False
10
+
11
+ try:
12
+ from loguru import logger
13
+ except ImportError:
14
+ import logging
15
+ logger = logging.getLogger(__name__)
7
16
 
8
17
 
9
18
  def generate_fernet_key(
@@ -2,7 +2,11 @@ import os
2
2
  import sys
3
3
  from typing import Dict, List, Optional, Tuple
4
4
 
5
- from loguru import logger
5
+ try:
6
+ from loguru import logger
7
+ except ImportError:
8
+ import logging
9
+ logger = logging.getLogger(__name__)
6
10
 
7
11
 
8
12
  def init_django(
@@ -151,12 +155,12 @@ def django_validate_phone(
151
155
 
152
156
  try:
153
157
  return PhoneNumber.from_string(phone).as_e164
154
- except:
158
+ except Exception:
155
159
  try:
156
160
  return PhoneNumber.from_string(phone, region=region).as_e164
157
161
  except Exception as e:
158
162
  logger.error(e)
159
- return
163
+ return None
160
164
 
161
165
 
162
166
  if __name__ == "__main__":
@@ -1,10 +1,16 @@
1
- from typing import TypeVar
1
+ from __future__ import annotations
2
2
 
3
- import numpy as np
4
- from numpy.typing import NDArray
3
+ from typing import TypeVar
5
4
 
6
- # Type variable for numeric types (float or numpy array)
7
- NumericType = TypeVar("NumericType", float, NDArray[np.float64])
5
+ try:
6
+ import numpy as np
7
+ from numpy.typing import NDArray
8
+ HAS_NUMPY = True
9
+ NumericType = TypeVar("NumericType", float, NDArray[np.float64])
10
+ except ImportError:
11
+ np = None
12
+ HAS_NUMPY = False
13
+ NumericType = TypeVar("NumericType", float, float)
8
14
 
9
15
  # Earth's radius in kilometers
10
16
  EARTH_RADIUS_KM = 6371.0
@@ -2,7 +2,11 @@ import os
2
2
  import shlex
3
3
  import subprocess
4
4
 
5
- from loguru import logger
5
+ try:
6
+ from loguru import logger
7
+ except ImportError:
8
+ import logging
9
+ logger = logging.getLogger(__name__)
6
10
 
7
11
 
8
12
  def keytab_valid(
@@ -1,7 +1,11 @@
1
1
  from contextlib import contextmanager
2
2
  from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Union
3
3
 
4
- from loguru import logger
4
+ try:
5
+ from loguru import logger
6
+ except ImportError:
7
+ import logging
8
+ logger = logging.getLogger(__name__)
5
9
 
6
10
  if TYPE_CHECKING:
7
11
  from mysql.connector.connection import MySQLConnection