Name | modkit JSON |
Version |
1.3.0
JSON |
| download |
home_page | None |
Summary | A kit containing tools for better programming. |
upload_time | 2025-01-05 23:48:34 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | MIT License Copyright (c) 2025 Soumyo Deep Gupta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
keywords |
d33p0st
modkit
modstore
module
kit
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
[![PyPI Downloads](https://static.pepy.tech/badge/modkit)](https://pepy.tech/projects/modkit)
# Overview
`modkit` is a collection of tools that can be used for better programming
experience and automation.
## Features
- `@override` decorator for python3.8+
- New `Property` class that automates property definition and takes less number of lines of code to define.
- New `Possibility` class that helps extend return types with custom methods/properties which in turn helps in better readability of code.
## Usage
1. `@override` decorator and functionality.
The `@override` decorator was introduced in python3.12+ under the `typing` module. However, for python<3.12, it is not available.
The `override` class under `modkit` supports python3.8+ and brings
additional features rather than just helping in maintaining an error
free codebase.
The `override` class contains two static methods that can be used as
decorators: `cls` and `mtd`. The `@override.mtd` acts as a decorator
for the child class methods which are being over-ridden from the
parent class. It sets the `__override__` attribute of the methods,
it has been decorated with, as `True`.
The `@override.cls` decorator on top of a child class, checks for
all methods that have their `__override__` attribute set as `True`
and it generates errors accordingly. It will raise
`MethodOverrideError` if the method set as override is not present in
the parent class. Additionally, it will also check if the current class
is a child class or not (inheritance is implemented or not).
Apart from methods, the properties need not be decorated with `mtd`,
the `@override.cls` over the class will automatically check if the
property is over-ridden or not. If a over-ridden property does not include a `setter` or `deleter` or both, the parent property's
`setter` and `deleter` will be copied to the child class.
### Use case
1. Import the `override` class from `modkit.typing`
```python
from modkit.typing import override
```
2. Define a Base Class.
```python
class Base:
def __init__(self) -> None:
self.value = 10
def base_method(self) -> None:
self.value += 1
@property
def base_property(self) -> int:
return self.value
@base_property.setter
def base_property(self, value) -> None:
self.value = value
@base_property.deleter
def base_property(self) -> None:
self.value = 0
```
3. Define the Child Class with `override` class.
```python
@override.cls # valid
class Child(Base):
@override.mtd # valid
def base_method(self, incrementor: int) -> None
self.value += incrementor
@property # valid
def base_property(self) -> int:
return self.value + 10
# since no setter, deleter are set,
# it will use parent's setter and deleter.
@override.mtd # invalid, not present in base class.
def child_method(self) -> str:
return str(self.value)
```
4. The `look_in` classmethod of `override` class.
The `override` class checks for methods and properties present in
the child class with respect to the recent parent of the child.
However, this behavior can be changed to check for the topmost
base class (not object).
Example:
```python
override.look_in('topmost') # other values: `recent`
class Top:
def top_method(self) -> None:
pass
@override.cls
class Mid(Top):
@override.mtd # valid
def top_method(self) -> str:
pass
def mid_method(self) -> None:
pass
@override.cls
class Bottom(Mid):
@override.mtd
# invalid, mid_method is present in mid,
# and not in the top most base class that is Top
def mid_method(self) -> str:
pass
@override.mtd # valid
def top_method(self) -> int:
pass
```
To avoid conflicts in most cases, use the default setting,
or go back to the default setting using `override.look_in('recent')`.
2. `Property` class for automating `property` creation.
Usage Warning: `Property` class internally uses `property` to maintain,
create and dispatch property `getter`, `setter` and `deleter`, however,
it does not support static type checking as this an external class.
The `Property` class can be used to quickly and neatly define properties
of a class.
1. By binding an attribute.
The `property` class can be used to create a property that is bound
to an attribute, with easy `setter` and `deleter` creation or blocking.
```python
from modkit.classtools import Property
class Example:
def __init__(self, value: int) -> None:
self._value = value
# note that, this is the only way the Property can be defined,
# it cannot be defined inside a method, or another property.
value = Property(
attribute='_value', # bind to _value,
setter=True, # enable setter,
deleter=True, # enable deleter,
deleter_deletes_attribute=False,
# this prevents the deleter from actually deleting the
# attribute (del self._value). Instead, it sets self._value
# to None.
# However, if deleter_deletes_attribute is set to True,
# the _value attribute will be deleted.
)
```
It also supports blocking `setter` or `deleter` or both.
```python
from modkit.classtools import Property
class Example2:
def __init__(self, value: int) -> None:
self._value = value
value = Property(
attribute='_value', # bind to _value,
setter=False, # block setting it,
deleter=False, # block deletion,
error=AttributeError, # choose any exception to raise,
# any custom exception can also be used.
# as long as it is a child class of Exception or Exception
# itself,
setter_error_arguments=("Cannot set _value",),
# choose setter error arguments for the exception.
deleter_error_arguments-("Cannot delete _value",),
# choose deleter error arguments for the exception.
)
# This property can only be accessed and cannot be set or
# deleted. Trying to do the same will raise AttributeError.
```
2. By explicitly defining `getter`, `setter`, and `deleter`.
```python
from modkit.classtools import Property
class Example3:
def __init__(self, value: int) -> None:
self._value = value
value_is_none = Property(
getter=lambda cls: cls._value is None, # returns bool.
setter=None, # blocking setter, or define it just like getter.
deleter=None, # blocking deleter.
error=ValueError, # set, delete will raise ValueError,
setter_error_arguments=("Read-only property.",),
deleter_error_arguments=("Read-only property.",),
)
# This will create a property named value_is_none which returns
# bool, based on _value.
# using previously defined property.
value_is_not_none = Property(
getter=lambda cls: not cls.value_is_none,
setter=None,
deleter=None,
error=AttributeError,
setter_error_arguments=("Read-only.",),
deleter_error_arguments=("Read-only.",),
)
# creating with setter and deleter.
value = Property(
getter=lambda cls: cls._value,
setter=lambda cls, value: setattr(cls, '_value', value),
deleter=lambda cls: setattr(cls, '_value', None),
)
```
3. The `Possibility` class to extend return types functionality.
Suppose there is a function that returns an `int` and that is it for that, it cannot do anything else.
I wanted to check if the `int` value returned is a positive number
or negative. A normal code will do it like this:
```python
# a function that returns an int
def func() -> int:
return 10
```
```python
# checking positive or negative
>>> func() > 0
True
```
The `Possibility` way:
1. Import the `Possibility` abstract class.
```python
from modkit.classtools import Possibility
```
2. Create a new return type, say, `Extra`.
```python
class Extra(Possibility):
# leave the __init__ method untouched.
# The Possibility class has three properties
# parent, attribute (attribute_name)
# and value (attribute's value).
# __bool__ method needs to be defined (as it is an
# abstract method.), we will do it later.
# let us create a method named, is_positive.
def is_positive(self) -> bool:
return self.value >= 0
# the __bool__ method
def __bool__(self) -> bool:
return self.value is not None
# this implements the `if <class-here>` statement
# so u can set any logic here.
```
3. Modify the Function.
```python
# The __init__ method of Extra will take 3 parameters.
# parent: the parent class.
# attribute: The attribute name
# value: The value.
# for now, we will leave, parent, and attribute.
def func() -> Extra[int]:
return Extra(parent=None, attribute='', value=10)
```
4. Assess the result.
```python
>>> func() # will return Extra
<Extra object at XXXXXXX>
>>> func().is_positive() # if the value is positive.
True
>>> func()() # get the value.
10
```
5. Using in a class.
We will use the `Extra` class we created above.
```python
class Example:
def __init__(self, value: int) -> None:
self._value = value
@property
def value(self) -> Extra[int]:
return Extra(
parent=self,
attribute='_value',
value=self._value
)
```
Let us see the results.
```python
>>> obj = Example(100)
>>> obj.value # simply using this will return the Extra class object
<Extra object at XXXXXX>
>>> obj.value() # even though it is a property, () calling it will return the value
100
# if it was a method, you have to call it twice, like value()().
>>> obj.value.is_positive() # better readability.
True
```
## Contributing
Contributions are welcome, create and issue [here](https://github.com/d33p0st/modkit/issues) and create a pull request [here](https://github.com/d33p0st/modkit/pulls).
Raw data
{
"_id": null,
"home_page": null,
"name": "modkit",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Soumyo Deep Gupta <deep.main.ac@gmail.com>",
"keywords": "d33p0st, modkit, modstore, module, kit",
"author": null,
"author_email": "Soumyo Deep Gupta <deep.main.ac@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/fd/ba/3f480e76dbf1b958953a84dd5cc5d2be76e25f88bc219f48df6592e157d3/modkit-1.3.0.tar.gz",
"platform": null,
"description": "[![PyPI Downloads](https://static.pepy.tech/badge/modkit)](https://pepy.tech/projects/modkit)\n\n# Overview\n\n`modkit` is a collection of tools that can be used for better programming\nexperience and automation.\n\n## Features\n\n- `@override` decorator for python3.8+\n- New `Property` class that automates property definition and takes less number of lines of code to define.\n- New `Possibility` class that helps extend return types with custom methods/properties which in turn helps in better readability of code.\n\n## Usage\n\n1. `@override` decorator and functionality.\n\n The `@override` decorator was introduced in python3.12+ under the `typing` module. However, for python<3.12, it is not available.\n\n The `override` class under `modkit` supports python3.8+ and brings\n additional features rather than just helping in maintaining an error\n free codebase.\n\n The `override` class contains two static methods that can be used as\n decorators: `cls` and `mtd`. The `@override.mtd` acts as a decorator\n for the child class methods which are being over-ridden from the\n parent class. It sets the `__override__` attribute of the methods,\n it has been decorated with, as `True`.\n\n The `@override.cls` decorator on top of a child class, checks for\n all methods that have their `__override__` attribute set as `True`\n and it generates errors accordingly. It will raise\n `MethodOverrideError` if the method set as override is not present in\n the parent class. Additionally, it will also check if the current class\n is a child class or not (inheritance is implemented or not).\n\n Apart from methods, the properties need not be decorated with `mtd`,\n the `@override.cls` over the class will automatically check if the\n property is over-ridden or not. If a over-ridden property does not include a `setter` or `deleter` or both, the parent property's\n `setter` and `deleter` will be copied to the child class.\n\n ### Use case\n\n 1. Import the `override` class from `modkit.typing`\n\n ```python\n from modkit.typing import override\n ```\n 2. Define a Base Class.\n\n ```python\n class Base:\n def __init__(self) -> None:\n self.value = 10\n \n def base_method(self) -> None:\n self.value += 1\n\n @property\n def base_property(self) -> int:\n return self.value\n \n @base_property.setter\n def base_property(self, value) -> None:\n self.value = value\n \n @base_property.deleter\n def base_property(self) -> None:\n self.value = 0\n ```\n 3. Define the Child Class with `override` class.\n\n ```python\n @override.cls # valid\n class Child(Base):\n @override.mtd # valid\n def base_method(self, incrementor: int) -> None\n self.value += incrementor\n \n @property # valid\n def base_property(self) -> int:\n return self.value + 10\n \n # since no setter, deleter are set,\n # it will use parent's setter and deleter.\n\n @override.mtd # invalid, not present in base class.\n def child_method(self) -> str:\n return str(self.value)\n ```\n 4. The `look_in` classmethod of `override` class.\n\n The `override` class checks for methods and properties present in\n the child class with respect to the recent parent of the child.\n However, this behavior can be changed to check for the topmost\n base class (not object).\n\n Example:\n ```python\n override.look_in('topmost') # other values: `recent`\n\n class Top:\n def top_method(self) -> None:\n pass\n \n @override.cls\n class Mid(Top):\n @override.mtd # valid\n def top_method(self) -> str:\n pass\n \n def mid_method(self) -> None:\n pass\n \n @override.cls\n class Bottom(Mid):\n @override.mtd \n # invalid, mid_method is present in mid,\n # and not in the top most base class that is Top\n def mid_method(self) -> str:\n pass\n\n @override.mtd # valid\n def top_method(self) -> int:\n pass\n ```\n\n To avoid conflicts in most cases, use the default setting,\n or go back to the default setting using `override.look_in('recent')`.\n2. `Property` class for automating `property` creation.\n\n Usage Warning: `Property` class internally uses `property` to maintain,\n create and dispatch property `getter`, `setter` and `deleter`, however,\n it does not support static type checking as this an external class.\n\n The `Property` class can be used to quickly and neatly define properties\n of a class.\n\n 1. By binding an attribute.\n\n The `property` class can be used to create a property that is bound\n to an attribute, with easy `setter` and `deleter` creation or blocking.\n\n ```python\n from modkit.classtools import Property\n\n class Example:\n def __init__(self, value: int) -> None:\n self._value = value\n \n # note that, this is the only way the Property can be defined,\n # it cannot be defined inside a method, or another property.\n value = Property(\n attribute='_value', # bind to _value,\n setter=True, # enable setter,\n deleter=True, # enable deleter,\n deleter_deletes_attribute=False,\n # this prevents the deleter from actually deleting the\n # attribute (del self._value). Instead, it sets self._value\n # to None.\n # However, if deleter_deletes_attribute is set to True,\n # the _value attribute will be deleted.\n )\n ```\n\n It also supports blocking `setter` or `deleter` or both.\n\n ```python\n from modkit.classtools import Property\n\n class Example2:\n def __init__(self, value: int) -> None:\n self._value = value\n \n value = Property(\n attribute='_value', # bind to _value,\n setter=False, # block setting it,\n deleter=False, # block deletion,\n error=AttributeError, # choose any exception to raise,\n # any custom exception can also be used.\n # as long as it is a child class of Exception or Exception\n # itself,\n setter_error_arguments=(\"Cannot set _value\",),\n # choose setter error arguments for the exception.\n deleter_error_arguments-(\"Cannot delete _value\",),\n # choose deleter error arguments for the exception.\n )\n\n # This property can only be accessed and cannot be set or\n # deleted. Trying to do the same will raise AttributeError.\n ```\n 2. By explicitly defining `getter`, `setter`, and `deleter`.\n\n ```python\n from modkit.classtools import Property\n\n class Example3:\n def __init__(self, value: int) -> None:\n self._value = value\n \n value_is_none = Property(\n getter=lambda cls: cls._value is None, # returns bool.\n setter=None, # blocking setter, or define it just like getter.\n deleter=None, # blocking deleter.\n error=ValueError, # set, delete will raise ValueError,\n setter_error_arguments=(\"Read-only property.\",),\n deleter_error_arguments=(\"Read-only property.\",),\n )\n\n # This will create a property named value_is_none which returns\n # bool, based on _value.\n\n # using previously defined property.\n value_is_not_none = Property(\n getter=lambda cls: not cls.value_is_none,\n setter=None,\n deleter=None,\n error=AttributeError,\n setter_error_arguments=(\"Read-only.\",),\n deleter_error_arguments=(\"Read-only.\",),\n )\n\n # creating with setter and deleter.\n value = Property(\n getter=lambda cls: cls._value,\n setter=lambda cls, value: setattr(cls, '_value', value),\n deleter=lambda cls: setattr(cls, '_value', None),\n )\n ```\n3. The `Possibility` class to extend return types functionality.\n\n Suppose there is a function that returns an `int` and that is it for that, it cannot do anything else.\n\n I wanted to check if the `int` value returned is a positive number\n or negative. A normal code will do it like this:\n\n ```python\n # a function that returns an int\n def func() -> int:\n return 10\n ```\n\n ```python\n # checking positive or negative\n >>> func() > 0\n True\n ```\n\n The `Possibility` way:\n\n 1. Import the `Possibility` abstract class.\n\n ```python\n from modkit.classtools import Possibility\n ```\n 2. Create a new return type, say, `Extra`.\n\n ```python\n class Extra(Possibility):\n # leave the __init__ method untouched.\n # The Possibility class has three properties\n # parent, attribute (attribute_name) \n # and value (attribute's value).\n\n # __bool__ method needs to be defined (as it is an\n # abstract method.), we will do it later.\n\n # let us create a method named, is_positive.\n def is_positive(self) -> bool:\n return self.value >= 0\n \n # the __bool__ method\n def __bool__(self) -> bool:\n return self.value is not None\n # this implements the `if <class-here>` statement\n # so u can set any logic here.\n ```\n 3. Modify the Function.\n\n ```python\n # The __init__ method of Extra will take 3 parameters.\n # parent: the parent class.\n # attribute: The attribute name\n # value: The value.\n\n # for now, we will leave, parent, and attribute.\n def func() -> Extra[int]:\n return Extra(parent=None, attribute='', value=10)\n ```\n 4. Assess the result.\n\n ```python\n >>> func() # will return Extra\n <Extra object at XXXXXXX>\n\n >>> func().is_positive() # if the value is positive.\n True\n\n >>> func()() # get the value.\n 10\n ```\n 5. Using in a class.\n\n We will use the `Extra` class we created above.\n\n ```python\n class Example:\n def __init__(self, value: int) -> None:\n self._value = value\n \n @property\n def value(self) -> Extra[int]:\n return Extra(\n parent=self,\n attribute='_value',\n value=self._value\n )\n ```\n\n Let us see the results.\n\n ```python\n >>> obj = Example(100)\n >>> obj.value # simply using this will return the Extra class object\n <Extra object at XXXXXX>\n >>> obj.value() # even though it is a property, () calling it will return the value\n 100\n # if it was a method, you have to call it twice, like value()().\n >>> obj.value.is_positive() # better readability.\n True\n ```\n\n## Contributing\n\nContributions are welcome, create and issue [here](https://github.com/d33p0st/modkit/issues) and create a pull request [here](https://github.com/d33p0st/modkit/pulls).\n",
"bugtrack_url": null,
"license": "MIT License Copyright (c) 2025 Soumyo Deep Gupta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
"summary": "A kit containing tools for better programming.",
"version": "1.3.0",
"project_urls": {
"GitHub": "https://github.com/d33p0st/modkit",
"Issues": "https://github.com/d33p0st/modkit/issues",
"Pull-Request": "https://github.com/d33p0st/modkit/pulls"
},
"split_keywords": [
"d33p0st",
" modkit",
" modstore",
" module",
" kit"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "09e68662ae89bd424d805771d56ed83c70d61e592cb90332730e3ec1573c5aaf",
"md5": "f4927e4cccc863e957353c2df85f5cfb",
"sha256": "c197f34d9b9bb50fd4904b52ed58ed7a87e04822a5dc987562e709475875c911"
},
"downloads": -1,
"filename": "modkit-1.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f4927e4cccc863e957353c2df85f5cfb",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 15466,
"upload_time": "2025-01-05T23:48:32",
"upload_time_iso_8601": "2025-01-05T23:48:32.848775Z",
"url": "https://files.pythonhosted.org/packages/09/e6/8662ae89bd424d805771d56ed83c70d61e592cb90332730e3ec1573c5aaf/modkit-1.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "fdba3f480e76dbf1b958953a84dd5cc5d2be76e25f88bc219f48df6592e157d3",
"md5": "e3c835f0bad8e5e9d7c06ebd9b32dceb",
"sha256": "8504f56a1b2f83d83dd00ed44f86eab86ba89d38354bbc71d7d664f7f8b8c00b"
},
"downloads": -1,
"filename": "modkit-1.3.0.tar.gz",
"has_sig": false,
"md5_digest": "e3c835f0bad8e5e9d7c06ebd9b32dceb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 15702,
"upload_time": "2025-01-05T23:48:34",
"upload_time_iso_8601": "2025-01-05T23:48:34.352281Z",
"url": "https://files.pythonhosted.org/packages/fd/ba/3f480e76dbf1b958953a84dd5cc5d2be76e25f88bc219f48df6592e157d3/modkit-1.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-05 23:48:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "d33p0st",
"github_project": "modkit",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "modkit"
}