# History
## 0.1.2 (2026-03-02)
- **get_locale**, **get_country**: return `dict` instead of `MappingProxyType` to fix Zope Restricted Python compatibility (`Unauthorized` on item access). Internally, `lru_cache` still stores `MappingProxyType` for cache safety.

## 0.1.1 (2026-02-22)
- **dict_from_dict_list**: new parameter **allow_duplicates** (default `True`); restores original behavior where duplicate keys are silently overwritten (last entry wins). Use `allow_duplicates=False` to enforce uniqueness.
- fixed Changelog URL in pyproject.toml (`main` → `master`)

## 0.1.0 (2026-02-22)
- Sphinx documentation theme switched from **sphinx-rtd-theme** to **furo** (fixes missing version number on GitLab Pages)
- **conf.py**: robust path resolution via `__file__`, added `release` setting
- tests changed from unittest to pytest
- removed setup.py
- better error messages for **dict_from_dict_list**
- requires: < Python 3.13 (instead of 3.12)
- **leet** can now handle strings with numbers without throwing an Error
- added missing tests for **itb_restricted_python** (contains_all, contains_any, remove_duplicates)
- added missing tests for **itb_security** (create_secure_key, create_key, check_password_security)
- added tests for **check_spam** and **compare_phonenumbers** in itb_email_phone
- added test for **fetch_all_countries** in itb_locales
- added tests for **normalize_text** and **sort_word_list** in itb_text_name
- added pendulum conversion tests (**str_to_pendulum**, **DT_to_pendulum**, **dt_to_pendulum**, **pendulum_to_DT**) and tests for **get_monday**, **create_timedelta** in itb_date_time
- Sphinx **conf.py**: reads version and author from **pyproject.toml** instead of removed setup.cfg
- added **pendulum** to dependencies in pyproject.toml
- Python 3.8 compatibility: conditional imports for `assert_never` (typing_extensions) and `zoneinfo` (backports.zoneinfo)
- added **typing_extensions** and **backports.zoneinfo** as conditional dependencies in pyproject.toml
- **itb_date_time**: bugfixes from code review:
  - **get_dateformat**: fixed missing colon in seconds format (`'%H:%M%S'` → `'%H:%M:%S'`); added test data with seconds
  - **date_to_dt**: fixed falsy check on `H` and `M` parameters (`if H and M` → `if H is not None and M is not None`); `H=0` or `M=0` were silently ignored; added `test_date_to_dt_with_zero_time`
  - **convert_datetime**: added missing identity case for pendulum→pendulum conversion
- **itb_email_phone**: bugfixes from code review:
  - **create_email_message**: removed BCC from MIME headers (was exposing BCC recipients); added **reply_to** parameter
- **itb_date_time** major refactoring:
  - **is_valid_datetime_string**: rewritten as standalone function (no longer wraps **str_to_dt**), supports ISO8601 and US date format (mm/dd/yyyy)
  - **get_dateformat**: new parameter **for_type** ('strptime', 'DateTime', 'pendulum'); **for_DateTime** kept but deprecated
  - **str_to_dt**, **str_to_DT**, **str_to_date**, **str_to_pendulum**: new **tz** parameter (default: 'Europe/Vienna')
  - **date_to_dt**, **date_to_DT**: new optional **tz** parameter (default: None = naive)
  - **convert_datetime**: new optional **tz** parameter, passed through to str_to_* and date_to_* functions
  - **DT_to_dt**: now uses **asdatetime()** to preserve timezone
  - **DT_to_pendulum**: simplified to **pendulum.instance()**, preserves timezone and seconds
  - **dt_to_pendulum**: uses **pendulum.instance()**, preserves timezone and seconds (naive input assumes UTC)
  - **pendulum_to_DT**: now preserves seconds
  - **remove_time**: simplified, delegates to **convert_datetime**, supports all type combinations
  - **get_monday**: simplified to **fromisocalendar()**
  - **day_from_week**: simplified to **fromisocalendar()** (was generating all week days)
  - **daterange_from_week**, **dates_from_week**, **monday_from_week**: removed unused **fmt** parameter, fixed docstrings
  - **create_timedelta**: fixed bug (days stayed as string), renamed parameter **dt** to **returning** (dt kept as deprecated alias)
  - **add_time**: fixed isinstance order (pendulum before datetime), implemented **returning** parameter, fixed subtract bug
  - **unravel_duration**: fixed bug (tuple returning was missing return), simplified with divmod
  - added timezone handling documentation in module docstring
  - **DEFAULT_TIMEZONE** = 'Europe/Vienna' as module constant
- **itb_locales** refactoring:
  - **get_locale**: returns `MappingProxyType` (immutable) to protect `lru_cache`; explicit `None` check instead of try/except; safe currency lookup for countries without currency (e.g. Antarctica)
  - **fetch_all_countries**: removed `lru_cache`; `KEYS_TO_TRANSLATE` and `VALID_COUNTRY_KEYS` as module-level `frozenset` constants; fixed single-key sorting (was trying `tup[-1]` on plain strings); split `translate` into two definitions (with/without translation); removed redundant `returning` validation (handled by `Literal` type hint); documented sort-key behavior in docstring
  - **format_price**: **loc** parameter now accepts strings like `'de_AT'` or `'AT'` in addition to `Mapping`; uses `collections.abc.Mapping` instead of `dict` for type check (supports `MappingProxyType`)
  - **get_country**: returns `MappingProxyType`; `.upper()` only applied for alpha_2/alpha_3 keys; uses `alpha_2` from found country for Babel locale lookup; raises `LookupError` if country not found; `getattr` with default to avoid `AttributeError`
  - **fetch_holidays**: fixed mutable default argument (`years=[]` → `years=None`); removed broken early-return code path in `years` branch; inlined `remove_dates` closure; `length` limit now applies to all returning types; `Exception` → `ValueError`; **names** returning now preserves chronological order
  - **is_holiday**: **fmt** parameter now passed to `convert_datetime`; improved docstring
- **itb_email_phone**:
  - **create_email_message**: removed duplicate branch; `Exception` → `ValueError`; fixed `Content-Disposition` header (uses `add_header` keyword argument for RFC-compliant filename encoding)
- **itb_html**:
  - **change_h_tags**: fixed double-shifting bug (h-tags collected before modifying); early return for `change=0`
- **itb_text_name**:
  - **could_be_a_name**: added `cast(HumanName, ...)` for type safety; removed outdated docstring note
  - **get_gender**: renamed module-level detector to **GENDER_DETECTOR**; maps `mostly_male` → `'male'` and `mostly_female` → `'female'`
  - **leet**: fixed inverted `symbol_chance` logic; fixed edge case in `max_length` with `start_at_begin=False`; documented all parameters
  - **sort_names**: removed dead code; fixed duplicate-key loss (dict → tuple-list sorting); added `'van der'`, `'von der'`, `'de la'` to **NAME_PREFIXES**
  - **map_special_chars**: iterates `TRANS_DICT.items()` directly; improved docstring
  - **normalize_text**: replaced manual dict-based implementation with `unicodedata.normalize('NFC', text)`
  - **normalize_name**: fixed docstring
  - **sort_word_list**: fixed duplicate-key loss (dict → tuple-list sorting)
- **itb_www**:
  - **change_query_string**: `zope_data_types` → **ZOPE_DATA_TYPES** (module-level constant); `Exception` → `ValueError`; unreachable `return ''` replaced with `assert_never(returning)`
  - added tests for `returning='auto'`, missing arguments (`TypeError`), and Zope data type unescaping
- code review cleanup (points 21–30):
  - removed `sys.path` mutation from `__init__.py`; all cross-module imports changed to relative imports
  - all test imports changed to `from invesytoolbox.itb_xxx import ...`
  - added `python-dateutil` to dependencies in pyproject.toml
  - fixed module docstrings (`xxx_tools` → `itb_xxx`) in all 9 modules
  - removed unused `datetime_chars` set from itb_data.py
  - fixed typo "portentionally" → "potentially" in itb_data.py
  - renamed `attPart` → `att_part` in itb_email_phone.py
  - documented import-time side effects in itb_locales.py and itb_email_phone.py
  - activated assertion in `test_adjust_spaces_on_punctuation`
  - activated `test_leet` with `random.seed(42)` for reproducibility
  - renamed `test_split_name` → `test_sort_names`
  - added assertion to `test_prettify_html`
- type hints improvements:
  - replaced bare `list`/`dict` return types with `List[...]`/`Dict[...]` across all modules
  - added `Literal` types for all `returning` parameters (9 functions)
  - fixed return types: `get_isocalendar` → `Tuple[int, int, int]`, `get_monday` → `DateTimeType`, `str_to_date` → `datetime.date`, `daterange_from_week` → `Tuple[DateTimeType, DateTimeType]`
- completed Sphinx docstrings: added missing `:param:` and `:return:` directives to all public functions across all 9 modules
- **itb_data**:
  - **any_2boolean**: fixed docstring typo ("signifie" → "signify")
  - **dict_from_dict_list**: uniqueness check now applies for all modes (including `include_key=True`); updated docstring
  - **dict_2unicode**: fixed docstring typo ("unchaged" → "unchanged")
  - **dict_2datatypes**: fixed mutable default argument (`metadata={}` → `metadata=None`); corrected `convert_keys` docstring
  - **dictlist_2datatypes**: fixed mutable default argument; corrected `:param dictList` → `:param dictlist`
  - **sort_dictlist**: `reverse` now works with multi-key sorting; corrected `:param dictList` → `:param dictlist`
  - **value_2datatype**: `raise Exception` → `raise ValueError`; renamed shadowed `dt` variable to `parsed_dt` in time branch
  - comprehensive test suite expansion (45 → 100 tests): validation error tests for **dict_from_dict_list**, edge cases for **dict_2unicode**, all type branches for **value_2datatype** (typ-based and guessing mode), reverse/comma-string tests for **sort_dictlist**, mutation safety tests

## 0.0.26 (2025-07-13)
- **value_2datatype**: new *typ* "string". This of course also affects *metadata* of **value_2datatype**, **dict_2datatypes** and **dictlist_2datatypes**

## 0.0.25 (2025-05-21)
- **fetch_all_countries** now also works with newer versions of **pycountry** (iso3166-1 used instead of iso3166)
- replaced **setup.cfg** with **pyproject.toml**

## 0.0.24 (2024-09-29)
**timezone** can be passed as an argument to **dictlist_2datatypes** and **dict_2datatypes**

## 0.0.23 (2024-07-22)
* **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
* **value_2datatype**: 
  - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
  - 'pendulum' is now an option for the parameter 
- default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
	- **dict_2datatypes**
	- **dictlist_2datatypes**

## 0.0.22 (2023-10-14)
* **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
* **get_locale**: locale can be only the language, without the country.
* if get_locale fails to get the locale from the system (because invesytoolbox may be imported from a program called from an applicatio rather than the termial), it defaults to `language: 'de'` and `country: 'AT'`.
* added support for pendulum DateTime
* **create_email_message**: fixed MIMEMultipart settings

## 0.0.21 (2023-05-21)
* New function **unravel_duration**: turns a duration string (for ex. '5T23:05:20') into a list or dictionary.

## 0.0.20 (2023-05-06)
* New function **add_time**: adds (or subtracts) time to (or from) datetime and DateTime

## 0.0.19 (2023-04-07)
* Corrected bug in **fetch_holidays** (argument *length* resulted in an error)

## 0.0.18 (2023-04-07)
* new function **change_h_tags** (in new module **itb_html**)

## 0.0.17 (2023-03-01)
* new function *compare_phonenumbers*: compares two phone numbers after normalizing them (*process_phonenumber*).

## 0.0.16
* *map_special_chars*: works now.

## 0.0.15
* *could_be_a_name*: can now handle names with multiple parts like "Robert De La Mancha". Per word, 2 capitals are allowed (i.e. "MacArthur" or "DeLa Cruz)

## 0.0.14
* *could_be_a_name* and *sort_names*: now working also with prename-only and prenames including a hyphen.

## 0.0.13
* *get_dateformat* now also processes time
* *str_to_dt* now checks for valid string
* *is_valid_datetime_string*: wrapper for checking with *str_to_dt*
* *remove_time* from datetime or DateTime

## 0.0.12
* removed documentation from the README file, instead a link to the gitlab pages
* *map_specialChars* now recognizes Unicode character U+0308 (UTF-8 cc 88 = "COMBINING DIAERESIS").
* *any_2boolean*
* *get_dateformat*: argument *checkonly*
* data functions: argument *json_data* changed to *metadata* (it's a dictionary)

## 0.0.11
* BeautifulSoup and nameparser added to requirements.txt
* Removed "Date" conversions (modified DateTime) because it's a bad idea
* *normalize_name* (using nameparser) added to *itb_text_name*
* *capitalize_name* rewritten (now quasi a wrapper for normalize_name)
* *could_be_a_name* rewritten using *normalize_name* (nameparser)

## 0.0.10
* *check_spam* for web forms
* *dictlist_2datatypes*: iterates through a list of dictionaries and applies *dict_2datatypes*
* *prettify_html*: provides *prettify* from BeautifulSoup, because it can't be used directly from restricted Python.
* *could_be_a_name*: checks if a string could be a name

## 0.0.9
* *change\_query\_string* respects Zope parameter converters (like `paramname:int`)

## 0.0.8
* New submodule www
   * *change\_query\_string*

## 0.0.7
* shorter submodule names (no _tools suffix)
* *is_holiday* works without argument
* *create\_email\_message*: new argument **encoding**
* *process_phonenumbers*: cleaned up arguments
* *DT_date*: strip a DateTime of its time (get a "naked" date)
* *could\_be\_a\_name*: check if a string could possibly be a name

## 0.0.6
* renamed *capitalize\_text* to *capitalize_name* and removed name argument
* added Sphinx documentation

## 0.0.5 (2022-06-11)
* removed **terminal_tools** (will be included in a separate package)

## 0.0.4 (2022-06-09)
* better formatted README

## 0.0.3 (2022-06-09)
* updated README (list of functions and a short description)

## 0.0.2 (2022-06-09)
* removed VERSION file

## 0.0.1 (2022-06-09)
* first version
