dycw-utilities 0.108.2__tar.gz → 0.108.3__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.
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/PKG-INFO +1 -1
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/pyproject.toml +2 -2
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_functions.py +21 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_parse.py +8 -0
- dycw_utilities-0.108.3/src/tests/test_python_dotenv.py +148 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/functions.py +28 -0
- dycw_utilities-0.108.3/src/utilities/python_dotenv.py +116 -0
- dycw_utilities-0.108.2/src/tests/test_python_dotenv.py +0 -452
- dycw_utilities-0.108.2/src/utilities/python_dotenv.py +0 -259
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/.gitignore +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/LICENSE +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/README.md +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/test_async_service/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/test_async_service/__main__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/test_async_service/run.sh +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/scripts/test_queue_processor/run.sh +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_astor.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/astor.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/click.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/git.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/http.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/math.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/os.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/period.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/random.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/re.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/text.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/types.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/version.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.108.2 → dycw_utilities-0.108.3}/src/utilities/zoneinfo.py +0 -0
@@ -89,7 +89,7 @@ dependencies = [
|
|
89
89
|
name = "dycw-utilities"
|
90
90
|
readme = "README.md"
|
91
91
|
requires-python = ">= 3.12"
|
92
|
-
version = "0.108.
|
92
|
+
version = "0.108.3"
|
93
93
|
|
94
94
|
[project.optional-dependencies]
|
95
95
|
test = [
|
@@ -332,7 +332,7 @@ zzz-test-zoneinfo = [
|
|
332
332
|
# bump-my-version
|
333
333
|
[tool.bumpversion]
|
334
334
|
allow_dirty = true
|
335
|
-
current_version = "0.108.
|
335
|
+
current_version = "0.108.3"
|
336
336
|
|
337
337
|
[[tool.bumpversion.files]]
|
338
338
|
filename = "src/utilities/__init__.py"
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|
5
5
|
from functools import cache, cached_property, lru_cache, partial, wraps
|
6
6
|
from itertools import chain
|
7
7
|
from operator import neg
|
8
|
+
from pathlib import Path
|
8
9
|
from types import NoneType
|
9
10
|
from typing import TYPE_CHECKING, Any, ClassVar, ParamSpec, TypeVar, cast
|
10
11
|
|
@@ -37,6 +38,7 @@ from utilities.functions import (
|
|
37
38
|
EnsureMemberError,
|
38
39
|
EnsureNotNoneError,
|
39
40
|
EnsureNumberError,
|
41
|
+
EnsurePathError,
|
40
42
|
EnsureSizedError,
|
41
43
|
EnsureSizedNotStrError,
|
42
44
|
EnsureStrError,
|
@@ -56,6 +58,7 @@ from utilities.functions import (
|
|
56
58
|
ensure_member,
|
57
59
|
ensure_not_none,
|
58
60
|
ensure_number,
|
61
|
+
ensure_path,
|
59
62
|
ensure_sized,
|
60
63
|
ensure_sized_not_str,
|
61
64
|
ensure_str,
|
@@ -336,6 +339,24 @@ class TestEnsureNumber:
|
|
336
339
|
_ = ensure_number(sentinel, nullable=nullable)
|
337
340
|
|
338
341
|
|
342
|
+
class TestEnsurePath:
|
343
|
+
@given(case=sampled_from([(Path.home(), False), (Path.home(), True), (None, True)]))
|
344
|
+
def test_main(self, *, case: tuple[int | None, bool]) -> None:
|
345
|
+
obj, nullable = case
|
346
|
+
_ = ensure_path(obj, nullable=nullable)
|
347
|
+
|
348
|
+
@given(
|
349
|
+
case=sampled_from([
|
350
|
+
(False, "Object '.*' of type '.*' must be a Path"),
|
351
|
+
(True, "Object '.*' of type '.*' must be a Path or None"),
|
352
|
+
])
|
353
|
+
)
|
354
|
+
def test_error(self, *, case: tuple[bool, str]) -> None:
|
355
|
+
nullable, match = case
|
356
|
+
with raises(EnsurePathError, match=match):
|
357
|
+
_ = ensure_path(sentinel, nullable=nullable)
|
358
|
+
|
359
|
+
|
339
360
|
class TestEnsureSized:
|
340
361
|
@given(obj=sampled_from([[], (), ""]))
|
341
362
|
def test_main(self, *, obj: Any) -> None:
|
@@ -12,6 +12,7 @@ from hypothesis.strategies import booleans, dates, floats, integers, sampled_fro
|
|
12
12
|
from pytest import raises
|
13
13
|
|
14
14
|
from tests.test_operator import TruthEnum
|
15
|
+
from utilities.functions import ensure_path
|
15
16
|
from utilities.hypothesis import (
|
16
17
|
local_datetimes,
|
17
18
|
paths,
|
@@ -101,6 +102,13 @@ class TestParseText:
|
|
101
102
|
result = parse_text(Path, text)
|
102
103
|
assert result == path
|
103
104
|
|
105
|
+
@given(path=paths())
|
106
|
+
def test_path_expanded(self, *, path: Path) -> None:
|
107
|
+
path_use = Path("~", path)
|
108
|
+
text = str(path_use)
|
109
|
+
result = ensure_path(parse_text(Path, text))
|
110
|
+
assert result == result.expanduser()
|
111
|
+
|
104
112
|
def test_sentinel(self) -> None:
|
105
113
|
text = str(sentinel)
|
106
114
|
result = parse_text(Sentinel, text)
|
@@ -0,0 +1,148 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from re import DOTALL
|
6
|
+
from typing import TYPE_CHECKING
|
7
|
+
|
8
|
+
from hypothesis import given
|
9
|
+
from hypothesis.strategies import DataObject, booleans, data, integers, sampled_from
|
10
|
+
from pytest import raises
|
11
|
+
|
12
|
+
from utilities.errors import ImpossibleCaseError
|
13
|
+
from utilities.hypothesis import git_repos, settings_with_reduced_examples, text_ascii
|
14
|
+
from utilities.os import temp_environ
|
15
|
+
from utilities.python_dotenv import (
|
16
|
+
_LoadSettingsDuplicateKeysError,
|
17
|
+
_LoadSettingsEmptyError,
|
18
|
+
_LoadSettingsFileNotFoundError,
|
19
|
+
_LoadSettingsParseTextError,
|
20
|
+
load_settings,
|
21
|
+
)
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from pathlib import Path
|
25
|
+
|
26
|
+
|
27
|
+
class TestLoadSettings:
|
28
|
+
@given(
|
29
|
+
data=data(),
|
30
|
+
root=git_repos(),
|
31
|
+
key_file=sampled_from(["key", "KEY"]),
|
32
|
+
value_file=text_ascii(),
|
33
|
+
use_env=booleans(),
|
34
|
+
)
|
35
|
+
@settings_with_reduced_examples()
|
36
|
+
def test_main(
|
37
|
+
self,
|
38
|
+
*,
|
39
|
+
data: DataObject,
|
40
|
+
root: Path,
|
41
|
+
key_file: str,
|
42
|
+
value_file: str,
|
43
|
+
use_env: bool,
|
44
|
+
) -> None:
|
45
|
+
with root.joinpath(".env").open(mode="w") as fh:
|
46
|
+
_ = fh.write(f"{key_file} = {value_file}\n")
|
47
|
+
|
48
|
+
@dataclass(kw_only=True, slots=True)
|
49
|
+
class SettingsLower:
|
50
|
+
key: str
|
51
|
+
|
52
|
+
@dataclass(kw_only=True, slots=True)
|
53
|
+
class SettingsUpper:
|
54
|
+
KEY: str
|
55
|
+
|
56
|
+
SettingsUse = data.draw(sampled_from([SettingsLower, SettingsUpper])) # noqa: N806
|
57
|
+
if use_env:
|
58
|
+
key_env = data.draw(sampled_from(["key", "KEY"]))
|
59
|
+
value_env = data.draw(text_ascii())
|
60
|
+
with temp_environ({key_env: value_env}):
|
61
|
+
settings = load_settings(SettingsUse, cwd=root)
|
62
|
+
exp_value = value_env
|
63
|
+
else:
|
64
|
+
settings = load_settings(SettingsUse, cwd=root)
|
65
|
+
exp_value = value_file
|
66
|
+
|
67
|
+
if SettingsUse is SettingsLower:
|
68
|
+
expected = SettingsLower(key=exp_value)
|
69
|
+
elif SettingsUse is SettingsUpper:
|
70
|
+
expected = SettingsUpper(KEY=exp_value)
|
71
|
+
else:
|
72
|
+
raise ImpossibleCaseError(case=[f"{SettingsUse=}"])
|
73
|
+
assert settings == expected
|
74
|
+
|
75
|
+
@given(root=git_repos(), value=text_ascii())
|
76
|
+
@settings_with_reduced_examples()
|
77
|
+
def test_file_extra_key(self, *, root: Path, value: str) -> None:
|
78
|
+
@dataclass(kw_only=True, slots=True)
|
79
|
+
class Settings:
|
80
|
+
key: str
|
81
|
+
|
82
|
+
with root.joinpath(".env").open(mode="w") as fh:
|
83
|
+
_ = fh.write(f"key = {value}\n")
|
84
|
+
_ = fh.write(f"other = {value}\n")
|
85
|
+
|
86
|
+
settings = load_settings(Settings, cwd=root)
|
87
|
+
expected = Settings(key=value)
|
88
|
+
assert settings == expected
|
89
|
+
|
90
|
+
@given(root=git_repos())
|
91
|
+
@settings_with_reduced_examples()
|
92
|
+
def test_error_file_not_found(self, *, root: Path) -> None:
|
93
|
+
@dataclass(kw_only=True, slots=True)
|
94
|
+
class Settings:
|
95
|
+
KEY: str
|
96
|
+
|
97
|
+
with raises(_LoadSettingsFileNotFoundError, match=r"Path '.*' must exist"):
|
98
|
+
_ = load_settings(Settings, cwd=root)
|
99
|
+
|
100
|
+
@given(root=git_repos(), value=integers())
|
101
|
+
@settings_with_reduced_examples()
|
102
|
+
def test_error_duplicate_keys(self, *, root: Path, value: int) -> None:
|
103
|
+
@dataclass(kw_only=True, slots=True)
|
104
|
+
class Settings:
|
105
|
+
key: str
|
106
|
+
|
107
|
+
with root.joinpath(".env").open(mode="w") as fh:
|
108
|
+
_ = fh.write(f"key = {value}\n")
|
109
|
+
_ = fh.write(f"KEY = {value}\n")
|
110
|
+
|
111
|
+
with raises(
|
112
|
+
_LoadSettingsDuplicateKeysError,
|
113
|
+
match=re.compile(
|
114
|
+
r"Mapping .* keys must not contain duplicates \(modulo case\); got .*",
|
115
|
+
flags=DOTALL,
|
116
|
+
),
|
117
|
+
):
|
118
|
+
_ = load_settings(Settings, cwd=root)
|
119
|
+
|
120
|
+
@given(root=git_repos())
|
121
|
+
@settings_with_reduced_examples()
|
122
|
+
def test_error_field_missing(self, *, root: Path) -> None:
|
123
|
+
@dataclass(kw_only=True, slots=True)
|
124
|
+
class Settings:
|
125
|
+
key: str
|
126
|
+
|
127
|
+
root.joinpath(".env").touch()
|
128
|
+
|
129
|
+
with raises(
|
130
|
+
_LoadSettingsEmptyError, match=r"Field 'key' must exist \(modulo case\)"
|
131
|
+
):
|
132
|
+
_ = load_settings(Settings, cwd=root)
|
133
|
+
|
134
|
+
@given(root=git_repos())
|
135
|
+
@settings_with_reduced_examples()
|
136
|
+
def test_error_parse_text(self, *, root: Path) -> None:
|
137
|
+
@dataclass(kw_only=True, slots=True)
|
138
|
+
class Settings:
|
139
|
+
key: int
|
140
|
+
|
141
|
+
with root.joinpath(".env").open(mode="w") as fh:
|
142
|
+
_ = fh.write("key = '...'\n")
|
143
|
+
|
144
|
+
with raises(
|
145
|
+
_LoadSettingsParseTextError,
|
146
|
+
match=r"Unable to parse field 'key' of type <class 'int'>; got '...'",
|
147
|
+
):
|
148
|
+
_ = load_settings(Settings, cwd=root)
|
@@ -5,6 +5,7 @@ from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
5
5
|
from dataclasses import asdict, dataclass, is_dataclass
|
6
6
|
from functools import _lru_cache_wrapper, cached_property, partial, reduce, wraps
|
7
7
|
from inspect import getattr_static
|
8
|
+
from pathlib import Path
|
8
9
|
from re import findall
|
9
10
|
from types import (
|
10
11
|
BuiltinFunctionType,
|
@@ -405,6 +406,31 @@ class EnsureNumberError(Exception):
|
|
405
406
|
##
|
406
407
|
|
407
408
|
|
409
|
+
@overload
|
410
|
+
def ensure_path(obj: Any, /, *, nullable: bool) -> Path | None: ...
|
411
|
+
@overload
|
412
|
+
def ensure_path(obj: Any, /, *, nullable: Literal[False] = False) -> Path: ...
|
413
|
+
def ensure_path(obj: Any, /, *, nullable: bool = False) -> Path | None:
|
414
|
+
"""Ensure an object is a Path."""
|
415
|
+
try:
|
416
|
+
return ensure_class(obj, Path, nullable=nullable)
|
417
|
+
except EnsureClassError as error:
|
418
|
+
raise EnsurePathError(obj=error.obj, nullable=nullable) from None
|
419
|
+
|
420
|
+
|
421
|
+
@dataclass(kw_only=True, slots=True)
|
422
|
+
class EnsurePathError(Exception):
|
423
|
+
obj: Any
|
424
|
+
nullable: bool
|
425
|
+
|
426
|
+
@override
|
427
|
+
def __str__(self) -> str:
|
428
|
+
return _make_error_msg(self.obj, "a Path", nullable=self.nullable)
|
429
|
+
|
430
|
+
|
431
|
+
##
|
432
|
+
|
433
|
+
|
408
434
|
def ensure_sized(obj: Any, /) -> Sized:
|
409
435
|
"""Ensure an object is sized."""
|
410
436
|
if is_sized(obj):
|
@@ -985,6 +1011,7 @@ __all__ = [
|
|
985
1011
|
"EnsureMemberError",
|
986
1012
|
"EnsureNotNoneError",
|
987
1013
|
"EnsureNumberError",
|
1014
|
+
"EnsurePathError",
|
988
1015
|
"EnsureSizedError",
|
989
1016
|
"EnsureSizedNotStrError",
|
990
1017
|
"EnsureStrError",
|
@@ -1004,6 +1031,7 @@ __all__ = [
|
|
1004
1031
|
"ensure_member",
|
1005
1032
|
"ensure_not_none",
|
1006
1033
|
"ensure_number",
|
1034
|
+
"ensure_path",
|
1007
1035
|
"ensure_sized",
|
1008
1036
|
"ensure_sized_not_str",
|
1009
1037
|
"ensure_str",
|
@@ -0,0 +1,116 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from functools import partial
|
5
|
+
from os import environ
|
6
|
+
from typing import TYPE_CHECKING, Any, override
|
7
|
+
|
8
|
+
from dotenv import dotenv_values
|
9
|
+
|
10
|
+
from utilities.dataclasses import (
|
11
|
+
_MappingToDataclassEmptyError,
|
12
|
+
_YieldFieldsClass,
|
13
|
+
mapping_to_dataclass,
|
14
|
+
)
|
15
|
+
from utilities.git import get_repo_root
|
16
|
+
from utilities.iterables import MergeStrMappingsError, merge_str_mappings
|
17
|
+
from utilities.parse import ParseTextError, parse_text
|
18
|
+
from utilities.pathlib import PWD
|
19
|
+
from utilities.reprlib import get_repr
|
20
|
+
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
from collections.abc import Mapping
|
23
|
+
from pathlib import Path
|
24
|
+
|
25
|
+
from utilities.types import PathLike, StrMapping, TDataclass
|
26
|
+
|
27
|
+
|
28
|
+
def load_settings(
|
29
|
+
cls: type[TDataclass],
|
30
|
+
/,
|
31
|
+
*,
|
32
|
+
cwd: PathLike = PWD,
|
33
|
+
globalns: StrMapping | None = None,
|
34
|
+
localns: StrMapping | None = None,
|
35
|
+
) -> TDataclass:
|
36
|
+
"""Load a set of settings from the `.env` file."""
|
37
|
+
path = get_repo_root(cwd=cwd).joinpath(".env")
|
38
|
+
if not path.exists():
|
39
|
+
raise _LoadSettingsFileNotFoundError(path=path) from None
|
40
|
+
maybe_values_dotenv = dotenv_values(path)
|
41
|
+
try:
|
42
|
+
maybe_values = merge_str_mappings(maybe_values_dotenv, environ)
|
43
|
+
except MergeStrMappingsError as error:
|
44
|
+
raise _LoadSettingsDuplicateKeysError(
|
45
|
+
path=path, values=error.mapping, counts=error.counts
|
46
|
+
) from None
|
47
|
+
values = {k: v for k, v in maybe_values.items() if v is not None}
|
48
|
+
try:
|
49
|
+
return mapping_to_dataclass(
|
50
|
+
cls,
|
51
|
+
values,
|
52
|
+
globalns=globalns,
|
53
|
+
localns=localns,
|
54
|
+
post=partial(_load_settings_post, path=path, values=values),
|
55
|
+
)
|
56
|
+
except _MappingToDataclassEmptyError as error:
|
57
|
+
raise _LoadSettingsEmptyError(
|
58
|
+
path=path, values=error.mapping, field=error.field
|
59
|
+
) from None
|
60
|
+
|
61
|
+
|
62
|
+
def _load_settings_post(
|
63
|
+
field: _YieldFieldsClass[Any], text: str, /, *, path: Path, values: StrMapping
|
64
|
+
) -> Any:
|
65
|
+
try:
|
66
|
+
return parse_text(field.type_, text)
|
67
|
+
except ParseTextError:
|
68
|
+
raise _LoadSettingsParseTextError(
|
69
|
+
path=path, values=values, field=field, text=text
|
70
|
+
) from None
|
71
|
+
|
72
|
+
|
73
|
+
@dataclass(kw_only=True, slots=True)
|
74
|
+
class LoadSettingsError(Exception):
|
75
|
+
path: Path
|
76
|
+
|
77
|
+
|
78
|
+
@dataclass(kw_only=True, slots=True)
|
79
|
+
class _LoadSettingsDuplicateKeysError(LoadSettingsError):
|
80
|
+
values: StrMapping
|
81
|
+
counts: Mapping[str, int]
|
82
|
+
|
83
|
+
@override
|
84
|
+
def __str__(self) -> str:
|
85
|
+
return f"Mapping {get_repr(dict(self.values))} keys must not contain duplicates (modulo case); got {get_repr(self.counts)}"
|
86
|
+
|
87
|
+
|
88
|
+
@dataclass(kw_only=True, slots=True)
|
89
|
+
class _LoadSettingsEmptyError(LoadSettingsError):
|
90
|
+
values: StrMapping
|
91
|
+
field: str
|
92
|
+
|
93
|
+
@override
|
94
|
+
def __str__(self) -> str:
|
95
|
+
return f"Field {self.field!r} must exist (modulo case)"
|
96
|
+
|
97
|
+
|
98
|
+
@dataclass(kw_only=True, slots=True)
|
99
|
+
class _LoadSettingsFileNotFoundError(LoadSettingsError):
|
100
|
+
@override
|
101
|
+
def __str__(self) -> str:
|
102
|
+
return f"Path {str(self.path)!r} must exist"
|
103
|
+
|
104
|
+
|
105
|
+
@dataclass(kw_only=True, slots=True)
|
106
|
+
class _LoadSettingsParseTextError(LoadSettingsError):
|
107
|
+
values: StrMapping
|
108
|
+
field: _YieldFieldsClass[Any]
|
109
|
+
text: str
|
110
|
+
|
111
|
+
@override
|
112
|
+
def __str__(self) -> str:
|
113
|
+
return f"Unable to parse field {self.field.name!r} of type {self.field.type_!r}; got {self.text!r}"
|
114
|
+
|
115
|
+
|
116
|
+
__all__ = ["LoadSettingsError", "load_settings"]
|