dycw-utilities 0.125.2__tar.gz → 0.125.4__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.125.2 → dycw_utilities-0.125.4}/PKG-INFO +3 -3
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/pyproject.toml +7 -6
- dycw_utilities-0.125.4/src/tests/test_libcst.py +160 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_more_itertools.py +42 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/__init__.py +1 -1
- dycw_utilities-0.125.4/src/utilities/libcst.py +184 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/more_itertools.py +60 -5
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/.gitignore +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/LICENSE +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/README.md +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/click.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/git.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/http.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/math.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/os.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/period.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/random.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/re.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/text.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/types.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/version.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dycw-utilities
|
3
|
-
Version: 0.125.
|
3
|
+
Version: 0.125.4
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
@@ -10,7 +10,7 @@ Requires-Dist: hypothesis<6.132,>=6.131.27; extra == 'test'
|
|
10
10
|
Requires-Dist: pytest-asyncio<0.27,>=0.26.0; extra == 'test'
|
11
11
|
Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
|
12
12
|
Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
|
13
|
-
Requires-Dist: pytest-lazy-fixtures<1.2,>=1.1.
|
13
|
+
Requires-Dist: pytest-lazy-fixtures<1.2,>=1.1.3; extra == 'test'
|
14
14
|
Requires-Dist: pytest-only<2.2,>=2.1.2; extra == 'test'
|
15
15
|
Requires-Dist: pytest-randomly<3.17,>=3.16.0; extra == 'test'
|
16
16
|
Requires-Dist: pytest-regressions<2.8,>=2.7.0; extra == 'test'
|
@@ -174,7 +174,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
|
|
174
174
|
Provides-Extra: zzz-test-sentinel
|
175
175
|
Provides-Extra: zzz-test-shelve
|
176
176
|
Provides-Extra: zzz-test-slack-sdk
|
177
|
-
Requires-Dist: aiohttp<3.
|
177
|
+
Requires-Dist: aiohttp<3.13,>=3.12.0; extra == 'zzz-test-slack-sdk'
|
178
178
|
Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
|
179
179
|
Provides-Extra: zzz-test-socket
|
180
180
|
Provides-Extra: zzz-test-sqlalchemy
|
@@ -8,7 +8,7 @@ requires = ["hatchling"]
|
|
8
8
|
# dependency groups
|
9
9
|
[dependency-groups]
|
10
10
|
dev = [
|
11
|
-
"aiohttp >= 3.
|
11
|
+
"aiohttp >= 3.12.0, < 3.13", # for slack
|
12
12
|
"aiosqlite >= 0.21.0, < 0.22",
|
13
13
|
"altair >= 5.5.0, < 5.6",
|
14
14
|
"asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
|
@@ -35,6 +35,7 @@ dev = [
|
|
35
35
|
"nest-asyncio >= 1.6.0, < 1.7", # for sqlalchemy async
|
36
36
|
"nox >= 2025.2.9, < 2025.3",
|
37
37
|
"numpy >= 2.2.6, < 2.3",
|
38
|
+
"libcst >= 1.7.0, < 1.8",
|
38
39
|
"optuna >= 4.3.0, < 4.4",
|
39
40
|
"orjson >= 3.10.18, < 3.11",
|
40
41
|
"pathvalidate >= 3.2.3, < 3.3",
|
@@ -70,7 +71,7 @@ dev = [
|
|
70
71
|
"pytest-asyncio >= 0.26.0, < 0.27",
|
71
72
|
"pytest-cov >= 6.1.1, < 6.2",
|
72
73
|
"pytest-instafail >= 0.5.0, < 0.6",
|
73
|
-
"pytest-lazy-fixtures >= 1.1.
|
74
|
+
"pytest-lazy-fixtures >= 1.1.3, < 1.2",
|
74
75
|
"pytest-only >= 2.1.2, < 2.2",
|
75
76
|
"pytest-randomly >= 3.16.0, < 3.17",
|
76
77
|
"pytest-rerunfailures >= 15.1, < 16",
|
@@ -91,7 +92,7 @@ dependencies = [
|
|
91
92
|
name = "dycw-utilities"
|
92
93
|
readme = "README.md"
|
93
94
|
requires-python = ">= 3.12"
|
94
|
-
version = "0.125.
|
95
|
+
version = "0.125.4"
|
95
96
|
|
96
97
|
[project.optional-dependencies]
|
97
98
|
test = [
|
@@ -100,7 +101,7 @@ test = [
|
|
100
101
|
"pytest-asyncio >= 0.26.0, < 0.27",
|
101
102
|
"pytest-cov >= 6.1.1, < 6.2",
|
102
103
|
"pytest-instafail >= 0.5.0, < 0.6",
|
103
|
-
"pytest-lazy-fixtures >= 1.1.
|
104
|
+
"pytest-lazy-fixtures >= 1.1.3, < 1.2",
|
104
105
|
"pytest-only >= 2.1.2, < 2.2",
|
105
106
|
"pytest-randomly >= 3.16.0, < 3.17",
|
106
107
|
"pytest-regressions >= 2.7.0, < 2.8",
|
@@ -268,7 +269,7 @@ zzz-test-scipy = ["scipy >= 1.15.3, < 1.16"]
|
|
268
269
|
zzz-test-sentinel = []
|
269
270
|
zzz-test-shelve = []
|
270
271
|
zzz-test-slack-sdk = [
|
271
|
-
"aiohttp >= 3.
|
272
|
+
"aiohttp >= 3.12.0, < 3.13", # for slack
|
272
273
|
"slack-sdk >= 3.35.0, < 3.36",
|
273
274
|
]
|
274
275
|
zzz-test-socket = []
|
@@ -332,7 +333,7 @@ zzz-test-zoneinfo = [
|
|
332
333
|
# bump-my-version
|
333
334
|
[tool.bumpversion]
|
334
335
|
allow_dirty = true
|
335
|
-
current_version = "0.125.
|
336
|
+
current_version = "0.125.4"
|
336
337
|
|
337
338
|
[[tool.bumpversion.files]]
|
338
339
|
filename = "src/utilities/__init__.py"
|
@@ -0,0 +1,160 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from hypothesis import given
|
4
|
+
from hypothesis.strategies import sampled_from
|
5
|
+
from libcst import (
|
6
|
+
Attribute,
|
7
|
+
Dot,
|
8
|
+
Expr,
|
9
|
+
Import,
|
10
|
+
ImportAlias,
|
11
|
+
ImportFrom,
|
12
|
+
ImportStar,
|
13
|
+
Integer,
|
14
|
+
Module,
|
15
|
+
Name,
|
16
|
+
SimpleStatementLine,
|
17
|
+
)
|
18
|
+
from pytest import raises
|
19
|
+
|
20
|
+
from tests.conftest import SKIPIF_CI
|
21
|
+
from utilities.iterables import one
|
22
|
+
from utilities.libcst import (
|
23
|
+
JoinDottedStrError,
|
24
|
+
_ParseImportAliasError,
|
25
|
+
_ParseImportEmptyModuleError,
|
26
|
+
generate_f_string,
|
27
|
+
generate_from_import,
|
28
|
+
generate_import,
|
29
|
+
join_dotted_str,
|
30
|
+
parse_import,
|
31
|
+
render_module,
|
32
|
+
split_dotted_str,
|
33
|
+
)
|
34
|
+
|
35
|
+
|
36
|
+
class TestGenerateFString:
|
37
|
+
def test_main(self) -> None:
|
38
|
+
string = generate_f_string("foo", "bar")
|
39
|
+
result = Module([SimpleStatementLine([Expr(string)])]).code.strip("\n")
|
40
|
+
expected = 'f"{foo}bar"'
|
41
|
+
assert result == expected
|
42
|
+
|
43
|
+
|
44
|
+
class TestGenerateFromImport:
|
45
|
+
@given(
|
46
|
+
case=sampled_from([
|
47
|
+
("foo", "bar", None, "from foo import bar"),
|
48
|
+
("foo", "bar", "bar2", "from foo import bar as bar2"),
|
49
|
+
("foo.bar", "baz", None, "from foo.bar import baz"),
|
50
|
+
("foo.bar", "baz", "baz2", "from foo.bar import baz as baz2"),
|
51
|
+
])
|
52
|
+
)
|
53
|
+
def test_main(self, *, case: tuple[str, str, str | None, str]) -> None:
|
54
|
+
module, name, asname, expected = case
|
55
|
+
imp = generate_from_import(module, name, asname=asname)
|
56
|
+
result = Module([SimpleStatementLine([imp])]).code.strip("\n")
|
57
|
+
assert result == expected
|
58
|
+
|
59
|
+
|
60
|
+
class TestGenerateImport:
|
61
|
+
@given(
|
62
|
+
case=sampled_from([
|
63
|
+
("foo", None, "import foo"),
|
64
|
+
("foo", "foo2", "import foo as foo2"),
|
65
|
+
("foo.bar", None, "import foo.bar"),
|
66
|
+
("foo.bar", "bar2", "import foo.bar as bar2"),
|
67
|
+
])
|
68
|
+
)
|
69
|
+
def test_main(self, *, case: tuple[str, str | None, str]) -> None:
|
70
|
+
module, asname, expected = case
|
71
|
+
imp = generate_import(module, asname=asname)
|
72
|
+
result = Module([SimpleStatementLine([imp])]).code.strip("\n")
|
73
|
+
assert result == expected
|
74
|
+
|
75
|
+
|
76
|
+
class TestParseImport:
|
77
|
+
def test_import_one_name(self) -> None:
|
78
|
+
imp = Import(names=[ImportAlias(Name("foo"))])
|
79
|
+
parsed = one(parse_import(imp))
|
80
|
+
assert parsed.module == "foo"
|
81
|
+
assert parsed.name is None
|
82
|
+
|
83
|
+
def test_import_one_attr(self) -> None:
|
84
|
+
imp = Import(names=[ImportAlias(Attribute(Name("foo"), Name("bar")))])
|
85
|
+
parsed = one(parse_import(imp))
|
86
|
+
assert parsed.module == "foo.bar"
|
87
|
+
assert parsed.name is None
|
88
|
+
|
89
|
+
def test_import_many(self) -> None:
|
90
|
+
imp = Import(names=[ImportAlias(Name("foo")), ImportAlias(Name("bar"))])
|
91
|
+
result = parse_import(imp)
|
92
|
+
assert len(result) == 2
|
93
|
+
first, second = result
|
94
|
+
assert first.module == "foo"
|
95
|
+
assert first.name is None
|
96
|
+
assert second.module == "bar"
|
97
|
+
assert second.name is None
|
98
|
+
|
99
|
+
def test_from_import_one(self) -> None:
|
100
|
+
imp = ImportFrom(module=Name("foo"), names=[ImportAlias(Name("bar"))])
|
101
|
+
result = one(parse_import(imp))
|
102
|
+
assert result.module == "foo"
|
103
|
+
assert result.name == "bar"
|
104
|
+
|
105
|
+
def test_from_import_many(self) -> None:
|
106
|
+
imp = ImportFrom(
|
107
|
+
module=Name("foo"),
|
108
|
+
names=[ImportAlias(Name("bar")), ImportAlias(Name("baz"))],
|
109
|
+
)
|
110
|
+
result = parse_import(imp)
|
111
|
+
first, second = result
|
112
|
+
assert first.module == "foo"
|
113
|
+
assert first.name == "bar"
|
114
|
+
assert second.module == "foo"
|
115
|
+
assert second.name == "baz"
|
116
|
+
|
117
|
+
def test_from_import_star(self) -> None:
|
118
|
+
imp = ImportFrom(module=Name("foo"), names=ImportStar())
|
119
|
+
result = one(parse_import(imp))
|
120
|
+
assert result.module == "foo"
|
121
|
+
assert result.name == "*"
|
122
|
+
|
123
|
+
def test_error_empty_module(self) -> None:
|
124
|
+
alias = ImportAlias(name=Name("foo"))
|
125
|
+
imp = ImportFrom(module=None, names=[alias], relative=[Dot()])
|
126
|
+
with raises(
|
127
|
+
_ParseImportEmptyModuleError, match="Module must not be None; got .*"
|
128
|
+
):
|
129
|
+
_ = parse_import(imp)
|
130
|
+
|
131
|
+
def test_error_alias(self) -> None:
|
132
|
+
alias = ImportAlias(name=Attribute(Name("foo"), Name("bar")))
|
133
|
+
imp = ImportFrom(module=Name("baz"), names=[alias])
|
134
|
+
with raises(
|
135
|
+
_ParseImportAliasError,
|
136
|
+
match=r"Invalid alias name; got module 'baz' and attribute 'foo\.bar'",
|
137
|
+
):
|
138
|
+
_ = parse_import(imp)
|
139
|
+
|
140
|
+
|
141
|
+
class TestRenderModule:
|
142
|
+
@SKIPIF_CI
|
143
|
+
def test_main(self) -> None:
|
144
|
+
module = Module([SimpleStatementLine([generate_import("foo")])])
|
145
|
+
result = render_module(module)
|
146
|
+
expected = "import foo\n"
|
147
|
+
assert result == expected
|
148
|
+
|
149
|
+
|
150
|
+
class TestSplitAndJoinDottedStr:
|
151
|
+
@given(text=sampled_from(["foo", "foo.bar", "foo.bar.baz"]))
|
152
|
+
def test_main(self, *, text: str) -> None:
|
153
|
+
result = join_dotted_str(split_dotted_str(text))
|
154
|
+
assert result == text
|
155
|
+
|
156
|
+
def test_join_dotted_fallback_expression(self) -> None:
|
157
|
+
with raises(
|
158
|
+
JoinDottedStrError, match="Only names & attributes allowed; got .*"
|
159
|
+
):
|
160
|
+
_ = join_dotted_str(Integer("0"))
|
@@ -1,11 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from collections.abc import Iterator
|
4
|
+
from re import escape
|
4
5
|
from typing import TYPE_CHECKING, Any, ClassVar, TypeGuard
|
5
6
|
|
6
7
|
from pytest import mark, param, raises
|
7
8
|
|
8
9
|
from utilities.more_itertools import (
|
10
|
+
BucketMappingError,
|
9
11
|
Split,
|
10
12
|
bucket_mapping,
|
11
13
|
partition_list,
|
@@ -64,6 +66,46 @@ class TestBucketMapping:
|
|
64
66
|
assert mapping["b"] == [1, 2, 3]
|
65
67
|
assert mapping["c"] == [1, 2]
|
66
68
|
|
69
|
+
iterable_unique: ClassVar[list[str]] = ["a1", "b2", "c3"]
|
70
|
+
|
71
|
+
def test_unique(self) -> None:
|
72
|
+
mapping = bucket_mapping(self.iterable_unique, lambda x: x[0], unique=True)
|
73
|
+
expected = {"a": "a1", "b": "b2", "c": "c3"}
|
74
|
+
assert mapping == expected
|
75
|
+
|
76
|
+
def test_transform_and_unique(self) -> None:
|
77
|
+
mapping = bucket_mapping(
|
78
|
+
self.iterable_unique,
|
79
|
+
lambda x: x[0],
|
80
|
+
transform=lambda x: int(x[-1]),
|
81
|
+
unique=True,
|
82
|
+
)
|
83
|
+
expected = {"a": 1, "b": 2, "c": 3}
|
84
|
+
assert mapping == expected
|
85
|
+
|
86
|
+
def test_error_unique(self) -> None:
|
87
|
+
with raises(
|
88
|
+
BucketMappingError,
|
89
|
+
match=escape(
|
90
|
+
"Buckets must contain exactly one item each; got 'a' (#1: 'a1', #2: 'a2'), 'b' (#1: 'b1', #2: 'b2'), 'c' (#1: 'c1', #2: 'c2')"
|
91
|
+
),
|
92
|
+
):
|
93
|
+
_ = bucket_mapping(self.iterable, lambda x: x[0], unique=True)
|
94
|
+
|
95
|
+
def test_error_transform_and_unique(self) -> None:
|
96
|
+
with raises(
|
97
|
+
BucketMappingError,
|
98
|
+
match=escape(
|
99
|
+
"Buckets must contain exactly one item each; got 'a' (#1: 1, #2: 2), 'b' (#1: 1, #2: 2), 'c' (#1: 1, #2: 2)"
|
100
|
+
),
|
101
|
+
):
|
102
|
+
_ = bucket_mapping(
|
103
|
+
self.iterable,
|
104
|
+
lambda x: x[0],
|
105
|
+
transform=lambda x: int(x[-1]),
|
106
|
+
unique=True,
|
107
|
+
)
|
108
|
+
|
67
109
|
|
68
110
|
class TestPartitionList:
|
69
111
|
def test_main(self) -> None:
|
@@ -0,0 +1,184 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Sequence
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from subprocess import check_output
|
6
|
+
from typing import assert_never, override
|
7
|
+
|
8
|
+
from libcst import (
|
9
|
+
AsName,
|
10
|
+
Attribute,
|
11
|
+
BaseExpression,
|
12
|
+
FormattedString,
|
13
|
+
FormattedStringExpression,
|
14
|
+
FormattedStringText,
|
15
|
+
Import,
|
16
|
+
ImportAlias,
|
17
|
+
ImportFrom,
|
18
|
+
ImportStar,
|
19
|
+
Module,
|
20
|
+
Name,
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
def generate_from_import(
|
25
|
+
module: str, name: str, /, *, asname: str | None = None
|
26
|
+
) -> ImportFrom:
|
27
|
+
"""Generate an `ImportFrom` object."""
|
28
|
+
alias = ImportAlias(
|
29
|
+
name=Name(name), asname=AsName(Name(asname)) if asname else None
|
30
|
+
)
|
31
|
+
return ImportFrom(module=split_dotted_str(module), names=[alias])
|
32
|
+
|
33
|
+
|
34
|
+
def generate_f_string(var: str, suffix: str, /) -> FormattedString:
|
35
|
+
"""Generate an f-string."""
|
36
|
+
return FormattedString([
|
37
|
+
FormattedStringExpression(expression=Name(var)),
|
38
|
+
FormattedStringText(suffix),
|
39
|
+
])
|
40
|
+
|
41
|
+
|
42
|
+
def generate_import(module: str, /, *, asname: str | None = None) -> Import:
|
43
|
+
"""Generate an `Import` object."""
|
44
|
+
alias = ImportAlias(
|
45
|
+
name=split_dotted_str(module), asname=AsName(Name(asname)) if asname else None
|
46
|
+
)
|
47
|
+
return Import(names=[alias])
|
48
|
+
|
49
|
+
|
50
|
+
##
|
51
|
+
|
52
|
+
|
53
|
+
@dataclass(kw_only=True, slots=True)
|
54
|
+
class _ParseImportOutput:
|
55
|
+
module: str
|
56
|
+
name: str | None = None
|
57
|
+
|
58
|
+
|
59
|
+
def parse_import(import_: Import | ImportFrom, /) -> Sequence[_ParseImportOutput]:
|
60
|
+
"""Parse an import."""
|
61
|
+
match import_:
|
62
|
+
case Import():
|
63
|
+
return [_parse_import_one(n) for n in import_.names]
|
64
|
+
case ImportFrom():
|
65
|
+
if (attr_or_name := import_.module) is None:
|
66
|
+
raise _ParseImportEmptyModuleError(import_=import_)
|
67
|
+
module = join_dotted_str(attr_or_name)
|
68
|
+
match import_.names:
|
69
|
+
case Sequence() as names:
|
70
|
+
return [_parse_import_from_one(module, n) for n in names]
|
71
|
+
case ImportStar():
|
72
|
+
return [_ParseImportOutput(module=module, name="*")]
|
73
|
+
case _ as never:
|
74
|
+
assert_never(never)
|
75
|
+
case _ as never:
|
76
|
+
assert_never(never)
|
77
|
+
|
78
|
+
|
79
|
+
def _parse_import_one(alias: ImportAlias, /) -> _ParseImportOutput:
|
80
|
+
return _ParseImportOutput(module=join_dotted_str(alias.name))
|
81
|
+
|
82
|
+
|
83
|
+
def _parse_import_from_one(module: str, alias: ImportAlias, /) -> _ParseImportOutput:
|
84
|
+
match alias.name:
|
85
|
+
case Name(name):
|
86
|
+
return _ParseImportOutput(module=module, name=name)
|
87
|
+
case Attribute() as attr:
|
88
|
+
raise _ParseImportAliasError(module=module, attr=attr)
|
89
|
+
case _ as never:
|
90
|
+
assert_never(never)
|
91
|
+
|
92
|
+
|
93
|
+
@dataclass(kw_only=True, slots=True)
|
94
|
+
class ParseImportError(Exception): ...
|
95
|
+
|
96
|
+
|
97
|
+
@dataclass(kw_only=True, slots=True)
|
98
|
+
class _ParseImportEmptyModuleError(ParseImportError):
|
99
|
+
import_: ImportFrom
|
100
|
+
|
101
|
+
@override
|
102
|
+
def __str__(self) -> str:
|
103
|
+
return f"Module must not be None; got {self.import_}"
|
104
|
+
|
105
|
+
|
106
|
+
@dataclass(kw_only=True, slots=True)
|
107
|
+
class _ParseImportAliasError(ParseImportError):
|
108
|
+
module: str
|
109
|
+
attr: Attribute
|
110
|
+
|
111
|
+
@override
|
112
|
+
def __str__(self) -> str:
|
113
|
+
attr = self.attr
|
114
|
+
return f"Invalid alias name; got module {self.module!r} and attribute '{attr.value.value}.{attr.attr.value}'"
|
115
|
+
|
116
|
+
|
117
|
+
##
|
118
|
+
|
119
|
+
|
120
|
+
def split_dotted_str(dotted: str, /) -> Name | Attribute:
|
121
|
+
"""Split a dotted string into a name/attribute."""
|
122
|
+
parts = dotted.split(".")
|
123
|
+
node = Name(parts[0])
|
124
|
+
for part in parts[1:]:
|
125
|
+
node = Attribute(value=node, attr=Name(part))
|
126
|
+
return node
|
127
|
+
|
128
|
+
|
129
|
+
def join_dotted_str(name_or_attr: Name | Attribute, /) -> str:
|
130
|
+
"""Join a dotted from from a name/attribute."""
|
131
|
+
parts: Sequence[str] = []
|
132
|
+
curr: BaseExpression | Name | Attribute = name_or_attr
|
133
|
+
while True:
|
134
|
+
match curr:
|
135
|
+
case Name(value=value):
|
136
|
+
parts.append(value)
|
137
|
+
break
|
138
|
+
case Attribute(value=value, attr=Name(value=attr_value)):
|
139
|
+
parts.append(attr_value)
|
140
|
+
curr = value
|
141
|
+
case BaseExpression() as expr:
|
142
|
+
raise JoinDottedStrError(name_or_attr=name_or_attr, expr=expr)
|
143
|
+
case _ as never:
|
144
|
+
assert_never(never)
|
145
|
+
return ".".join(reversed(parts))
|
146
|
+
|
147
|
+
|
148
|
+
@dataclass(kw_only=True, slots=True)
|
149
|
+
class JoinDottedStrError(Exception):
|
150
|
+
name_or_attr: Name | Attribute
|
151
|
+
expr: BaseExpression
|
152
|
+
|
153
|
+
@override
|
154
|
+
def __str__(self) -> str:
|
155
|
+
return f"Only names & attributes allowed; got {self.expr}"
|
156
|
+
|
157
|
+
|
158
|
+
##
|
159
|
+
|
160
|
+
|
161
|
+
def render_module(source: str | Module, /) -> str:
|
162
|
+
"""Render a module."""
|
163
|
+
match source: # skipif-ci
|
164
|
+
case str() as text:
|
165
|
+
return check_output(["ruff", "format", "-"], input=text, text=True)
|
166
|
+
case Module() as module:
|
167
|
+
return render_module(module.code)
|
168
|
+
case _ as never:
|
169
|
+
assert_never(never)
|
170
|
+
|
171
|
+
|
172
|
+
##
|
173
|
+
|
174
|
+
|
175
|
+
__all__ = [
|
176
|
+
"ParseImportError",
|
177
|
+
"generate_f_string",
|
178
|
+
"generate_from_import",
|
179
|
+
"generate_import",
|
180
|
+
"join_dotted_str",
|
181
|
+
"parse_import",
|
182
|
+
"render_module",
|
183
|
+
"split_dotted_str",
|
184
|
+
]
|
@@ -21,12 +21,14 @@ from more_itertools import bucket, partition, split_into
|
|
21
21
|
from more_itertools import peekable as _peekable
|
22
22
|
|
23
23
|
from utilities.functions import get_class_name
|
24
|
+
from utilities.iterables import OneNonUniqueError, one
|
25
|
+
from utilities.reprlib import get_repr
|
24
26
|
from utilities.sentinel import Sentinel, sentinel
|
27
|
+
from utilities.types import THashable
|
25
28
|
|
26
29
|
if TYPE_CHECKING:
|
27
30
|
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
|
28
31
|
|
29
|
-
from utilities.types import THashable
|
30
32
|
|
31
33
|
_T = TypeVar("_T")
|
32
34
|
_U = TypeVar("_U")
|
@@ -35,6 +37,26 @@ _U = TypeVar("_U")
|
|
35
37
|
##
|
36
38
|
|
37
39
|
|
40
|
+
@overload
|
41
|
+
def bucket_mapping(
|
42
|
+
iterable: Iterable[_T],
|
43
|
+
func: Callable[[_T], THashable],
|
44
|
+
/,
|
45
|
+
*,
|
46
|
+
transform: Callable[[_T], _U],
|
47
|
+
list: bool = False,
|
48
|
+
unique: Literal[True],
|
49
|
+
) -> Mapping[THashable, _U]: ...
|
50
|
+
@overload
|
51
|
+
def bucket_mapping(
|
52
|
+
iterable: Iterable[_T],
|
53
|
+
func: Callable[[_T], THashable],
|
54
|
+
/,
|
55
|
+
*,
|
56
|
+
transform: Callable[[_T], _U] | None = None,
|
57
|
+
list: bool = False,
|
58
|
+
unique: Literal[True],
|
59
|
+
) -> Mapping[THashable, _T]: ...
|
38
60
|
@overload
|
39
61
|
def bucket_mapping(
|
40
62
|
iterable: Iterable[_T],
|
@@ -79,11 +101,14 @@ def bucket_mapping(
|
|
79
101
|
*,
|
80
102
|
transform: Callable[[_T], _U] | None = None,
|
81
103
|
list: bool = False,
|
104
|
+
unique: bool = False,
|
82
105
|
) -> (
|
83
106
|
Mapping[THashable, Iterator[_T]]
|
84
107
|
| Mapping[THashable, Iterator[_U]]
|
85
108
|
| Mapping[THashable, Sequence[_T]]
|
86
109
|
| Mapping[THashable, Sequence[_U]]
|
110
|
+
| Mapping[THashable, _T]
|
111
|
+
| Mapping[THashable, _U]
|
87
112
|
): ...
|
88
113
|
def bucket_mapping(
|
89
114
|
iterable: Iterable[_T],
|
@@ -92,26 +117,55 @@ def bucket_mapping(
|
|
92
117
|
*,
|
93
118
|
transform: Callable[[_T], _U] | None = None,
|
94
119
|
list: bool = False, # noqa: A002
|
120
|
+
unique: bool = False,
|
95
121
|
) -> (
|
96
122
|
Mapping[THashable, Iterator[_T]]
|
97
123
|
| Mapping[THashable, Iterator[_U]]
|
98
124
|
| Mapping[THashable, Sequence[_T]]
|
99
125
|
| Mapping[THashable, Sequence[_U]]
|
126
|
+
| Mapping[THashable, _T]
|
127
|
+
| Mapping[THashable, _U]
|
100
128
|
):
|
101
129
|
"""Bucket the values of iterable into a mapping."""
|
102
130
|
b = bucket(iterable, func)
|
103
131
|
mapping = {key: b[key] for key in b}
|
104
132
|
match transform, list:
|
105
133
|
case None, False:
|
106
|
-
|
134
|
+
...
|
107
135
|
case None, True:
|
108
|
-
|
136
|
+
mapping = {k: builtins.list(v) for k, v in mapping.items()}
|
109
137
|
case _, False:
|
110
|
-
|
138
|
+
mapping = {k: map(transform, v) for k, v in mapping.items()}
|
111
139
|
case _, True:
|
112
|
-
|
140
|
+
mapping = {k: builtins.list(map(transform, v)) for k, v in mapping.items()}
|
113
141
|
case _ as never:
|
114
142
|
assert_never(never)
|
143
|
+
if not unique:
|
144
|
+
return mapping
|
145
|
+
results = {}
|
146
|
+
error_no_transform: dict[THashable, tuple[_T, _T]] = {}
|
147
|
+
for key, value in mapping.items():
|
148
|
+
try:
|
149
|
+
results[key] = one(value)
|
150
|
+
except OneNonUniqueError as error:
|
151
|
+
error_no_transform[key] = (error.first, error.second)
|
152
|
+
if len(error_no_transform) >= 1:
|
153
|
+
raise BucketMappingError(errors=error_no_transform)
|
154
|
+
return results
|
155
|
+
|
156
|
+
|
157
|
+
@dataclass(kw_only=True, slots=True)
|
158
|
+
class BucketMappingError(Exception, Generic[THashable, _U]):
|
159
|
+
errors: Mapping[THashable, tuple[_U, _U]]
|
160
|
+
|
161
|
+
@override
|
162
|
+
def __str__(self) -> str:
|
163
|
+
parts = [
|
164
|
+
f"{get_repr(key)} (#1: {get_repr(first)}, #2: {get_repr(second)})"
|
165
|
+
for key, (first, second) in self.errors.items()
|
166
|
+
]
|
167
|
+
desc = ", ".join(parts)
|
168
|
+
return f"Buckets must contain exactly one item each; got {desc}"
|
115
169
|
|
116
170
|
|
117
171
|
##
|
@@ -264,6 +318,7 @@ def _yield_splits3(
|
|
264
318
|
|
265
319
|
|
266
320
|
__all__ = [
|
321
|
+
"BucketMappingError",
|
267
322
|
"Split",
|
268
323
|
"bucket_mapping",
|
269
324
|
"partition_list",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_missing/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_missing/module.py
RENAMED
File without changes
|
{dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/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.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_without/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/src/tests/modules/package_without/module_1.py
RENAMED
File without changes
|
{dycw_utilities-0.125.2 → dycw_utilities-0.125.4}/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
|