dycw-utilities 0.108.1__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.1 → dycw_utilities-0.108.2}/PKG-INFO +1 -1
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/pyproject.toml +2 -2
- 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.1 → dycw_utilities-0.108.2}/src/tests/test_text.py +21 -4
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/__init__.py +1 -1
- dycw_utilities-0.108.2/src/utilities/parse.py +129 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/sentinel.py +28 -1
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/text.py +22 -1
- dycw_utilities-0.108.1/src/tests/test_sentinel.py +0 -22
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/.gitignore +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/LICENSE +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/README.md +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/__main__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/run.sh +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/run.sh +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_astor.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/astor.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/click.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/git.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/http.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/math.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/os.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/period.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/random.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/re.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/types.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/version.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.108.1 → 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"
|
@@ -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:
|
@@ -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",
|
@@ -1,22 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING
|
4
|
-
|
5
|
-
from pytest import mark, param
|
6
|
-
|
7
|
-
from utilities.sentinel import SENTINEL_REPR, Sentinel, sentinel
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from collections.abc import Callable
|
11
|
-
|
12
|
-
|
13
|
-
class TestSentinel:
|
14
|
-
def test_isinstance(self) -> None:
|
15
|
-
assert isinstance(sentinel, Sentinel)
|
16
|
-
|
17
|
-
@mark.parametrize("method", [param(repr), param(str)])
|
18
|
-
def test_repr_and_str(self, method: Callable[..., str]) -> None:
|
19
|
-
assert method(sentinel) == SENTINEL_REPR
|
20
|
-
|
21
|
-
def test_singletone(self) -> None:
|
22
|
-
assert Sentinel() is sentinel
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_missing/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_missing/module.py
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_with/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_without/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_without/module_1.py
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/modules/package_without/module_2.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/__main__.py
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_async_service/run.sh
RENAMED
File without changes
|
{dycw_utilities-0.108.1 → dycw_utilities-0.108.2}/src/tests/scripts/test_queue_processor/__init__.py
RENAMED
File without changes
|