Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Minor changes
~~~~~~~~~~~~~

- Deprecate ``icalendar.parser.escape_string`` and ``icalendar.parser.unescape_string`` for icalendar version 8. Use ``_escape_string`` and ``_unescape_string`` internally. :issue:`1011`
- Deprecate ``icalendar.parser.q_split``, ``icalendar.parser.q_join``, and ``icalendar.parser.foldline`` for icalendar version 8. Use ``_q_split``, ``_q_join``, and ``_foldline`` internally. :issue:`1011`
- Deprecate ``icalendar.parser.dquote``, ``icalendar.parser.param_value``, ``icalendar.parser.validate_param_value``, ``icalendar.parser.rfc_6868_escape``, and ``icalendar.parser.rfc_6868_unescape`` for icalendar version 8. Use ``_dquote``, ``_param_value``, ``_validate_param_value``, ``_rfc_6868_escape``, and ``_rfc_6868_unescape`` internally. :issue:`1011`
- Deprecate ``icalendar.parser.escape_char``, ``icalendar.parser.unescape_char``, and ``icalendar.parser.validate_token`` for icalendar version 8. Use ``_escape_char``, ``_unescape_char``, and ``_validate_token`` internally. :issue:`1011`
- Deprecate ``icalendar.parser.unescape_list_or_string``, ``icalendar.parser.unescape_backslash``, ``icalendar.parser.split_on_unescaped_comma``, and ``icalendar.parser.split_on_unescaped_semicolon`` for icalendar version 8. Use ``_unescape_list_or_string``, ``_unescape_backslash``, ``_split_on_unescaped_comma``, and ``_split_on_unescaped_semicolon`` internally. :issue:`1011`
- Added behavioral tests for :class:`~icalendar.cal.lazy.LazyCalendar` covering serialization round-trips, ``.todos``, ``.journals``, forward timezone references, and ``with_uid()`` substring false-positives. :issue:`1050`
- Added edge case tests for :class:`~icalendar.prop.conference.Conference` parameter normalization covering string passthrough, empty list filtering, and None omission. :issue:`925`
- Make icalendar an explicit editable install for clarity. :pr:`1268`
Expand Down
4 changes: 4 additions & 0 deletions src/icalendar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
# chars.
from icalendar.parser import (
Parameters,
_q_join,
_q_split,
q_join,
q_split,
)
Expand Down Expand Up @@ -143,6 +145,8 @@
"TypesFactory",
"__version__",
"__version_tuple__",
"_q_join",
"_q_split",
"is_utc",
"q_join",
"q_split",
Expand Down
8 changes: 4 additions & 4 deletions src/icalendar/cal/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
Contentline,
Contentlines,
Parameters,
q_join,
q_split,
_q_join,
_q_split,
)
from icalendar.parser.ical.component import ComponentIcalParser
from icalendar.parser_tools import DEFAULT_ENCODING
Expand Down Expand Up @@ -372,7 +372,7 @@ def decoded(self, name: str, default: Any = _marker) -> Any:

def get_inline(self, name, decode=1):
"""Returns a list of values (split on comma)."""
vals = [v.strip('" ') for v in q_split(self[name])]
vals = [v.strip('" ') for v in _q_split(self[name])]
if decode:
return [self._decode(name, val) for val in vals]
return vals
Expand All @@ -383,7 +383,7 @@ def set_inline(self, name, values, encode=1):
"""
if encode:
values = [self._encode(name, value, encode=1) for value in values]
self[name] = self.types_factory["inline"](q_join(values))
self[name] = self.types_factory["inline"](_q_join(values))

#########################
# Handling of components
Expand Down
85 changes: 65 additions & 20 deletions src/icalendar/caselessdict.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import annotations

import warnings
from collections import OrderedDict
from typing import TYPE_CHECKING, Any, TypeVar

from icalendar.parser_tools import to_unicode
from icalendar.parser_tools import _to_unicode

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping
Expand All @@ -17,7 +18,7 @@
VT = TypeVar("VT")


def canonsort_keys(
def _canonsort_keys(
keys: Iterable[KT], canonical_order: Iterable[KT] | None = None
) -> list[KT]:
"""Sort leading keys according to a canonical order.
Expand All @@ -38,8 +39,8 @@ def canonsort_keys(
Example:
.. code-block:: pycon

>>> from icalendar.caselessdict import canonsort_keys
>>> canonsort_keys(["C", "A", "B"], ["B", "C"])
>>> from icalendar.caselessdict import _canonsort_keys
>>> _canonsort_keys(["C", "A", "B"], ["B", "C"])
['B', 'C', 'A']
"""
canonical_map = {k: i for i, k in enumerate(canonical_order or [])}
Expand All @@ -48,7 +49,26 @@ def canonsort_keys(
return sorted(head, key=lambda k: canonical_map[k]) + sorted(tail)


def canonsort_items(
def canonsort_keys(
keys: Iterable[KT], canonical_order: Iterable[KT] | None = None
) -> list[KT]:
"""Sort leading keys according to a canonical order.

.. deprecated:: 7.0.0
Use the private :func:`_canonsort_keys` internally. For external use,
this function is deprecated. Please contact the maintainers if you
rely on this function.
"""
warnings.warn(
"canonsort_keys is deprecated and will be removed in a future version. "
"If you are using this function externally, please contact the maintainers.",
DeprecationWarning,
stacklevel=2,
)
return _canonsort_keys(keys, canonical_order)


def _canonsort_items(
dict1: Mapping[KT, VT], canonical_order: Iterable[KT] | None = None
) -> list[tuple[KT, VT]]:
"""Sort items from a mapping according to a canonical key order.
Expand All @@ -64,11 +84,30 @@ def canonsort_items(
Example:
.. code-block:: pycon

>>> from icalendar.caselessdict import canonsort_items
>>> canonsort_items({"C": 3, "A": 1, "B": 2}, ["B", "C"])
>>> from icalendar.caselessdict import _canonsort_items
>>> _canonsort_items({"C": 3, "A": 1, "B": 2}, ["B", "C"])
[('B', 2), ('C', 3), ('A', 1)]
"""
return [(k, dict1[k]) for k in canonsort_keys(dict1.keys(), canonical_order)]
return [(k, dict1[k]) for k in _canonsort_keys(dict1.keys(), canonical_order)]


def canonsort_items(
dict1: Mapping[KT, VT], canonical_order: Iterable[KT] | None = None
) -> list[tuple[KT, VT]]:
"""Sort items from a mapping according to a canonical key order.

.. deprecated:: 7.0.0
Use the private :func:`_canonsort_items` internally. For external use,
this function is deprecated. Please contact the maintainers if you
rely on this function.
"""
warnings.warn(
"canonsort_items is deprecated and will be removed in a future version. "
"If you are using this function externally, please contact the maintainers.",
DeprecationWarning,
stacklevel=2,
)
return _canonsort_items(dict1, canonical_order)


class CaselessDict(OrderedDict):
Expand Down Expand Up @@ -100,7 +139,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
super().__init__(*args, **kwargs)
for key, value in self.items():
key_upper = to_unicode(key).upper()
key_upper = _to_unicode(key).upper()
if key != key_upper:
super().__delitem__(key)
self[key_upper] = value
Expand All @@ -120,7 +159,7 @@ def __getitem__(self, key: Any) -> Any:
Raises:
KeyError: If the key is not found.
"""
key = to_unicode(key)
key = _to_unicode(key)
return super().__getitem__(key.upper())

def __setitem__(self, key: Any, value: Any) -> None:
Expand All @@ -130,7 +169,7 @@ def __setitem__(self, key: Any, value: Any) -> None:
key: The key of the pair, case-insensitive.
value: The value to associate with the key.
"""
key = to_unicode(key)
key = _to_unicode(key)
super().__setitem__(key.upper(), value)

def __delitem__(self, key: Any) -> None:
Expand All @@ -142,7 +181,7 @@ def __delitem__(self, key: Any) -> None:
Raises:
KeyError: If the key is not found.
"""
key = to_unicode(key)
key = _to_unicode(key)
super().__delitem__(key.upper())

def __contains__(self, key: Any) -> bool:
Expand All @@ -154,7 +193,7 @@ def __contains__(self, key: Any) -> bool:
Returns:
``True`` if the uppercased key exists, else ``False``.
"""
key = to_unicode(key)
key = _to_unicode(key)
return super().__contains__(key.upper())

def get(self, key: Any, default: Any = None) -> Any:
Expand All @@ -167,7 +206,7 @@ def get(self, key: Any, default: Any = None) -> Any:
Returns:
The value for the key, if present, else the value specified by ``default``.
"""
key = to_unicode(key)
key = _to_unicode(key)
return super().get(key.upper(), default)

def setdefault(self, key: Any, value: Any = None) -> Any:
Expand All @@ -182,7 +221,7 @@ def setdefault(self, key: Any, value: Any = None) -> Any:
Returns:
The value for the key.
"""
key = to_unicode(key)
key = _to_unicode(key)
return super().setdefault(key.upper(), value)

def pop(self, key: Any, default: Any = None) -> Any:
Expand All @@ -195,7 +234,7 @@ def pop(self, key: Any, default: Any = None) -> Any:
Returns:
The removed value, or the value of ``default``.
"""
key = to_unicode(key)
key = _to_unicode(key)
return super().pop(key.upper(), default)

def popitem(self) -> tuple[Any, Any]:
Expand All @@ -220,7 +259,7 @@ def has_key(self, key: Any) -> bool:
Returns:
``True`` if the key exists, else ``False``.
"""
key = to_unicode(key)
key = _to_unicode(key)
return super().__contains__(key.upper())

def update(self, *args: Any, **kwargs: Any) -> None:
Expand Down Expand Up @@ -298,7 +337,7 @@ def sorted_keys(self) -> list[str]:
Returns:
A sorted list of keys.
"""
return canonsort_keys(self.keys(), self.canonical_order)
return _canonsort_keys(self.keys(), self.canonical_order)

def sorted_items(self) -> list[tuple[Any, Any]]:
"""Sort items according to the canonical order for this class.
Expand All @@ -309,7 +348,13 @@ def sorted_items(self) -> list[tuple[Any, Any]]:
Returns:
A sorted list of (key, value) tuples.
"""
return canonsort_items(self, self.canonical_order)
return _canonsort_items(self, self.canonical_order)


__all__ = ["CaselessDict", "canonsort_items", "canonsort_keys"]
__all__ = [
"CaselessDict",
"_canonsort_items",
"_canonsort_keys",
"canonsort_items",
"canonsort_keys",
]
43 changes: 43 additions & 0 deletions src/icalendar/compatibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,50 @@
Members will be added and removed without deprecation warnings.
"""

import functools
import warnings
from typing import TYPE_CHECKING


def deprecate_for_version_8(func):
"""Return a deprecated public alias for *func*.

Wraps *func* so that every call emits a :exc:`DeprecationWarning` and
then delegates to the original implementation. The public name used in
the warning message is derived from ``func.__name__`` by stripping a
leading underscore.

Use like this::

def _q_join(...):
\"\"\"docstring...\"\"\"
...

q_join = deprecate_for_version_8(_q_join)

Parameters:
func: The private implementation to wrap.

Returns:
A wrapper with the same signature and docstring as *func* that emits
a :exc:`DeprecationWarning` on every call.
"""
public_name = func.__name__.lstrip("_")

@functools.wraps(func)
def wrapper(*args, **kwargs):
warnings.warn(
f"{public_name} is deprecated and will be removed in icalendar 8. "
"If you are using this function externally, "
"please contact the maintainers.",
DeprecationWarning,
stacklevel=2,
)
return func(*args, **kwargs)

return wrapper


try:
from typing import Self
except ImportError:
Expand Down Expand Up @@ -37,4 +79,5 @@
"Self",
"TypeGuard",
"TypeIs",
"deprecate_for_version_8",
]
26 changes: 26 additions & 0 deletions src/icalendar/parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
from .content_line import Contentline, Contentlines
from .parameter import (
Parameters,
_dquote,
_param_value,
_q_join,
_q_split,
_rfc_6868_escape,
_rfc_6868_unescape,
_validate_param_value,
dquote,
param_value,
q_join,
Expand All @@ -18,6 +25,10 @@
validate_param_value,
)
from .property import (
_split_on_unescaped_comma,
_split_on_unescaped_semicolon,
_unescape_backslash,
_unescape_list_or_string,
split_on_unescaped_comma,
split_on_unescaped_semicolon,
unescape_backslash,
Expand All @@ -26,8 +37,10 @@
from .string import (
_escape_char,
_escape_string,
_foldline,
_unescape_char,
_unescape_string,
_validate_token,
escape_char,
escape_string,
foldline,
Expand All @@ -40,10 +53,23 @@
"Contentline",
"Contentlines",
"Parameters",
"_dquote",
"_escape_char",
"_escape_string",
"_foldline",
"_param_value",
"_q_join",
"_q_split",
"_rfc_6868_escape",
"_rfc_6868_unescape",
"_split_on_unescaped_comma",
"_split_on_unescaped_semicolon",
"_unescape_backslash",
"_unescape_char",
"_unescape_list_or_string",
"_unescape_string",
"_validate_param_value",
"_validate_token",
"dquote",
"escape_char",
"escape_string",
Expand Down
Loading