Skip to content
Merged
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
12 changes: 12 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ v1.3.1
- Fixed :func:`tkclasswiz.convert.convert_objects_to_script` not including enum imports.


v1.4.0
================
- Definition of enums and literal values inside iterable types.
- Ability to register deprecated parameters.
- Ability to define :class:`enum.Flag` like flags.


v1.3.1
================
- Fixed :func:`tkclasswiz.convert.convert_objects_to_script` not including enum imports.


v1.3.0
================
- The types will now have their subscripted type displayed alongside them.
Expand Down
42 changes: 42 additions & 0 deletions docs/source/guide/defining.rst
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,48 @@ This is how our frame looks after defining 4 ``Wheel`` objects:
:width: 15cm


Defining (enum) flags
=======================
Let's say that our car also needs a type designation. The ``Car`` class may contain an enum flag parameter,
indicating this designation.

.. code-block:: python

from enum import Flag, auto

class Designation(Flag):
FAMILIY = auto()
SINGLE_PERSON = auto()
HEAVY_TRANSPORT = auto()

class Car:
def __init__(
self,
...
designation: Designation,
):
...

When we try to define the ``designation`` parameter from the above example,
the following window will be displayed.

.. image:: ./images/new_define_frame_flag.png
:width: 15cm

The flag definition frame has 4 main elements:

- a placeholder for the current value,
- a Combobox (dropdown menu) for selecting a flag
- an add button for adding the selected (in Combobox) flag to the current flag value.
- a remove button for removing the selected (in Combobox) flag from the current flag value.

The following image shows show two added flags look inside the placeholder.

.. image:: ./images/new_define_frame_flag_values.png
:width: 15cm

The flag value can be saved like any other type. It is done by clicking on the "Save" button.

Final definition
=================

Expand Down
34 changes: 34 additions & 0 deletions docs/source/guide/deprecations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
========================
Deprecations
========================

TkClassWizard allows users to deprecate different classes, class's parameters and types under a class's parameter.

All the deprecations can be made with the :func:`tkclasswiz.deprecation.register_deprecated` function.
The function has 3 modes:

- Deprecate class globally (only ``cls`` parameter given)
- Deprecate a class's parameter (``cls`` and ``parameter`` both given)
- Deprecate a type under class's parameter (``cls``, ``parameter`` and ``types`` are all given).
Please note that ``types`` is a variadic parameter, which means multiple types can be passed by
just separating them with a comma.


.. code-block:: python
:caption: Deprecate usage of type :class:`~datetime.timedelta` under parameter ``next_service`` of class ``Car``.

from datetime import timedelta, datetime
import tkclasswiz as wiz

class Car:
def __init__(self, name: str, next_service: timedelta | datetime):
... # Implementation

wiz.register_deprecated(Car, "next_service", timedelta)
... # Other needed code


The above example will create the following definition window:

.. image:: ./images/new_define_frame_struct_deprecation.png

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/source/guide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ Index:
customrepr
aliasing
generics
deprecations
2 changes: 1 addition & 1 deletion docs/source/scripts/generate_autodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
if manual:
_async_ = ":async:" if inspect.iscoroutinefunction(item) else ""
annotations = get_type_hints(item)
return_ano = annotations.pop("return")
return_ano = annotations.pop("return", None)
doc_str = inspect.cleandoc(item.__doc__)
# Replace titles with list titles
doc_str_titles = re.findall(r"[A-z]+\n-+", doc_str)
Expand Down
2 changes: 1 addition & 1 deletion requirements/docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ sphinx-copybutton~=0.5.2
furo==2023.9.10
enum-tools[sphinx]~=0.11.0
sphinx-design[furo]~=0.5.0
readthedocs-sphinx-search~=0.3.1
readthedocs-sphinx-search~=0.3.2
sphinxcontrib-svg2pdfconverter~=1.2.2
5 changes: 4 additions & 1 deletion tkclasswiz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
__version__ = "1.3.1"

__version__ = "1.4.0"


from .object_frame import *
from .annotations import *
Expand All @@ -37,3 +39,4 @@
from .storage import *
from .utilities import *
from .aliasing import *
from .deprecation import *
2 changes: 1 addition & 1 deletion tkclasswiz/annotations.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""
Module used for managing annotations.
"""
from datetime import datetime, timedelta, timezone
from typing import Union, Optional, get_args, Generic, get_origin, get_type_hints
from datetime import datetime, timedelta, timezone
from contextlib import suppress
from inspect import isclass
from .doc import doc_category
Expand Down
78 changes: 78 additions & 0 deletions tkclasswiz/deprecation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Module can be used to mark deprecated classes / parameters / parameter types.
"""
from typing import overload

from .doc import doc_category


__all__ = (
"register_deprecated",
"is_deprecated",
)


@overload
@doc_category("Deprecations", manual=True)
def register_deprecated(cls: type):
"""
Mark ``cls`` deprecated globally. This function cannot be
used on built-in Python classes.
"""

@overload
@doc_category("Deprecations", manual=True)
def register_deprecated(cls: type, parameter: str):
"""
Marks a ``parameter`` to be deprecated under specific ``cls``.
"""

@overload
@doc_category("Deprecations", manual=True)
def register_deprecated(cls: type, parameter: str, *types: type):
"""
Marks multiple ``types`` to be deprecated for a certain ``parameter`` under ``cls``.
"""

def register_deprecated(cls, parameter: str = None, *types: type):
if parameter is None:
cls.__deprecated__ = True

elif not types:
cls.__wiz_deprecated_params__ = getattr(cls, "__wiz_deprecated_params__", set())
cls.__wiz_deprecated_params__.add(parameter)
else:
cls.__wiz_deprecated_param_types__ = getattr(cls, "__wiz_deprecated_param_types__", dict())
cls.__wiz_deprecated_param_types__[parameter] = cls.__wiz_deprecated_param_types__.get(parameter, set())
cls.__wiz_deprecated_param_types__[parameter].update(types)

@overload
@doc_category("Deprecations", manual=True)
def is_deprecated(cls: type):
"""
Checks if ``cls`` is deprecated globally.
"""

@overload
@doc_category("Deprecations", manual=True)
def is_deprecated(cls: type, parameter: str):
"""
Checks if ``parameter`` is deprecated under ``cls``.
"""

@overload
@doc_category("Deprecations", manual=True)
def is_deprecated(cls: type, parameter: str, type_: type):
"""
Checks if ``type_`` is deprecated for a certain ``parameter`` under ``cls``.
"""

def is_deprecated(cls: type, parameter: str = None, type_: type = None):
if parameter is None:
return hasattr(cls, "__deprecated__")

if type_ is None:
params = getattr(cls, "__wiz_deprecated_params__", set())
return parameter in params

return type_ in getattr(cls, "__wiz_deprecated_param_types__", dict()).get(parameter, set())
43 changes: 35 additions & 8 deletions tkclasswiz/object_frame/frame_base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import get_args, get_origin, Iterable, Union, Literal, Any, TYPE_CHECKING, TypeVar, Generic
from abc import ABC, abstractmethod
from inspect import isabstract
from contextlib import suppress
from itertools import chain
Expand Down Expand Up @@ -32,7 +33,7 @@

@extendable
@doc_category("Object frames")
class NewObjectFrameBase(ttk.Frame):
class NewObjectFrameBase(ttk.Frame, ABC):
"""
Base Frame for inside the :class:`ObjectEditWindow` that allows object definition.

Expand Down Expand Up @@ -121,6 +122,36 @@ def get_cls_name(cls: Any, args: bool = False) -> str:
def set_origin_window(cls, window: "ObjectEditWindow"):
cls.origin_window = window

@classmethod
def filter_literals(cls, types_: Iterable):
"Returns only the literal types from ``types_``"
return [x for x in types_ if get_origin(x) is Literal]

@classmethod
def check_literals(cls, value: str, literal_types: Iterable):
"""
Checks if the ``value`` is a correct iterable.

Parameters
-------------
value: str
The string value to be checked.
types: Iterable[Literal]
An iterable of accepted literals.
"""
allowed_values = []
for literal_type in literal_types:
args = get_args(literal_type)
if value in args:
return value

allowed_values.extend(args)

raise ValueError(
f"'{value}' (a string) is not one of accepted types and does not match any literal values.\n"
f"'Allowed literals: {allowed_values}."
)

@classmethod
def cast_type(cls, value: Any, types: Iterable):
"""
Expand All @@ -136,13 +167,6 @@ def cast_type(cls, value: Any, types: Iterable):
dict: lambda v: convert_to_object_info(json.loads(v))
}

# Validate literals
if get_origin(types[0]) is Literal:
if value not in (args := get_args(types[0])):
raise ValueError(f"'{value}' is not a valid value'. Accepted: {args}")

return value

for type_ in filter(lambda t: t.__module__ == "builtins", types):
with suppress(Exception):
cast_funct = CAST_FUNTIONS.get(type_)
Expand Down Expand Up @@ -264,12 +288,14 @@ def new_object_frame(
class_, widget, allow_save=allow_save, *args, **kwargs
)

@abstractmethod
def to_object(self):
"""
Creates an object from the GUI data.
"""
raise NotImplementedError

@abstractmethod
def load(self, old_data: Any):
"""
Loads the old object info data into the GUI.
Expand Down Expand Up @@ -305,6 +331,7 @@ def remember_gui_data(self):
"""
self._original_gui_data = self.get_gui_data()

@abstractmethod
def get_gui_data(self) -> Any:
"""
Returns all GUI values.
Expand Down
Loading