Skip to content

Commit

Permalink
Add modifiable items attr dict (#21)
Browse files Browse the repository at this point in the history
* changing import to from import statement

* Changing from `black` to `blue`.

* Changing version to `v3.0.0`

* Adding `ModifiableItemsAttrDict`.

* Running `blue`

* Refactoring examples and `README.md` to include details about `ModelsItemsAttrDict`

* Refactoring description and keywords to include `ModifiableItemsAttrDict`.

---------

Co-authored-by: tybruno <tybruno@cisco.com>
  • Loading branch information
tybruno and tybruno authored May 23, 2024
1 parent 2843862 commit cac9c29
Show file tree
Hide file tree
Showing 9 changed files with 546 additions and 236 deletions.
119 changes: 104 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity)
[![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-0000ff.svg)](https://github.com/psf/blue)
[![License: MIT](https://img.shields.io/badge/License-MIT-blueviolet.svg)](https://opensource.org/licenses/MIT)
[![codecov](https://codecov.io/gh/tybruno/modifiable-items-dictionary/branch/main/graph/badge.svg?token=ZO94EJFI3G)](https://codecov.io/gh/tybruno/modifiable-items-dictionary)

# modifiable-items-dict

A simple, fast, typed, and tested implementation for a python3.6+ Modifiable
Items dictionary. `ModifiableItemsDict`
extends `dict` with the ability to modify key's and value's on creation,
insertion, and retrieval.
This class extends and maintains the original functionality of the
builtin `dict`.
A simple, fast, typed, and tested implementation for a python3.6+ Modifiable Items dictionary. `ModifiableItemsDict`
extends `dict` with the ability to modify keys and values on creation, insertion, and retrieval. This class extends and
maintains the original functionality of the builtin `dict`.

In addition to `ModifiableItemsDict`, this implementation also includes `ModifiableItemsAttrDict`,
a class that inherits from `ModifiableItemsDict` and adds attribute-style access to dictionary items.
This means you can access dictionary items as if they were attributes of the object, providing an alternative,
often more readable way to access dictionary items.
Like `ModifiableItemsDict`, `ModifiableItemsAttrDict` also allows modification of keys and values on creation,
insertion, and retrieval, ensuring consistency and flexibility in handling dictionary items.

#### Key Features:

* **Easy**: Flexable and easy to add Key and/or Value modifiers to
the `ModifiableItemsDict`
* **Easy**: Flexible and easy to add Key and/or Value modifiers to
the `ModifiableItemsDict` and `ModifiableItemsAttrDict`.
* **Attribute Access**: `ModifiableItemsAttrDict` allows attribute-style access to dictionary items, providing an
alternative, often more readable way to access dictionary items.
* **Great Developer Experience**: Being fully typed makes it great for editor
support.
* **Fully Tested**: Our test suit fully tests the functionality to ensure
that `ModifiableItemsDict` runs as expected.
* **Fully Tested**: Our test suite fully tests the functionality to ensure
that `ModifiableItemsDict` and `ModifiableItemsAttrDict` run as expected.
* **There is More!!!**:
* [CaselessDict](https://github.com/tybruno/caseless-dictionary): `CaselessDict`
extends `ModifiableItemsDict` which
is a `CaselessDict` which ignores the case of the keys.
extends `ModifiableItemsDict` and `ModifiableItemsAttrDict` which
is a `CaselessDict` and `CaselessAttrDict` that ignores the case of the keys.

## Installation

`pip install modifiable-items-dictionary`

## Simple Example
## ModifiableItemsDict and ModifiableItemsAttrDict

The `ModifiableItemsDict` and `ModifiableItemsAttrDict` classes are part of the `modifiable_items_dictionary` module.
They provide advanced dictionary functionalities with additional features for modifying keys and values.

### ModifiableItemsDict

The `ModifiableItemsDict` class is a dictionary class that allows modification of keys and values during creation,
insertion, and retrieval. It provides the following features:

- **Key Modifiers**: This class allows you to specify functions that modify keys on creation, insertion, and retrieval.
This can be useful for ensuring that all keys adhere to a certain format or standard.
- **Value Modifiers**: Similar to key modifiers, you can also specify functions that modify values. This can be useful
for transforming or sanitizing data as it's added to the dictionary.
- **Thread Safety**: This class is designed to be thread-safe, making it suitable for use in multi-threaded
applications.
- **Fully Tested**: The functionality of this class is fully tested to ensure reliable operation.

#### Example

```python
from modifiable_items_dictionary import ModifiableItemsDict
Expand Down Expand Up @@ -73,7 +97,72 @@ modifiable_items_dictionary["HeLLO"] = 5
print(modifiable_items_dictionary) # {'camelcase': 4, 'hello': 6}
```

## Example
### ModifiableItemsAttrDict

The `ModifiableItemsAttrDict` class is an extension of the `ModifiableItemsDict` class, inheriting all its features
while adding attribute-style access to dictionary items. This class maintains the same key and value modifiers, ensuring
consistency and flexibility in handling dictionary items.

Like `ModifiableItemsDict`, `ModifiableItemsAttrDict` is designed with thread safety in mind, making it suitable for use
in multi-threaded applications. It is also fully tested to ensure reliable operation.

In addition to these features, `ModifiableItemsAttrDict` offers:

- **Attribute-style Access**: This feature allows you to access dictionary items as if they were attributes of the
object. For example, you can use `dict.key` instead of `dict['key']`. This provides an alternative, often more
readable way to access dictionary items, while still retaining the traditional dictionary access methods.

This combination of features makes `ModifiableItemsAttrDict` a powerful tool for managing dictionaries in a way that is
both flexible and intuitive, while maintaining the robustness and reliability of the `ModifiableItemsDict` class.

#### Example

```python
import modifiable_items_dictionary


def _add_1(_value):
if isinstance(_value, int):
_value += 1
return _value


def _case_fold_string(_value):
if isinstance(_value, str):
_value = _value.casefold()
return _value


modifiable_items_dictionary.ModifiableItemsAttrDict._key_modifiers = [
str.casefold
]
modifiable_items_dictionary.ModifiableItemsAttrDict._value_modifiers = (
_add_1,
_case_fold_string,
)

modifiable_items_attr_dict = (
modifiable_items_dictionary.ModifiableItemsAttrDict(
{'lower': 1, 'UPPER': 2}, CamelCase=3, snake_case='FoUR'
)
)

print(
modifiable_items_attr_dict
) # {'lower': 2, 'upper': 3, 'camelcase': 4, 'snake_case': 'four'}

# Key-based access
del modifiable_items_attr_dict['LOWER']
modifiable_items_attr_dict['HeLLO'] = 5

# Attribute-based access
del modifiable_items_attr_dict.UPPER
modifiable_items_attr_dict.HeLLO = 6
print(modifiable_items_attr_dict.hELlo) # 6
print(modifiable_items_attr_dict) # {'camelcase': 4, 'hello': 6}
```

## Use Case Example

Let's say that there is a `.json` file that has Url hosts and their IP address.
Our Goal is to load the json data into a dictionary like structure that will
Expand Down
118 changes: 94 additions & 24 deletions examples/examples.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
"""This module contains examples of how to use the
`modifiable_items_dictionary` module.
The `modifiable_items_dictionary` module provides two classes:
- `ModifiableItemsDict`: A dictionary class that can modify keys and
values at runtime.
- `ModifiableItemsAttrDict`: A dictionary class that allows
attribute-style access to its items.
This module contains examples of how to use these classes.
"""
import ipaddress
import multiprocessing.pool
import string
Expand All @@ -19,30 +30,79 @@ def _case_fold_string(_value):
_value = _value.casefold()
return _value

modifiable_items_dictionary.ModifiableItemsDict._key_modifiers = [str.casefold]
modifiable_items_dictionary.ModifiableItemsDict._value_modifiers = (_add_1, _case_fold_string)
modifiable_items_dictionary.ModifiableItemsDict._key_modifiers = [
str.casefold
]
modifiable_items_dictionary.ModifiableItemsDict._value_modifiers = (
_add_1,
_case_fold_string,
)
# Or
# modifiable_items_dict.ModifiableItemsDict._key_modifiers = staticmethod(_lower)
# modifiable_items_dict.ModifiableItemsDict._value_modifiers = [_lower, _add_1]

modifiable_items_dict = modifiable_items_dictionary.ModifiableItemsDict({"lower": 1, "UPPER": 2}, CamelCase=3,
snake_case="FoUR")
modifiable_items_dict = modifiable_items_dictionary.ModifiableItemsDict(
{'lower': 1, 'UPPER': 2}, CamelCase=3, snake_case='FoUR'
)

print(modifiable_items_dict) # {'lower': 2, 'upper': 3, 'camelcase': 4, 'snake_case': 'four'}
print(
modifiable_items_dict
) # {'lower': 2, 'upper': 3, 'camelcase': 4, 'snake_case': 'four'}

del modifiable_items_dictionary["LOWER"]
del modifiable_items_dictionary["UPPER"]
modifiable_items_dict.pop("SNAKE_CAse")
del modifiable_items_dictionary['LOWER']
del modifiable_items_dictionary['UPPER']
modifiable_items_dict.pop('SNAKE_CAse')

modifiable_items_dict["HeLLO"] = 5
modifiable_items_dict['HeLLO'] = 5

print(modifiable_items_dict) # {'camelcase': 4, 'hello': 6}


def simple_inheritance_example():
"""This example shows how to create a Host Dictionary that will casefold keys, strip keys, and convert values to ip addresses
def modifiable_items_attr_dict_example():
"""This example shows how to use `ModifiableItemsAttrDict`."""
import modifiable_items_dictionary

"""
def _add_1(_value):
if isinstance(_value, int):
_value += 1
return _value

def _case_fold_string(_value):
if isinstance(_value, str):
_value = _value.casefold()
return _value

modifiable_items_dictionary.ModifiableItemsAttrDict._key_modifiers = [
str.casefold
]
modifiable_items_dictionary.ModifiableItemsAttrDict._value_modifiers = (
_add_1,
_case_fold_string,
)

modifiable_items_attr_dict = (
modifiable_items_dictionary.ModifiableItemsAttrDict(
{'lower': 1, 'UPPER': 2}, CamelCase=3, snake_case='FoUR'
)
)

print(
modifiable_items_attr_dict
) # {'lower': 2, 'upper': 3, 'camelcase': 4, 'snake_case': 'four'}

# Key-based access
del modifiable_items_attr_dict['LOWER']
modifiable_items_attr_dict['HeLLO'] = 5

# Attribute-based access
del modifiable_items_attr_dict.UPPER
modifiable_items_attr_dict.HeLLO = 6
print(modifiable_items_attr_dict.hELlo) # 6
print(modifiable_items_attr_dict) # {'camelcase': 4, 'hello': 6}


def simple_inheritance_example():
"""This example shows how to create a Host Dictionary that will casefold keys, strip keys, and convert values to ip addresses"""

# Inherit from `ModifiableItemsDict` and set the `_key_modifiers` and `_value_modifiers` class variables.
class HostDict(modifiable_items_dictionary.ModifiableItemsDict):
Expand All @@ -51,18 +111,24 @@ class HostDict(modifiable_items_dictionary.ModifiableItemsDict):
# Or
# _value_modifiers = @staticmethod(ipaddress.ip_address)

browsers = HostDict({" GooGle.com ": "142.250.69.206", " duckDUCKGo.cOM ": "52.250.42.157"})
browsers = HostDict(
{
' GooGle.com ': '142.250.69.206',
' duckDUCKGo.cOM ': '52.250.42.157',
}
)

print(browsers) # {'google.com': IPv4Address('142.250.69.206'), 'duckduckgo.com': IPv4Address('52.250.42.157')}
print(
browsers
) # {'google.com': IPv4Address('142.250.69.206'), 'duckduckgo.com': IPv4Address('52.250.42.157')}

_old_browser = browsers.pop(" gOOgle.com ")
_old_browser = browsers.pop(' gOOgle.com ')
# or
# del host_dict[" GooGle.com "]

browsers[" BrAvE.com "] = "2600:9000:234c:5a00:6:d0d2:780:93a1"
browsers[' BrAvE.com '] = '2600:9000:234c:5a00:6:d0d2:780:93a1'

print(
browsers)
print(browsers)
# {'duckduckgo.com': IPv4Address('52.250.42.157'), 'brave.com': IPv6Address('2600:9000:234c:5a00:6:d0d2:780:93a1')}


Expand All @@ -72,10 +138,12 @@ def threading_example():
pool = multiprocessing.pool.ThreadPool(10)

def _slow_function(x):
time.sleep(.05)
time.sleep(0.05)
return x

class TimeDictWithThreading(modifiable_items_dictionary.ModifiableItemsDict):
class TimeDictWithThreading(
modifiable_items_dictionary.ModifiableItemsDict
):
_key_modifiers = (_slow_function,)
_value_modifiers = (_slow_function,)
_map_function = pool.imap_unordered
Expand All @@ -86,19 +154,21 @@ class TimeDict(modifiable_items_dictionary.ModifiableItemsDict):
_key_modifiers = (_slow_function,)
_value_modifiers = (_slow_function,)

iterable = {_letter: _index for _index, _letter in enumerate(string.ascii_letters)}
iterable = {
_letter: _index for _index, _letter in enumerate(string.ascii_letters)
}

# Without Threading
start = time.perf_counter()
TimeDict(iterable)
end = time.perf_counter()
print(f"{end - start:.2f} seconds") # 5.54 seconds
print(f'{end - start:.2f} seconds') # 5.54 seconds

# With Threading
start = time.perf_counter()
TimeDictWithThreading(iterable)
end = time.perf_counter()
print(f"{end - start:.2f} seconds") # 0.64 seconds
print(f'{end - start:.2f} seconds') # 0.64 seconds


def main():
Expand All @@ -107,5 +177,5 @@ def main():
threading_example()


if __name__ == "__main__":
if __name__ == '__main__':
main()
9 changes: 7 additions & 2 deletions modifiable_items_dictionary/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from modifiable_items_dictionary.modifiable_items_dictionary import ModifiableItemsDict
from modifiable_items_dictionary.modifiable_items_dictionary import (
ModifiableItemsDict,
)
from modifiable_items_dictionary.modifiable_items_attribute_dictionary import (
ModifiableItemsAttrDict,
)

__all__ = (ModifiableItemsDict.__name__,)
__all__ = (ModifiableItemsDict.__name__, ModifiableItemsAttrDict.__name__)
Loading

0 comments on commit cac9c29

Please sign in to comment.