dycw-utilities 0.108.0__tar.gz → 0.108.2__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.0 → dycw_utilities-0.108.2}/PKG-INFO +1 -1
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/pyproject.toml +2 -2
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_datetime.py +4 -7
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_functions.py +17 -13
- dycw_utilities-0.108.2/src/tests/test_parse.py +234 -0
- dycw_utilities-0.108.2/src/tests/test_sentinel.py +45 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_text.py +21 -4
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/functions.py +13 -4
- dycw_utilities-0.108.2/src/utilities/parse.py +129 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/sentinel.py +28 -1
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/text.py +22 -1
- dycw_utilities-0.108.0/src/tests/test_sentinel.py +0 -22
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/.gitignore +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/LICENSE +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/README.md +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/__main__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/run.sh +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/run.sh +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_astor.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/astor.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/click.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/git.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/http.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/math.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/os.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/period.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/random.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/re.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/types.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/version.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.108.0 → dycw_utilities-0.108.2}/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.2"
|
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.2"
|
336
336
|
|
337
337
|
[[tool.bumpversion.files]]
|
338
338
|
filename = "src/utilities/__init__.py"
|
@@ -795,13 +795,10 @@ class TestIsLocalDateTime:
|
|
795
795
|
|
796
796
|
|
797
797
|
class TestIsSubClassDateNotDateTime:
|
798
|
-
@given(
|
799
|
-
def test_date(self, *,
|
800
|
-
|
801
|
-
|
802
|
-
@given(datetime=datetimes())
|
803
|
-
def test_datetime(self, *, datetime: dt.datetime) -> None:
|
804
|
-
assert not is_subclass_date_not_datetime(type(datetime))
|
798
|
+
@given(case=sampled_from([(dt.date, True), (dt.datetime, False)]))
|
799
|
+
def test_date(self, *, case: tuple[type[Any], bool]) -> None:
|
800
|
+
type_, expected = case
|
801
|
+
assert is_subclass_date_not_datetime(type_) is expected
|
805
802
|
|
806
803
|
|
807
804
|
class TestIsWeekday:
|
@@ -70,6 +70,7 @@ from utilities.functions import (
|
|
70
70
|
is_dataclass_class,
|
71
71
|
is_dataclass_instance,
|
72
72
|
is_hashable,
|
73
|
+
is_instance_int_not_bool,
|
73
74
|
is_iterable_of,
|
74
75
|
is_none,
|
75
76
|
is_not_none,
|
@@ -78,7 +79,7 @@ from utilities.functions import (
|
|
78
79
|
is_sized,
|
79
80
|
is_sized_not_str,
|
80
81
|
is_string_mapping,
|
81
|
-
|
82
|
+
is_subclass_int_not_bool,
|
82
83
|
is_tuple,
|
83
84
|
is_tuple_or_str_mapping,
|
84
85
|
make_isinstance,
|
@@ -599,6 +600,16 @@ class TestIsHashable:
|
|
599
600
|
assert is_hashable(obj) is expected
|
600
601
|
|
601
602
|
|
603
|
+
class TestIsInstanceIntNotBool:
|
604
|
+
@given(int_=integers())
|
605
|
+
def test_int(self, *, int_: int) -> None:
|
606
|
+
assert is_instance_int_not_bool(int_)
|
607
|
+
|
608
|
+
@given(bool_=booleans())
|
609
|
+
def test_bool(self, *, bool_: bool) -> None:
|
610
|
+
assert not is_instance_int_not_bool(bool_)
|
611
|
+
|
612
|
+
|
602
613
|
class TestIsIterableOf:
|
603
614
|
@given(
|
604
615
|
case=sampled_from([
|
@@ -719,18 +730,11 @@ class TestIsStringMapping:
|
|
719
730
|
assert result is expected
|
720
731
|
|
721
732
|
|
722
|
-
class
|
723
|
-
@given(
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
x, y, expected = case
|
728
|
-
assert is_subclass_except_bool_int(x, y) is expected
|
729
|
-
|
730
|
-
def test_subclass_of_int(self) -> None:
|
731
|
-
class MyInt(int): ...
|
732
|
-
|
733
|
-
assert not is_subclass_except_bool_int(bool, MyInt)
|
733
|
+
class TestIsSubclassIntNotBool:
|
734
|
+
@given(case=sampled_from([(int, True), (bool, False)]))
|
735
|
+
def test_main(self, *, case: tuple[type[Any], bool]) -> None:
|
736
|
+
type_, expected = case
|
737
|
+
assert is_subclass_int_not_bool(type_) is expected
|
734
738
|
|
735
739
|
|
736
740
|
class TestIsTuple:
|
@@ -0,0 +1,234 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import datetime as dt
|
4
|
+
from collections.abc import Iterable
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from pathlib import Path
|
7
|
+
from types import NoneType
|
8
|
+
from typing import Literal
|
9
|
+
|
10
|
+
from hypothesis import given
|
11
|
+
from hypothesis.strategies import booleans, dates, floats, integers, sampled_from, times
|
12
|
+
from pytest import raises
|
13
|
+
|
14
|
+
from tests.test_operator import TruthEnum
|
15
|
+
from utilities.hypothesis import (
|
16
|
+
local_datetimes,
|
17
|
+
paths,
|
18
|
+
text_ascii,
|
19
|
+
timedeltas_2w,
|
20
|
+
versions,
|
21
|
+
zoned_datetimes,
|
22
|
+
)
|
23
|
+
from utilities.math import is_equal
|
24
|
+
from utilities.parse import ParseTextError, parse_text
|
25
|
+
from utilities.sentinel import Sentinel, sentinel
|
26
|
+
from utilities.version import Version
|
27
|
+
from utilities.whenever import (
|
28
|
+
serialize_date,
|
29
|
+
serialize_datetime,
|
30
|
+
serialize_time,
|
31
|
+
serialize_timedelta,
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
class TestParseText:
|
36
|
+
@given(value=booleans())
|
37
|
+
def test_bool(self, *, value: bool) -> None:
|
38
|
+
text = str(value)
|
39
|
+
result = parse_text(bool, text)
|
40
|
+
assert result is value
|
41
|
+
|
42
|
+
@given(date=dates())
|
43
|
+
def test_date(self, *, date: dt.date) -> None:
|
44
|
+
text = serialize_date(date)
|
45
|
+
result = parse_text(dt.date, text)
|
46
|
+
assert result == date
|
47
|
+
|
48
|
+
@given(datetime=local_datetimes() | zoned_datetimes())
|
49
|
+
def test_datetime(self, *, datetime: dt.datetime) -> None:
|
50
|
+
text = serialize_datetime(datetime)
|
51
|
+
result = parse_text(dt.datetime, text)
|
52
|
+
assert result == datetime
|
53
|
+
|
54
|
+
@given(truth=sampled_from(TruthEnum))
|
55
|
+
def test_enum(self, *, truth: TruthEnum) -> None:
|
56
|
+
text = truth.name
|
57
|
+
result = parse_text(TruthEnum, text)
|
58
|
+
assert result is truth
|
59
|
+
|
60
|
+
@given(value=floats())
|
61
|
+
def test_float(self, *, value: float) -> None:
|
62
|
+
text = str(value)
|
63
|
+
result = parse_text(float, text)
|
64
|
+
assert is_equal(result, value)
|
65
|
+
|
66
|
+
@given(value=integers())
|
67
|
+
def test_int(self, *, value: int) -> None:
|
68
|
+
text = str(value)
|
69
|
+
result = parse_text(int, text)
|
70
|
+
assert result == value
|
71
|
+
|
72
|
+
@given(truth=sampled_from(["true", "false"]))
|
73
|
+
def test_literal(self, *, truth: Literal["true", "false"]) -> None:
|
74
|
+
result = parse_text(Literal["true", "false"], truth)
|
75
|
+
assert result == truth
|
76
|
+
|
77
|
+
def test_nullable_int_none(self) -> None:
|
78
|
+
text = str(None)
|
79
|
+
result = parse_text(int | None, text)
|
80
|
+
assert result is None
|
81
|
+
|
82
|
+
@given(value=integers())
|
83
|
+
def test_nullable_int_int(self, *, value: int) -> None:
|
84
|
+
text = str(value)
|
85
|
+
result = parse_text(int | None, text)
|
86
|
+
assert result == value
|
87
|
+
|
88
|
+
def test_none(self) -> None:
|
89
|
+
text = str(None)
|
90
|
+
result = parse_text(None, text)
|
91
|
+
assert result is None
|
92
|
+
|
93
|
+
def test_none_type(self) -> None:
|
94
|
+
text = str(None)
|
95
|
+
result = parse_text(NoneType, text)
|
96
|
+
assert result is None
|
97
|
+
|
98
|
+
@given(path=paths())
|
99
|
+
def test_path(self, *, path: Path) -> None:
|
100
|
+
text = str(path)
|
101
|
+
result = parse_text(Path, text)
|
102
|
+
assert result == path
|
103
|
+
|
104
|
+
def test_sentinel(self) -> None:
|
105
|
+
text = str(sentinel)
|
106
|
+
result = parse_text(Sentinel, text)
|
107
|
+
assert result is sentinel
|
108
|
+
|
109
|
+
@given(text=text_ascii())
|
110
|
+
def test_str(self, *, text: str) -> None:
|
111
|
+
result = parse_text(str, text)
|
112
|
+
assert result == text
|
113
|
+
|
114
|
+
@given(time=times())
|
115
|
+
def test_time(self, *, time: dt.time) -> None:
|
116
|
+
text = serialize_time(time)
|
117
|
+
result = parse_text(dt.time, text)
|
118
|
+
assert result == time
|
119
|
+
|
120
|
+
@given(timedelta=timedeltas_2w())
|
121
|
+
def test_timedelta(self, *, timedelta: dt.timedelta) -> None:
|
122
|
+
text = serialize_timedelta(timedelta)
|
123
|
+
result = parse_text(dt.timedelta, text)
|
124
|
+
assert result == timedelta
|
125
|
+
|
126
|
+
@given(version=versions())
|
127
|
+
def test_version(self, *, version: Version) -> None:
|
128
|
+
text = str(version)
|
129
|
+
result = parse_text(Version, text)
|
130
|
+
assert result == version
|
131
|
+
|
132
|
+
def test_error_bool(self) -> None:
|
133
|
+
with raises(
|
134
|
+
ParseTextError, match="Unable to parse <class 'bool'>; got 'invalid'"
|
135
|
+
):
|
136
|
+
_ = parse_text(bool, "invalid")
|
137
|
+
|
138
|
+
def test_error_date(self) -> None:
|
139
|
+
with raises(
|
140
|
+
ParseTextError,
|
141
|
+
match=r"Unable to parse <class 'datetime\.date'>; got 'invalid'",
|
142
|
+
):
|
143
|
+
_ = parse_text(dt.date, "invalid")
|
144
|
+
|
145
|
+
def test_error_datetime(self) -> None:
|
146
|
+
with raises(
|
147
|
+
ParseTextError,
|
148
|
+
match=r"Unable to parse <class 'datetime\.datetime'>; got 'invalid'",
|
149
|
+
):
|
150
|
+
_ = parse_text(dt.datetime, "invalid")
|
151
|
+
|
152
|
+
def test_error_enum(self) -> None:
|
153
|
+
with raises(
|
154
|
+
ParseTextError, match="Unable to parse <enum 'TruthEnum'>; got 'invalid'"
|
155
|
+
):
|
156
|
+
_ = parse_text(TruthEnum, "invalid")
|
157
|
+
|
158
|
+
def test_error_float(self) -> None:
|
159
|
+
with raises(
|
160
|
+
ParseTextError, match="Unable to parse <class 'float'>; got 'invalid'"
|
161
|
+
):
|
162
|
+
_ = parse_text(float, "invalid")
|
163
|
+
|
164
|
+
def test_error_int(self) -> None:
|
165
|
+
with raises(
|
166
|
+
ParseTextError, match="Unable to parse <class 'int'>; got 'invalid'"
|
167
|
+
):
|
168
|
+
_ = parse_text(int, "invalid")
|
169
|
+
|
170
|
+
def test_error_none(self) -> None:
|
171
|
+
with raises(ParseTextError, match="Unable to parse None; got 'invalid'"):
|
172
|
+
_ = parse_text(None, "invalid")
|
173
|
+
|
174
|
+
def test_error_none_type(self) -> None:
|
175
|
+
with raises(
|
176
|
+
ParseTextError, match="Unable to parse <class 'NoneType'>; got 'invalid'"
|
177
|
+
):
|
178
|
+
_ = parse_text(NoneType, "invalid")
|
179
|
+
|
180
|
+
def test_error_nullable_int(self) -> None:
|
181
|
+
with raises(
|
182
|
+
ParseTextError, match=r"Unable to parse int \| None; got 'invalid'"
|
183
|
+
):
|
184
|
+
_ = parse_text(int | None, "invalid")
|
185
|
+
|
186
|
+
def test_error_nullable_not_type(self) -> None:
|
187
|
+
with raises(
|
188
|
+
ParseTextError,
|
189
|
+
match=r"Unable to parse collections\.abc\.Iterable\[None\] \| None; got 'invalid'",
|
190
|
+
):
|
191
|
+
_ = parse_text(Iterable[None] | None, "invalid")
|
192
|
+
|
193
|
+
def test_error_sentinel(self) -> None:
|
194
|
+
with raises(
|
195
|
+
ParseTextError,
|
196
|
+
match=r"Unable to parse <class 'utilities\.sentinel\.Sentinel'>; got 'invalid'",
|
197
|
+
):
|
198
|
+
_ = parse_text(Sentinel, "invalid")
|
199
|
+
|
200
|
+
def test_error_time(self) -> None:
|
201
|
+
with raises(
|
202
|
+
ParseTextError,
|
203
|
+
match=r"Unable to parse <class 'datetime\.time'>; got 'invalid'",
|
204
|
+
):
|
205
|
+
_ = parse_text(dt.time, "invalid")
|
206
|
+
|
207
|
+
def test_error_timedelta(self) -> None:
|
208
|
+
with raises(
|
209
|
+
ParseTextError,
|
210
|
+
match=r"Unable to parse <class 'datetime\.timedelta'>; got 'invalid'",
|
211
|
+
):
|
212
|
+
_ = parse_text(dt.timedelta, "invalid")
|
213
|
+
|
214
|
+
def test_error_unknown_annotation(self) -> None:
|
215
|
+
with raises(ParseTextError, match=r"Unable to parse int \| str; got 'invalid'"):
|
216
|
+
_ = parse_text(int | str, "invalid")
|
217
|
+
|
218
|
+
def test_error_unknown_type(self) -> None:
|
219
|
+
@dataclass(kw_only=True)
|
220
|
+
class Example:
|
221
|
+
pass
|
222
|
+
|
223
|
+
with raises(
|
224
|
+
ParseTextError,
|
225
|
+
match=r"Unable to parse <class 'tests\.test_parse\.TestParseText\.test_error_unknown_type\.<locals>\.Example'>; got 'invalid'",
|
226
|
+
):
|
227
|
+
_ = parse_text(Example, "invalid")
|
228
|
+
|
229
|
+
def test_error_version(self) -> None:
|
230
|
+
with raises(
|
231
|
+
ParseTextError,
|
232
|
+
match=r"Unable to parse <class 'utilities\.version\.Version'>; got 'invalid'",
|
233
|
+
):
|
234
|
+
_ = parse_text(Version, "invalid")
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
from hypothesis import given
|
6
|
+
from hypothesis.strategies import DataObject, data, sampled_from
|
7
|
+
from pytest import mark, param, raises
|
8
|
+
|
9
|
+
from utilities.sentinel import (
|
10
|
+
SENTINEL_REPR,
|
11
|
+
ParseSentinelError,
|
12
|
+
Sentinel,
|
13
|
+
parse_sentinel,
|
14
|
+
sentinel,
|
15
|
+
)
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from collections.abc import Callable
|
19
|
+
|
20
|
+
|
21
|
+
class TestParseSentinel:
|
22
|
+
@given(data=data())
|
23
|
+
def test_main(self, *, data: DataObject) -> None:
|
24
|
+
text = str(sentinel)
|
25
|
+
text_use = data.draw(sampled_from(["", text, text.lower(), text.upper()]))
|
26
|
+
result = parse_sentinel(text_use)
|
27
|
+
assert result is sentinel
|
28
|
+
|
29
|
+
def test_error(self) -> None:
|
30
|
+
with raises(
|
31
|
+
ParseSentinelError, match="Unable to parse sentinel value; got 'invalid'"
|
32
|
+
):
|
33
|
+
_ = parse_sentinel("invalid")
|
34
|
+
|
35
|
+
|
36
|
+
class TestSentinel:
|
37
|
+
def test_isinstance(self) -> None:
|
38
|
+
assert isinstance(sentinel, Sentinel)
|
39
|
+
|
40
|
+
@mark.parametrize("method", [param(repr), param(str)])
|
41
|
+
def test_repr_and_str(self, method: Callable[..., str]) -> None:
|
42
|
+
assert method(sentinel) == SENTINEL_REPR
|
43
|
+
|
44
|
+
def test_singletone(self) -> None:
|
45
|
+
assert Sentinel() is sentinel
|
@@ -16,8 +16,10 @@ from utilities.hypothesis import text_ascii
|
|
16
16
|
from utilities.sentinel import sentinel
|
17
17
|
from utilities.text import (
|
18
18
|
ParseBoolError,
|
19
|
+
ParseNoneError,
|
19
20
|
join_strs,
|
20
21
|
parse_bool,
|
22
|
+
parse_none,
|
21
23
|
repr_encode,
|
22
24
|
snake_case,
|
23
25
|
split_str,
|
@@ -29,18 +31,33 @@ from utilities.text import (
|
|
29
31
|
class TestParseBool:
|
30
32
|
@given(data=data(), value=booleans())
|
31
33
|
def test_main(self, *, data: DataObject, value: bool) -> None:
|
32
|
-
text =
|
33
|
-
|
34
|
-
|
34
|
+
text = str(value)
|
35
|
+
text_use = data.draw(
|
36
|
+
sampled_from([str(int(value)), text, text.lower(), text.upper()])
|
37
|
+
)
|
38
|
+
result = parse_bool(text_use)
|
35
39
|
assert result is value
|
36
40
|
|
37
41
|
def test_error(self) -> None:
|
38
42
|
with raises(
|
39
|
-
ParseBoolError, match="Unable to parse 'invalid'
|
43
|
+
ParseBoolError, match="Unable to parse boolean value; got 'invalid'"
|
40
44
|
):
|
41
45
|
_ = parse_bool("invalid")
|
42
46
|
|
43
47
|
|
48
|
+
class TestParseNone:
|
49
|
+
@given(data=data())
|
50
|
+
def test_main(self, *, data: DataObject) -> None:
|
51
|
+
text = str(None)
|
52
|
+
text_use = data.draw(sampled_from(["", text, text.lower(), text.upper()]))
|
53
|
+
result = parse_none(text_use)
|
54
|
+
assert result is None
|
55
|
+
|
56
|
+
def test_error(self) -> None:
|
57
|
+
with raises(ParseNoneError, match="Unable to parse null value; got 'invalid'"):
|
58
|
+
_ = parse_none("invalid")
|
59
|
+
|
60
|
+
|
44
61
|
class TestReprEncode:
|
45
62
|
@given(n=integers())
|
46
63
|
def test_main(self, *, n: int) -> None:
|
@@ -632,6 +632,14 @@ def is_hashable(obj: Any, /) -> TypeGuard[Hashable]:
|
|
632
632
|
##
|
633
633
|
|
634
634
|
|
635
|
+
def is_instance_int_not_bool(obj: Any, /) -> TypeGuard[int]:
|
636
|
+
"""Check if an object is an integer, and not a boolean."""
|
637
|
+
return isinstance(obj, int) and not isinstance(obj, bool)
|
638
|
+
|
639
|
+
|
640
|
+
##
|
641
|
+
|
642
|
+
|
635
643
|
@overload
|
636
644
|
def is_iterable_of(obj: Any, cls: type[_T], /) -> TypeGuard[Iterable[_T]]: ...
|
637
645
|
@overload
|
@@ -742,9 +750,9 @@ def is_string_mapping(obj: Any, /) -> TypeGuard[StrMapping]:
|
|
742
750
|
##
|
743
751
|
|
744
752
|
|
745
|
-
def
|
746
|
-
"""Check
|
747
|
-
return issubclass(
|
753
|
+
def is_subclass_int_not_bool(cls: type[Any], /) -> TypeGuard[type[int]]:
|
754
|
+
"""Check if a class is an integer, and not a boolean."""
|
755
|
+
return issubclass(cls, int) and not issubclass(cls, bool)
|
748
756
|
|
749
757
|
|
750
758
|
##
|
@@ -1010,6 +1018,7 @@ __all__ = [
|
|
1010
1018
|
"is_dataclass_class",
|
1011
1019
|
"is_dataclass_instance",
|
1012
1020
|
"is_hashable",
|
1021
|
+
"is_instance_int_not_bool",
|
1013
1022
|
"is_iterable_of",
|
1014
1023
|
"is_none",
|
1015
1024
|
"is_not_none",
|
@@ -1017,7 +1026,7 @@ __all__ = [
|
|
1017
1026
|
"is_sized",
|
1018
1027
|
"is_sized_not_str",
|
1019
1028
|
"is_string_mapping",
|
1020
|
-
"
|
1029
|
+
"is_subclass_int_not_bool",
|
1021
1030
|
"is_tuple",
|
1022
1031
|
"is_tuple_or_str_mapping",
|
1023
1032
|
"make_isinstance",
|
@@ -0,0 +1,129 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import datetime as dt
|
4
|
+
from contextlib import suppress
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from enum import Enum
|
7
|
+
from pathlib import Path
|
8
|
+
from types import NoneType
|
9
|
+
from typing import Any, get_args, override
|
10
|
+
|
11
|
+
from utilities.datetime import is_subclass_date_not_datetime
|
12
|
+
from utilities.enum import ParseEnumError, parse_enum
|
13
|
+
from utilities.functions import is_subclass_int_not_bool
|
14
|
+
from utilities.iterables import one, one_str
|
15
|
+
from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
|
16
|
+
from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
|
17
|
+
from utilities.typing import is_literal_type, is_optional_type
|
18
|
+
from utilities.version import ParseVersionError, Version, parse_version
|
19
|
+
|
20
|
+
|
21
|
+
def parse_text(
|
22
|
+
obj: Any, text: str, /, *, case_sensitive: bool = False, head: bool = False
|
23
|
+
) -> Any:
|
24
|
+
"""Parse text."""
|
25
|
+
if obj is None:
|
26
|
+
try:
|
27
|
+
return parse_none(text)
|
28
|
+
except ParseNoneError:
|
29
|
+
raise ParseTextError(obj=obj, text=text) from None
|
30
|
+
if isinstance(obj, type):
|
31
|
+
return _parse_text_type(obj, text, case_sensitive=case_sensitive)
|
32
|
+
if is_literal_type(obj):
|
33
|
+
return one_str(get_args(obj), text, head=head, case_sensitive=case_sensitive)
|
34
|
+
if is_optional_type(obj):
|
35
|
+
with suppress(ParseNoneError):
|
36
|
+
return parse_none(text)
|
37
|
+
inner = one(arg for arg in get_args(obj) if arg is not NoneType)
|
38
|
+
if isinstance(
|
39
|
+
inner := one(arg for arg in get_args(obj) if arg is not NoneType), type
|
40
|
+
):
|
41
|
+
try:
|
42
|
+
return _parse_text_type(inner, text, case_sensitive=case_sensitive)
|
43
|
+
except ParseTextError:
|
44
|
+
raise ParseTextError(obj=obj, text=text) from None
|
45
|
+
raise ParseTextError(obj=obj, text=text) from None
|
46
|
+
|
47
|
+
|
48
|
+
def _parse_text_type(
|
49
|
+
cls: type[Any], text: str, /, *, case_sensitive: bool = False
|
50
|
+
) -> Any:
|
51
|
+
"""Parse text."""
|
52
|
+
if issubclass(cls, NoneType):
|
53
|
+
try:
|
54
|
+
return parse_none(text)
|
55
|
+
except ParseNoneError:
|
56
|
+
raise ParseTextError(obj=cls, text=text) from None
|
57
|
+
if issubclass(cls, str):
|
58
|
+
return text
|
59
|
+
if issubclass(cls, bool):
|
60
|
+
try:
|
61
|
+
return parse_bool(text)
|
62
|
+
except ParseBoolError:
|
63
|
+
raise ParseTextError(obj=cls, text=text) from None
|
64
|
+
if is_subclass_int_not_bool(cls):
|
65
|
+
try:
|
66
|
+
return int(text)
|
67
|
+
except ValueError:
|
68
|
+
raise ParseTextError(obj=cls, text=text) from None
|
69
|
+
if issubclass(cls, float):
|
70
|
+
try:
|
71
|
+
return float(text)
|
72
|
+
except ValueError:
|
73
|
+
raise ParseTextError(obj=cls, text=text) from None
|
74
|
+
if issubclass(cls, Enum):
|
75
|
+
try:
|
76
|
+
return parse_enum(text, cls, case_sensitive=case_sensitive)
|
77
|
+
except ParseEnumError:
|
78
|
+
raise ParseTextError(obj=cls, text=text) from None
|
79
|
+
if issubclass(cls, Path):
|
80
|
+
return Path(text).expanduser()
|
81
|
+
if issubclass(cls, Sentinel):
|
82
|
+
try:
|
83
|
+
return parse_sentinel(text)
|
84
|
+
except ParseSentinelError:
|
85
|
+
raise ParseTextError(obj=cls, text=text) from None
|
86
|
+
if issubclass(cls, Version):
|
87
|
+
try:
|
88
|
+
return parse_version(text)
|
89
|
+
except ParseVersionError:
|
90
|
+
raise ParseTextError(obj=cls, text=text) from None
|
91
|
+
if is_subclass_date_not_datetime(cls):
|
92
|
+
from utilities.whenever import ParseDateError, parse_date
|
93
|
+
|
94
|
+
try:
|
95
|
+
return parse_date(text)
|
96
|
+
except ParseDateError:
|
97
|
+
raise ParseTextError(obj=cls, text=text) from None
|
98
|
+
if issubclass(cls, dt.datetime):
|
99
|
+
from utilities.whenever import ParseDateTimeError, parse_datetime
|
100
|
+
|
101
|
+
try:
|
102
|
+
return parse_datetime(text)
|
103
|
+
except ParseDateTimeError:
|
104
|
+
raise ParseTextError(obj=cls, text=text) from None
|
105
|
+
if issubclass(cls, dt.time):
|
106
|
+
from utilities.whenever import ParseTimeError, parse_time
|
107
|
+
|
108
|
+
try:
|
109
|
+
return parse_time(text)
|
110
|
+
except ParseTimeError:
|
111
|
+
raise ParseTextError(obj=cls, text=text) from None
|
112
|
+
if issubclass(cls, dt.timedelta):
|
113
|
+
from utilities.whenever import ParseTimedeltaError, parse_timedelta
|
114
|
+
|
115
|
+
try:
|
116
|
+
return parse_timedelta(text)
|
117
|
+
except ParseTimedeltaError:
|
118
|
+
raise ParseTextError(obj=cls, text=text) from None
|
119
|
+
raise ParseTextError(obj=cls, text=text) from None
|
120
|
+
|
121
|
+
|
122
|
+
@dataclass
|
123
|
+
class ParseTextError(Exception):
|
124
|
+
obj: Any
|
125
|
+
text: str
|
126
|
+
|
127
|
+
@override
|
128
|
+
def __str__(self) -> str:
|
129
|
+
return f"Unable to parse {self.obj!r}; got {self.text!r}"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from re import IGNORECASE, search
|
3
5
|
from typing import Any, override
|
4
6
|
|
5
7
|
|
@@ -33,4 +35,29 @@ class Sentinel(metaclass=_Meta):
|
|
33
35
|
sentinel = Sentinel()
|
34
36
|
|
35
37
|
|
36
|
-
|
38
|
+
##
|
39
|
+
|
40
|
+
|
41
|
+
def parse_sentinel(text: str, /) -> Sentinel:
|
42
|
+
"""Parse text into the Sentinel value."""
|
43
|
+
if text == "" or search("Sentinel", text, flags=IGNORECASE):
|
44
|
+
return sentinel
|
45
|
+
raise ParseSentinelError(text=text)
|
46
|
+
|
47
|
+
|
48
|
+
@dataclass(kw_only=True, slots=True)
|
49
|
+
class ParseSentinelError(Exception):
|
50
|
+
text: str
|
51
|
+
|
52
|
+
@override
|
53
|
+
def __str__(self) -> str:
|
54
|
+
return f"Unable to parse sentinel value; got {self.text!r}"
|
55
|
+
|
56
|
+
|
57
|
+
__all__ = [
|
58
|
+
"SENTINEL_REPR",
|
59
|
+
"ParseSentinelError",
|
60
|
+
"Sentinel",
|
61
|
+
"parse_sentinel",
|
62
|
+
"sentinel",
|
63
|
+
]
|
@@ -45,7 +45,26 @@ class ParseBoolError(Exception):
|
|
45
45
|
|
46
46
|
@override
|
47
47
|
def __str__(self) -> str:
|
48
|
-
return f"Unable to parse {self.text!r}
|
48
|
+
return f"Unable to parse boolean value; got {self.text!r}"
|
49
|
+
|
50
|
+
|
51
|
+
##
|
52
|
+
|
53
|
+
|
54
|
+
def parse_none(text: str, /) -> None:
|
55
|
+
"""Parse text into the None value."""
|
56
|
+
if text == "" or search("None", text, flags=IGNORECASE):
|
57
|
+
return
|
58
|
+
raise ParseNoneError(text=text)
|
59
|
+
|
60
|
+
|
61
|
+
@dataclass(kw_only=True, slots=True)
|
62
|
+
class ParseNoneError(Exception):
|
63
|
+
text: str
|
64
|
+
|
65
|
+
@override
|
66
|
+
def __str__(self) -> str:
|
67
|
+
return f"Unable to parse null value; got {self.text!r}"
|
49
68
|
|
50
69
|
|
51
70
|
##
|
@@ -108,8 +127,10 @@ def strip_and_dedent(text: str, /, *, trailing: bool = False) -> str:
|
|
108
127
|
|
109
128
|
__all__ = [
|
110
129
|
"ParseBoolError",
|
130
|
+
"ParseNoneError",
|
111
131
|
"join_strs",
|
112
132
|
"parse_bool",
|
133
|
+
"parse_none",
|
113
134
|
"repr_encode",
|
114
135
|
"snake_case",
|
115
136
|
"split_str",
|