Metadata-Version: 2.1
Name: safitty
Version: 0.5.6
Summary: Safitty. Wrapper on JSON/YAML configs for Python.
Home-page: https://github.com/TezRomacH/safitty
Author: Roman Tezikov
Author-email: tez.romach@gmail.com
License: MIT
Description: 
        # Safitty
        [![Pipi version](https://img.shields.io/pypi/v/safitty.svg?colorB=blue)](https://pypi.org/project/safitty/)
        [![License](https://img.shields.io/github/license/TezRomacH/safitty.svg)](LICENSE)
        
        Safitty is a wrapper on JSON/YAML configs for Python.
        Designed with a focus on the security of `get`/`set` operations for deep-nested dictionaries and lists.
        
        ## Installation
        ```bash
        pip install safitty
        ```
        
        ## Features
        - Safe `get` for dictionaries and lists
        - Nested keys at one time
        - Several strategies 
        
        ## Usage
        
        Suppose you have a configuration file looks like:
        
        ```yaml
        verbose: false
        
        status: 1
        
        paths:
          jsons:
            - first
            - second
            - third
          images: important/path/to/images/
          
        transforms:
          - name: Normalize
            function: ToTensor
            params: null
          -
            name: Padding
            function: Pad
            params:
              fill: 3
              padding_mode: reflect
        
        reader:
          need_reader: True
        
          params:
            width: 512
            height: 521
            grayscale: True
        ```
        Maybe it's located at your project folder or you receive it from a server.
        For now we want to load the config and get some of the nested keys 
        ```python
        import safitty as sf
        import yaml
        
        
        with open("path/to/config.yml") as stream:
            config = yaml.load(stream)
            
        param = sf.safe_get(config, 'paths', 'images')
        # param == 'important/path/to/images/'
        ```
        
        Python's dict will throw an error if we try to get a non-existing path. Or it will turn into a disgusting construction.
        ```python
        # usual way to work with dict
        
        >>> config['transforms'][1]['param']['fill'] # oops, we wrote `param` insted of `params
        KeyError: 'param'
        
        >>> config.get('transforms')[1].get('param').get('fill') # What if we go deeeeeeper
        AttributeError: 'NoneType' object has no attribute 'get'
        
        >>> config.get('transforms')[1].get('params').get('fill') # it works, but looks awful
        3
        ``` 
        
        Anyway this last way doesn't help us with lists. Standard Python library doesn't provide safe `get` method for type `list` 
        ```python
        >>> config.get('transforms')[2].get('params').get('fill') # wrong index
        IndexError: list index out of range
        ```
        
        Solution here is to use Saffity:
        ```python
        >>> sf.safe_get(config, 'transforms', 2, 'params', 'fill')
        # None
        
        >>> sf.safe_get(config, 'transforms', 1, 'params', 'fill')
        3
        
        >>> sf.safe_get(config, 'transforms', 2, 'values', 'fill', default=42)
        42
        ```
        
        With Safitty you'll not be scared about nested depth
        ```python
        >>> sf.safe_get(config, 'transforms', 1, 'params', 'fill', 'what', 'if', 'we', 'go', 'deeper')
        # None
        
        >>> sf.safe_get(config, 'transforms', 3, "wrong_key", 55, 'params', 'go', 'deeper', default="This is default value")
        This is default value
        ```
        
        Safitty provides several strategies for using the `default` param.
        ```python
        >>> sf.safe_get(config, 'transforms', 0, 'values', strategy='missing_key', default=42)
        42
        
        >>> sf.safe_get(config 'transforms', 0, 'values', strategy='last_value', default=42)
        {"name": "Normalize", "function": "ToTensor", "params": None}
        ```
        1. For strategy `final`, it returns `default` if the final result on applying keys is None. Safitty uses it by default
        1. For strategy `missing_key`, it returns `default` only if some key in keys doesn't exist in config.
        1. Strategy `last_value` returns last non null value if exists
        1. Strategy `last_container` returns last non null container if exists
        
        Note the difference for `final` and `missing_key` strategies.
        ```python
        >>> sf.safe_get(config, 'transforms', 0, 'params', default='No value')
        No value
        
        >>> sf.safe_get(config, 'transforms', 0, 'params', strategy='missing_key', default='No value')
        # return None, because there were not not-existing keys
        
        # But if we make a mistake in keys 
        >>> sf.safe_get(config, 'transforms', 23, 'params', strategy='missing_key', default='No value')
        No value
        
        # or
        >>> sf.safe_get(config, 'transforms', 0, 'values', strategy='missing_key', default='No value')
        No value
        
        ```
        
        Saffity provides `transform` params. If it's not `None`, the result will be applied to this type or function.
        
        ```python
        >>> from pathlib import Path
        >>> sf.safe_get(config, 'paths', 'images', transform=Path)
        PosixPath('important/path/to/images/')
        
        ```
        
        ```python
        >>> sf.safe_get(config, 'status', transform=bool)
        True
        
        ```
        
        Lambdas are also supported
        
        ```python
        NOT_FOUND = 404
        >>> sf.safe_get(config, 'status', transform=lambda x: x != NOT_FOUND)
        
        True
        
        ```
        
        With `apply` you can easily unpack values from config to your class.
        As `transform` the parameter applies the result but unpacks it (pass `*result` to a function/type  if `result` is a list or `**result` if `result` is a dict)
        
        ```python
        
        transform = sf.safe_get(config, 'transforms', 0, apply=YourAmazingClass)
        
        # same for longer version
        
        transform = sf.safe_get(config, 'transforms', 0, transform= lambda param: YourAmazingClass(**param))
        
        ```
        
        More advanced
        ```python
        import safitty as sf
        
        augmentations = []
        
        for transform in sf.safe_get(config, 'transforms', default=[]):
            function = ALL_FUNCTIONS[sf.safe_get(transform, 'name', default="Identity")]
            
            aug = sf.safe_get(transform, 'params', default={}, apply=function)
            
            augmentations.append(aug)
        ```
        
        
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Requires-Python: >=3.6.0
Description-Content-Type: text/markdown
