alex-ber-utils 0.13.11
AlexBerUtils is collection of the small utilities
License: Apache 2.0
Requires Python: >=3.8
Keywords: tools tool utils enum enums threadlocal UploadCommand upload uuid1mc uuid UUID UUID1 UUID4 UU1DMC issetdescriptor ismethod importer new_instance safe_eval is_empty parse_boolean Properties java.util.Properties

## AlexBerUtils

AlexBerUtils is collection of the small utilities. See CHANGELOG.md for detail description.



### Getting Help


### QuickStart
```bash
python -m pip install -U alex-ber-utils
```


### Installing from Github

```bash
python -m pip install -U https://github.com/alex-ber/AlexBerUtils/archive/master.zip
```
Optionally installing tests requirements.

```bash
python -m pip install -U https://github.com/alex-ber/AlexBerUtils/archive/master.zip#egg=alex-ber-utils[tests]
```

Or explicitly:

```bash
wget https://github.com/alex-ber/AlexBerUtils/archive/master.zip -O master.zip; unzip master.zip; rm master.zip
```
And then installing from source (see below).


### Installing from source
```bash
python -m pip install . # only installs "required"
```
```bash
python -m pip install .[tests] # installs dependencies for tests
```
```bash
python -m pip install .[piptools] # installs dependencies for pip-tools
```
```bash
python -m pip install .[fabric]   # installs fabric (used in fabs.py)
```
```bash
python -m pip install .[yml]   # installs Yml related dependencies 
                                # (used in ymlparsers.py, init_app_conf.py, deploys.py;
                                # optionally used in ymlparsers_extra.py, emails.py)
```
```bash
python -m pip install .[env]   # installs pydotenv (optionally used in deploys.py and mains.py)
```

#### Alternatively you install from requirements file:
```bash
python -m pip install -r requirements.txt # only installs "required"
```
```bash
python -m pip install -r requirements-tests.txt # installs dependencies for tests
```
```bash
python -m pip install -r requirements-piptools.txt # installs dependencies for pip-tools
```
```bash
python -m pip install -r requirements-fabric.txt   # installs fabric (used in fabs.py)
```
```bash
python -m pip install -r requirements-yml.txt   # installs Yml related dependencies 
                                                 # (used in ymlparsers.py, init_app_conf.py, deploys.py;
                                                 # optionally used in ymlparsers_extra.py, emails.py)
```
```bash
python -m pip install -r requirements-env.txt   # installs pydotenv (optionally used in deploys.py)
```

### Using Docker
`alexberkovich/AlexBerUtils:latest`  contains all `AlexBerUtils` dependencies.
This Dockerfile is very simple, you can take relevant part for you and put them into your Dockerfile.

##
Alternatively, you can use it as base Docker image for your project and add/upgrade 
another dependencies as you need.

For example:

```Dockerfile
FROM alexberkovich/alex_ber_utils:latest

COPY requirements.txt etc/requirements.txt

RUN set -ex && \
    #latest pip,setuptools,wheel
    pip install --upgrade pip setuptools wheel && \
    pip install alex_ber_utils 
    pip install -r etc/requirements.txt 

CMD ["/bin/sh"]
#CMD tail -f /dev/null
```

where `requirements.txt` is requirements for your project.

  

##

From the directory with setup.py
```bash
python setup.py test #run all tests
```

or

```bash

pytest
```

## Installing new version
See https://docs.python.org/3.1/distutils/uploading.html 


## Installing new version to venv
```bash
python -m pip uninstall --yes alex_ber_utils
python setup.py clean sdist bdist_wheel
python -m pip install --find-links=./dist alex_ber_utils==0.6.5
```

##Manual upload
```bash
#python setup.py clean sdist upload
```


## Requirements


AlexBerUtils requires the following modules.

* Python 3.8+

* PyYAML>=6.0.1

* packaging>=23.2


# Changelog

All notable changes to this project will be documented in this file.

\#https://pypi.org/manage/project/alex-ber-utils/releases/

## [Unreleased]

## [0.13.11] 02.04.2024

### Changed

- Accept String Log Levels in `initStream`, `StreamToLogger`. Now, they accepts `logger_level`
  specified as case-insensitive strings (e.g., `"INFO"`, `"DEBUG"`) in addition to
  standard integer constants (e.g., `logging.INFO`).

## [0.13.10] 02.04.2024

### Changed

- **BREAKING CHANGE**. Removed the internal `_validate_param` helper function.
  It incorrectly has checked its dictionary argument instead of the
  intended parameter's value. Now existing validate_param function is used that correctly
  validates if a given parameter value is `None`.

### Fixed

- Resolved an issue where the logging level specified by the `logger_level` parameter in
  `initStream` was ignored by the underlying `StreamToLogger` adapter, causing redirected
  stream output to always use the `DEBUG` level internally. `StreamToLogger`'s constructor
  now correctly accepts both `logger_level` (as passed by `initStream`) and its original
  `log_level` parameter when determining the logging level to use, ensuring redirected
  output respects the configured level.
  (See [#15](https://github.com/alex-ber/AlexBerUtils/issues/15))

## [0.13.9] 04.02.2024

### Fixed

- **_execute_task Enhancements:** 'AsyncExecutionQueue'
    - Added a callback to check and log unconsumed exceptions by calling `handle_future_exception()` with a 0.1-second
      delay.
    - Integrated the `chain_future_results()` helper function as a callback to propagate the result or exception to the
      target `task_future`.

- **Worker Exception Handling:**
    - Enhanced the `worker()` method to include exception handling around task execution, ensuring that any issues
      during task processing are properly caught and handled.

- **Graceful Shutdown Improvements:**
    - Updated the `aclose()` method to cancel all tasks waiting in the queue before signaling the worker to exit,
      ensuring that no pending tasks remain unprocessed during shutdown.

### Added

- **New Future Exception Handling Function:**
    - Added the `handle_future_exception()` function, which schedules a callback to check if the future was consumed
      after a given delay. This function wraps the provided future in a `FutureWrapper` and
      uses `_check_and_log_exception` to log any unconsumed exceptions.

## [0.13.5] 04.02.2024

### Fixed

- Resolved issue with `copy_context()` loss when passing messages via the
  `AsyncExecutionQueue`.

## [0.13.4] 02.02.2024

### Fixed

- In `exec_in_executor()` changed
  `wrapped_future.add_done_callback(lambda wf: _handle_future_exception(wf, delay=0.1))`
  to
  `wrapped_future.add_done_callback(lambda _: _handle_future_exception(wrapped_future, delay=0.1))`

## [0.13.3] 02.02.2024

### Added

- Introduced a new `FutureWrapper` class to wrap an `asyncio.Future`.
    - The `FutureWrapper` intercepts when the user awaits or calls `result()`/`exception()`
      by setting a `_consumed` flag.
- Implemented `_check_and_log_exception(wrapper: FutureWrapper)`:
    - Checks if the underlying future has an exception and whether the result was never consumed.
    - Logs an exception for fire-and-forget tasks if the exception remains unhandled.
- Added `_handle_future_exception(wrapper: FutureWrapper, delay: float)`:
    - Schedules the `_check_and_log_exception` function with a slight delay to allow
      the consumer to handle the future if desired.
- Modified `exec_in_executor()` to:
    - Wrap the underlying future in `FutureWrapper`.
    - Attach a done callback that logs exceptions (after a delay) if the result is not consumed.
    - Update the docstring to explain the behavior and how the executor is resolved.

### Changed

- Updated the `exec_in_executor` docstring to reflect:
    - The context preservation behavior using `ContextVars`.
    - The resolution order for selecting the executor.
    - That the returned future is wrapped to support both fire-and-forget (with logging)
      and explicit awaiting (without logging).

### Fixed

- Ensured that if an exception occurs while retrieving the exception from the future,
  a warning is logged instead of failing silently.

## [0.13.2] 02.02.2024

### Fixed

- **Exception Propagation in `exec_in_executor()`:**
    - Fixed an issue where exceptions raised by coroutine functions passed to
      `exec_in_executor()` were not properly propagated.

### Fixed

- Updated the `chain_future_results()` function to include a guard against setting the  
  result on an already-resolved future.
- Enhanced the docstring to clearly explain the function's behavior and usage,
  ensuring better clarity when multiple callbacks are involved.

- **Exception Propagation in `exec_in_executor()`:**
    - Fixed an issue where exceptions raised by coroutine functions passed to
      `exec_in_executor()` were not properly propagated.

## [0.13.0] 01.02.2024

### Fixed

- **Context Preservation for Coroutines in Threads:**
    - Fixed an issue where `copy_context()` in `_run_coroutine_in_thread` was redundantly capturing the context, which
      could lead to inconsistencies. The context is now correctly propagated from the main thread to the executor thread
      using `ctx.run()` in `exec_in_executor`.
    - Ensured that the original context is preserved when running coroutines in threads, preventing the loss
      of `ContextVar` values across asynchronous boundaries.

- **StopIteration Handling for Coroutines:**
    - Added a `_coro_wrapper` to +handle `StopIteration` exceptions in coroutines, converting them into `RuntimeError`.
      This prevents `asyncio.Future` from hanging indefinitely when `StopIteration` is raised in a coroutine running in
      a separate thread.

### Added

- **Explicit Exception Handling:**
    - Introduced `_coro_wrapper()` to ensure consistent handling of `StopIteration` for coroutines, aligning with the
      behavior of synchronous functions.

- **New Function: `get_context_vars()`**  
  A utility to collect and manage `ContextVar` instances from modules, along with their factory methods for default
  values.  
  **Features**:
    - Detects top-level `ContextVar` instances in specified modules.
    - Supports custom factory method resolution via the `factory_method_creator()` parameter.
    - Returns a list of entities containing `ContextVar` and its resolved factory method.
    - Works seamlessly with `reset_context_vars()` to reset `ContextVar` instances to their default values.

  **Usage**:
  ```python
  # Default usage (factory method in same module as ContextVar)
  entities = get_context_vars(my_module)

  # Custom factory resolver (factory in another module/class)
  def custom_factory_creator(var, module):
      from other_module import factories
      return getattr(factories, f"create_{var.name}_default")

  entities = get_context_vars(my_module, factory_method_creator=custom_factory_creator)
  reset_context_vars(*entities)

- **New Function: `reset_context_vars()`**  
  A utility to reset `ContextVar` instances to their default values using pre-resolved
  factory methods.  
  **Features**:
    - Accepts entities produced by `get_context_vars()` (containing `ContextVar` and its factory
      method).
    - Sets the default value for each `ContextVar` using its associated factory method.

- Introduced the `Auto` class, a callable counter designed to generate sequential integers
  starting from a configurable initial value.It is intended for use with Python's `Enum` to
  automatically assign sequential integer values to enum members.

- Introduced the `create_auto()` factory function to create new instances of the `Auto` class.
  It provides a convenient way to generate `Auto` instances with a specified starting value,
  defaulting to 0.

- Introduced the `check_not_empty()` function to validate that a given value is not empty. It
  raises a `ValueError` with a descriptive message if the value is empty, using the `field_name`
  attribute from the provided field object.

- Introduced the `parse_json()` function to parse JSON strings. It is useful for ensuring that
  input data is correctly formatted as a list, either directly or via a JSON string.

- Introduced the `validate_same_length()` function to ensure that multiple iterables
  have the same length. It raises a `ValueError` if the iterables do not have the same length
  or if `field_names` is not provided.

### Changed

- **BREAKING CHANGE**: Singature of `_run_coroutine_in_thread()` was reverted back
  to 0.12.9 (this is internal function, so you shouldn't use it anyway).
- **BREAKING CHANGE**: Updated `_run_coroutine_in_thread()` to remove the redundant
  `copy_context()` call, as the context is already captured and applied in `exec_in_executor`.
  The behaviour is the same as in 0.12.9.

## [0.12.10] 30.01.2024

### Fixed

- **Context Preservation for Coroutines**: Ensured that coroutine functions are executed
  within a copied context when run in an executor. This fix addresses a bug where coroutine
  functions bypassed `copy_context()`, potentially leading to issues with `ContextVars`
  not being maintained across asynchronous boundaries.

### Changed

- **Optimization for Regular Function Execution Path**: Refactored the creation of
  `func_call` and the `wrapper` function to only occur within the branch handling
  regular functions.

- **BREAKING CHANGE**: Singature of `_run_coroutine_in_thread()` was changed (this is internal
  function, so you shouldn't use it anyway),
  `ctx` was added as first parameter.

## [0.12.9] 19.12.2024

### Changed

- **AsyncContextManagerMixin**: bug fix. Now, it calls `async_acquire()` and `async_release()`.

## [0.12.8] 19.12.2024

### Added

- **SyncContextManagerMixin**: A new mixin to provide synchronous context management
  capabilities to classes. This mixin includes `__enter__()` and `__exit__()` methods to manage
  acquiring and releasing locks.
- **AsyncContextManagerMixin**: A new mixin to provide asynchronous context management
  capabilities to classes. This mixin includes `__aenter__()` and `__aexit__()` methods to manage
  acquiring and releasing locks asynchronously.

### Changed

- **LockingProxy**: Enhanced to include synchronous and asynchronous context management
  through the integration of `SyncContextManagerMixin` and `AsyncContextManagerMixin`.
  This update allows `LockingProxy` to be used with both `with` and `async with` statements
  for better resource management in multi-threaded and asynchronous environments.

## [0.12.7] 18.12.2024

### Added

- Introduced `LockingGetItemMixin` to provide thread-safe access to items in a collection.
- Introduced `LockingSetItemMixin` to provide thread-safe modification of items in a collection.

### Changed

- Updated `LockingProxy` class to include `LockingGetItemMixin` and `LockingSetItemMixin` for handling thread-safe item
  access and modification.
- Updated the docstring of `LockingProxy` to reflect the inclusion of the new mixins.

## [0.12.6] 03.12.2024

### Added

- `get_main_event_loop()` allows retrieving the main event loop that was set during the application configuration
  via `initConf()`. `initConf()` is intended to be called from the `MainThread` during the application startup.
  If the event loop has not been set, the function will return `None`.

### Changed

- **BREAKING CHANGE** `run_coroutine_threadsafe` and `arun_coroutine_threadsafe()` was dropped. You can achieve the same
  result
  by using `run_coroutine_threadsafe()` (it just scheduled coroutine to run; it's fast), obtaining
  `threading.Future` and doing the following
  ```asyncio_future = asyncio.wrap_future(threading_future, loop=loop)```. Then if you want to get the
  result you can simply ```await asyncio_future```.


- **Documentation for**
- `asyncio.run_coroutine_threadsafe()` API https://alex-ber.medium.com/asyncio-run-coroutine-threadsafe-api-d24cc56a5255

## [0.12.5] 01.12.2024

### Changed

- Now, in `AsyncExecutionQueue.__init__()` method executor can be None, not passed at all, or passed None
  explicitly. The executor resolution follows a specific order: directly provided executor, executor
  from `initConfig()`, or the default asyncio executor if None is provided. This change is backward compatible.
- **BREAKING CHANGE** Module `warnings.py` was renamed to `warning.py` to avoid shadowing of built-in `warnings.py`.
- **BREAKING CHANGE** In `AsyncExecutionQueue` `worker_task` was renamed to `_worker_task`.
- Dosctrings of `AsyncExecutionQueue` executor role description is added. No functionality change.
- Dosctrings of `exec_in_executor()`, `exec_in_executor_threading_future()` side fact that
  threads in the executor may have an event loop attached was added for clarity. This is also side effect.
- Nonfunctional change in `exec_in_executor_threading_future()`, avoiding variables scope hidden by renaming.
- `AsyncExecutionQueue.aclose()` guard check (`if self._worker_task`) was added. If somehow, between
  `AsyncExecutionQueue.__init__()` and `AsyncExecutionQueue.__aenter__()` exception was raised,
  this avoids `await None`.
- Fixing typo in docstring of `chain_future_results()`.

### Added

- `run_coroutine_threadsafe()`. Sync/regular function, wrapper around `asyncio.run_coroutine_threadsafe()`.
  Internally it uses `_EVENT_LOOP` that is initialized with MainThread's event loop via `initConfig()`.
  It exposes `threading.Future` to the caller. Implementation is pretty **straightforwards**.
- `arun_coroutine_threadsafe()`. Async function/coroutine, wrapper around `asyncio.run_coroutine_threadsafe()`.
  Internally it uses `_EVENT_LOOP` that is initialized with MainThread's event loop via `initConfig()`.
  It exposes `threading.Future` to the caller. Implementation is **complex**. As intermediate step
  `asyncio_future=asyncio.wrap_future(base_future)` where `base_future` is a result of
  `asyncio.run_coroutine_threadsafe()` call is used. `asyncio.wrap_future()` internally, uses private asyncio API.
  It chains `base_future` and newly created `asyncio_future` so that when one completes, so does the other.
  They progress together towards the completion, so **no "application freeze" occur**.

- **Documentation for**
- My `exec_in_executor()` API https://alex-ber.medium.com/my-exec-in-executor-api-72797e232f99
- My `AsyncExecutionQueue` https://alex-ber.medium.com/my-asyncexecutionqueue-4001ac168675

## [0.12.4] 23.11.2024

### Added

- **`AsyncExecutionQueue.add_task()` method**
    - Added a new method `add_task()` to the `AsyncExecutionQueue`, which allows for asynchronous task execution using
      an executor. This method supports both synchronous and asynchronous functions.
    - The executor resolution follows a specific order: directly provided executor, executor from `initConfig()`, or the
      default asyncio executor if none is provided.
    - The method ensures that `ContextVars` are preserved, maintaining context across asynchronous boundaries.
    - The function returns a `threading.Future` object, representing the future result of the task execution.


- **`chain_future_results()` function**:
    - Introduced a new utility function `chain_future_results()` to facilitate the transfer of results or exceptions
      between two futures. It supports both type of Futures, `threading.Future` and `asyncio.Future`.
    - This function is designed to be used as a callback for a `source_future` to propagate its result or exception to
      a `target_future`.
    - It ensures that the outcome of asynchronous operations is correctly handled and transferred, making it a versatile
      tool for managing future results in concurrent programming.

## [0.12.3] 23.11.2024

### Changed

- Now, in `exec_in_executor()` and `exec_in_executor_threading_future()` will preserve the metadata of `func`.

## [0.12.2] 23.11.2024

**BREAKING CHANGE**

### Removed

- All new function that was in 0.12.1 beta-version.

### Added

- Introduced a new type alias `FutureType`. This type is designed to be compatible with both `asyncio.Future`
  and `concurrent.futures.Future`, allowing for functions that can handle both asynchronous and concurrent futures.

- New unit tests for `exec_in_executor()` and `exec_in_executor_threading_future()`.

### Changed

- Now, in `exec_in_executor()` `ensure_thread_event_loop()` will be called in both async/sync function. Before this it
  was only for async functions. That is call to `ensure_thread_event_loop()` is now optional for the users of
  `exec_in_executor()`.

- Inner implementation of `exec_in_executor_threading_future()`. Now, it actually works.

## [0.12.1] 16.11.2024

### Changed

- Updated the `aadd_task()` method in `AsyncExecutionQueue` to explicitly associate futures with the currently running
  event loop. This change enhances the robustness and maintainability of the code by ensuring that futures are bound to
  the correct event loop context, reducing the risk of potential issues related to event loop mismatches in future
  modifications or extensions of the codebase. This update is backward compatible as it does not alter the external
  behavior or interface of the aadd_task method. It only changes the internal implementation detail concerning how
  futures are managed.

## [0.12.0] 16.11.2024

### Added

- Introduced a `deprecated` decorator to mark functions as deprecated.
    - The decorator allows specifying the version since which the function is deprecated, the reason for deprecation,
      and whether the function is marked for removal.
    - It issues a customizable warning (defaulting to `DeprecationWarning`) when the decorated function is called.
    - This addition helps developers communicate deprecation status and future removal plans for functions in the
      codebase.

- **`exec_in_executor_threading_future()`** a thin wrapper around `exec_in_executor()`, which returns an
  `asyncio.Future`. This function executes a function or coroutine within a specified executor and converts the
  resulting `asyncio.Future` into a `threading.Future`. It preserves `ContextVars`, ensuring that context is
  maintained across asynchronous boundaries. For more details, see description of `exec_in_executor()`.

- **`exec_in_executor()`**:
    - Added the `exec_in_executor` function to execute a function or coroutine within a specified executor while
      preserving `ContextVars`.
    - The function ensures context is maintained across asynchronous boundaries and resolves the executor in a
      prioritized manner:
        1. Uses the provided executor if available.
        2. Falls back to an executor set via `initConfig()`.
        3. Defaults to the asyncio executor if none is set.
    - Supports both coroutine and regular function execution.

- **`ensure_thread_event_loop()`**:
    - Implemented the `ensure_thread_event_loop()` function to initialize an event loop for the current thread if it
      does not already exist.


- **`handle_result()`**: Introduced a new `handle_result()` function that transfers the result or exception from one
  future to another.
  This function is designed to be generic and can be used with any types of futures, ensuring that the outcome of task
  execution is properly propagated between futures.
    - **Usage**: To use this function, add it as a callback to the `source_future`:
      ```python
      # Add the chain_future_results function as a callback to the source_future
      source_future.add_done_callback(lambda fut: handle_result(fut, target_future))
      ```
    - **Arguments**:
        - `source_future`: The future from which to retrieve the result or exception.
        - `target_future`: The future on which to set the result or exception.

- **AsyncExecutionQueue**:
- Introduced a new `AsyncExecutionQueue` class for managing asynchronous task execution using a specified executor. This
  class provides:
    - A context manager interface to start and stop a worker that processes tasks from the queue.
    - Asynchronous task execution with graceful queue closure.
    - Note: as a side fact, threads in the executor may have an event loop attached. This allows for the execution of
      asynchronous tasks within those threads.

#### Attributes

- `executor (Executor)`: The mandatory executor used to run tasks.
- `queue (asyncio.Queue)`: The optional queue that holds tasks to be executed. If not provided, a new `asyncio.Queue` is
  created by default.

#### Methods

- `worker()`: Continuously processes tasks from the queue until the `aclose()` method is called.
- `aadd_task(func, *args, **kwargs)`: Asynchronously adds a task to the queue for execution and returns a future.
- `aclose()`: Asynchronously closes the queue and waits for the worker to finish processing.

#### Initialization

- The `AsyncExecutionQueue` is initialized with a specified executor and an optional queue. The executor is required to
  run tasks, while a custom queue can be provided or the default `asyncio.Queue` will be used.

#### Context Management

- The `AsyncExecutionQueue` can be used as a context manager. When entering the context, the worker is started, and when
  exiting, the queue is closed, and the worker is stopped.

### Changed

- **`initConfig()`**:
    - The `initConfig` function now also supports `exec_in_executor()` through the `executor` parameter.
    - This function is designed to be called from the main thread.
    - It accepts optional keyword arguments to configure a global executor that can be (optionally) used
      in `exec_in_executor()`.
- [GitHub Issue #14](https://github.com/alex-ber/AlexBerUtils/issues/14): Enhanced the `lift_to_async` function
  to handle `StopAsyncIteration` exceptions more gracefully. If a `StopAsyncIteration` exception is raised from `afunc`,
  it is now converted to a `RuntimeError` with a descriptive message. This change prevents a `TypeError` from being
  raised and ensures that the `Future` does not remain pending indefinitely.
- Updated `initConfig()`:
    - Initializes the configuration for `lift_to_async()` and `exec_in_executor()`.
    - Ensures it is called from the main thread with a running event loop.
    - Sets a global executor if provided via keyword arguments.

#### Example Usage

```python
# Example usage of the decorator
@deprecated(version='1.0', reason="Use `new_function` instead.", for_removal=True)
def old_function(x, y):
    """Returns the sum of two numbers."""
    return x + y


# Call the deprecated function
result = old_function(3, 4)
```

## [0.11.11] 22.10.2024

### Changed

- **OptionalNumpyWarning**: Moved the definition of `OptionalNumpyWarning` to a separate
  module (`alexber.utils.warnings`) to allow for easier suppression of the warning without triggering it during import.
  This change improves the flexibility and usability of the library for users who do not require NumPy.

- **Type Hints**: Fixed and improved type hints across the codebase to enhance code clarity and support for static type
  checking tools.
  This ensures better integration with IDEs and type checkers, providing more accurate code suggestions and error
  detection.

## [0.11.10] 22.10.2024 yanked

### Added

- **USE_NUMPY**: Introduced a flag to determine whether NumPy is available and should be used for sampling operations.
  This allows for performance optimizations when NumPy is installed.
- **SamplingError**: Added a custom exception class to handle errors when sampling fails after a maximum number of
  retries. It provides detailed error messages, including distribution type, retries, and bounds, aiding in debugging
  and error handling.
- **OptionalNumpyWarning**: Introduced a custom warning to notify users when NumPy is not available, and the system
  falls back to standard Python operations. The primary purpose of this warning is to provide users with the ability to
  suppress it if desired.

### Changed

- **BaseSampler**: Refactored the base class for sampling to support various statistical distributions with configurable
  parameters. This includes validation for distribution types and bounds.
- **Sampler**: The class can optionally use NumPy for sampling if it is available, allowing for potentially faster and
  more efficient sampling operations. If NumPy is not available, the class defaults to using Python's standard `random`
  module, ensuring compatibility across environments.
    - Note: The `expovariate` method has been adjusted to align with NumPy's exponential function, using the scale
      directly as the mean of the distribution.
- **max_retries**: Added a parameter to limit the number of attempts to sample a valid value, primarily to avoid
  infinite loops. This ensures that the sampling process terminates gracefully if a valid sample cannot be obtained
  within the specified number of retries.

## [0.11.9] 21.10.2024

### Added

- **Sampler Class**: Introduced the `Sampler` class, a flexible utility for sampling from various statistical
  distributions.

## Distribution Support

- The class supports multiple distributions, including:
    - **Log-normal (`lognormvariate`)**: Uses `math.log(self.scale)` for the mean of the underlying normal distribution.
    - **Normal (`normalvariate`)**: Standard normal distribution with specified mean and standard deviation.
    - **Exponential (`expovariate`)**: Uses `1 / self.scale` as the rate parameter (lambda).
    - **Von Mises (`vonmisesvariate`)**: Circular distribution with specified mean and concentration.
    - **Gamma (`gammavariate`)**: Two-parameter distribution with shape and scale.
    - **Gaussian (`gauss`)**: Similar to normalvariate, with mean and standard deviation.
    - **Beta (`betavariate`)**: Distribution defined on the interval [0, 1] with two shape parameters.
    - **Pareto (`paretovariate`)**: Heavy-tailed distribution with a single shape parameter.
    - **Weibull (`weibullvariate`)**: Distribution with shape and scale parameters.

## Configurable Parameters

- The class requires configuration of key parameters:
    - `distribution`: Specifies the distribution to sample from (required).
    - `shape`: Shape parameter for the distribution, controlling the spread and skewness. For log-normal, it represents
      sigma of the underlying normal distribution (required).
    - `scale`: Scale parameter for the distribution, shifting the distribution and determining its median. For
      log-normal, it represents exp(mu) of the underlying normal distribution. For exponential, it is the inverse of the
      rate parameter (1/lambda) (required).
    - `lower_bound`: Optional lower bound for the sampled value, defaulting to `None` (no lower bound).
    - `upper_bound`: Optional upper bound for the sampled value, defaulting to `None` (no upper bound).
    - `random_seed`: Optional seed for the random number generator, allowing for reproducibility.
    - `random_instance`: Optional custom instance of `random.Random` for generating random numbers.

## [0.11.8] 01.08.2024

### Changed

- `find_most_similar()` and `find_most_similar()` was fixed for the edge case when no comparison texts are provided.
  If no texts are provided, `find_most_similar_with_scores()` now returns [(some negative number, input_text), 0.0)],
  and `find_most_similar()` returns (some negative number, input_text).
  Docstrings of both method were also changed to reflect this change.

## [0.11.7] 01.08.2024

### Changed

- `find_most_similar()` and `find_most_similar()` was fixed for the edge case when no comparison texts are provided.
  If no texts are provided, `find_most_similar_with_scores()` now returns [(some negative number, input_text), 1.0)],
  and `find_most_similar()` returns (some negative number, input_text).
  Docstrings of both method were also changed to reflect this change.

## [0.11.6] 31.07.2024

### Changed

- `_LRUCache` and `_LFUCache` now supports the in operator.
  Beforehand the work of the `AsyncCache` was incorrect.

## [0.11.5] 31.07.2024 YANKED

### Changed

- `async_cache()` is now support correctly methods.

## [0.11.4] 31.07.2024 YANKED

### Changed

- `async_cache()` is now support correctly methods.

## [0.11.3] 30.07.2024

### Added

- New class `AsyncCache` which supports both LFU (Least Frequently Used) and LRU (Least Recently Used) caching policies
  with optional Time-to-Live (TTL) for cache entries.
  Additional profiling and statistics gathering for cache hits, misses, average, max, and min execution times.
- New decorator `async_cache()` for applying asynchronous caching to a function with configurable caching policy and
  TTL.
- `HashableWrapper` class: This class wraps an object to make it hashable.
  It provides a fallback for objects that do not natively support hashing by using the string representation for
  hashing.
- `make_hashable()` function: A utility to convert various types of objects into hashable types.
  This function handles mappings, iterables, and objects with the `__hash__` method.
  Non-hashable objects are wrapped in a `HashableWrapper` to ensure compatibility as dictionary keys or set members.
- `is_iterable()` function: A utility to check if a value is iterable, excluding `None`, `str`, and `bytes`.
- `is_mapping()` function: A utility to check if a value is a mapping (dict-like).
  It determines this by checking whether the given value implements the `.items()` method
  and whether that method is callable.

## [0.11.2] 30.07.2024

### Added

- Introduced `is_running_in_main_thread()` utility function to check if the current thread
  is the main thread.

- A configuration initializer `initConfig()` has been added.
  You MUST call `initConfig()` from the MainThread before using `lift_to_async()`.
  It initializes the necessary configuration for using the `lift_to_async()` method.

- `lift_to_async()` primitive ensures seamless execution of async functions in a
  synchronous environment, while preserving the context of ContextVar instances.
  It makes easier to integrate async calls within existing synchronous codebases.

### Changed

- In the `RLock` class, the comparison of the lock owner with the current thread/task was changed
  from using the equality operator (`==`) to the `is` operator.
  This change should not affect the functionality because the `__eq__` method was not overridden.

## [0.11.1] 23.07.2024

### Added

- `RLock`  class to provide a reentrant lock that supports both synchronous and asynchronous
  operations. This lock ensures fairness by handling acquisition requests in the order they are made,
  both for threads and asynchronous tasks. See https://alex-ber.medium.com/a6b9a9021be8 for more details

- `LockingProxy` class to provide a comprehensive locking mechanism, ensuring thread-safe access and operations
  for various object types including iterables, async iterables, attributes, and callables. See
  https://alex-ber.medium.com/7a7a14021427 for more details.

- `prof_to_callgrind.sh` script for converting `.prof` files to `.callgrind` format using Docker.
  See https://alex-ber.medium.com/converting-prof-files-to-callgrind-f6ae56fae0d3 for more details.

- `prof_to_callgrind.py` to handle the conversion of `profile` statistics to `callgrind` files.
  See https://alex-ber.medium.com/converting-prof-files-to-callgrind-f6ae56fae0d3 for more details.

# Profiling Script with High-Resolution Timer

This script demonstrates how to set up a cProfile profiler in Python using a high-resolution timer based
on `time.perf_counter_ns()`.

```python
import time
import cProfile


# Define a precise timer function using time.perf_counter_ns()
def precise_timer():
    return time.perf_counter_ns()


# Create a cProfile.Profile object with the precise timer function
profiler = cProfile.Profile(timer=precise_timer)

# Usage note:
# The profiler is now configured to use the precise_timer function, 
# which provides higher resolution timing measurements in nanoseconds.
```

#### Misc

- `pyproject.toml` to specify build system requirements and configurations,
  ensuring compatibility with `setuptools`>=42 and `wheel`.

- `validate_param()` function to check for the presence of required parameters
  and raise a ValueError if they are missing.

- `RootMixin` class with an initializer that stops the delegation chain

- `LockingIterableMixin` to provide locking for iterable objects, ensuring thread-safe iteration.

- `LockingAsyncIterableMixin` to provide locking for asynchronous iterable objects, ensuring thread-safe iteration.

- `LockingAccessMixin` to provide locking for attribute access, ensuring thread-safe access to object attributes.

- `LockingCallableMixin` to provide locking for callable objects, ensuring thread-safe execution of callable objects.

- `LockingDefaultLockMixin` to provide a default lock if none is provided during initialization.

- `LockingBaseLanguageModelMixin` to provide locking for BaseLanguageModel objects, ensuring thread-safe access.

- `LockingDefaultAndBaseLanguageModelMixin` to combine default lock and BaseLanguageModel locking.

## [0.11.0] 22.06.2024

### Added

- New module `in_memory_similarity_search` added.

This module is usable to calculate cosine similarity for small number of vectors.

It has 2 public function and 1 type hint.

- `find_most_similar()` - this function identifies the most similar text to a given
  input text from a list of provided texts. It uses an embedding class instance to convert
  texts into vectors and calculates the cosine similarity based on the specified Euclidean norm.
  The function returns a tuple containing the index and the most similar text.

- `find_most_similar_with_scores()` - this function finds the most similar texts to a given
  input text from a list of provided texts and returns their cosine similarity scores.
  It uses an embedding class instance to convert texts into vectors and calculates the
  similarity based on the specified Euclidean norm.
  The function returns a list of tuples, each containing the index, text, and similarity score.

See https://alex-ber.medium.com/in-memory-similarity-search-998582fbb802 for details.

### Changed

- Removed leftover from multidispatch dependecy.
- Rename python3 to python across the documentation.

## [0.10.2] 05.05.2024

- Lose packaging constraint to packaging>=23.2.
- Fixed README.md to include packaging>=23.2.

## [0.10.1] 31.05.2024

### Changed

- Remove no longer supported Python versions 3.6 and 3.7 from setup's classifier.

## [0.10.0] 31.05.2024

### Changed

- Fixed README.md to include packaging<24.0,>=23.2
- Minimum library requirement is Python 3.8 now (not 3.9).
- Dockerfile's base docker image was changed to be based on Python 3.8 (not 3.9).
- importlib-metadata and it's dependency zipp are back. This is backport from Python 3.9.
  It is a library that provides access to the metadata of installed packages. It was
  originally developed as a backport of the `importlib.metadata` module introduced in Python 3.8,
  allowing users of earlier Python versions to access package metadata in a consistent way.
  It is used by `build`.

## [0.9.1] 31.05.2024

### Changed

- Fixed README.md to reflect changes in 0.9.0.

## [0.9.0] 31.05.2024

### Changed

- Minimum library requirement is Python 3.9 now.
- Dockerfile's base docker image was changed to be based on Python 3.9
- Some functionality was moved from dropped method_overloading_test.py to importer_test.py
- Implementation of inspects.ismethod() and inspects.has_method() was changed.
- inspect_test.py was changed. inspects.has_method() now expected to return False on descriptors, including property,
  (as it should in the first place).
- In Dockerfile setuptools was **downgraded** from 67.8.0 to 65.6.3.
  See https://stackoverflow.com/questions/76043689/pkg-resources-is-deprecated-as-an-api#comment136784284_76044568
- In Dockerfile reference to requirements-md.txt was droped.
- To requirements.in, req-piptools.txt and req-tests.txt packaging<24.0,>=23.2 was added.
- All requirements.txt was regenerated, so version has changed:
- importlib-metadata (and it's dependency zipp) were dropped.
- bcrypt upgraded from 4.1.1 to 4.1.3.
- build from 1.0.3 to 1.2.1.
- cryptography from 41.0.7 to 42.0.7.
- exceptiongroup from 1.2.0 to 1.2.1.
- hiyapyco from 0.5.4 to 0.6.0.
- jinja2 from 3.1.2 to 3.1.4.
- markupsafe from 2.1.3 to 2.1.5.
- pa