GithubHelp home page GithubHelp logo

metaist / attrbox Goto Github PK

View Code? Open in Web Editor NEW
1.0 1.0 0.0 756 KB

Attribute-based data structures

Home Page: https://metaist.github.io/attrbox

License: MIT License

Python 100.00%
python attribute-based dict list jsend

attrbox's Introduction

attrbox: attribute-based data structures

Otto the Otter
Otto the Otter

Build PyPI Supported Python Versions

Why?

I have common use cases where I want to improve python's dict and list:

  • AttrDict: attribute-based dict with better merge and deep value access
  • AttrList: list that broadcasts operations to its members
  • Environment: reading environment files
  • Configuration: loading command-line arguments and configuration files
  • JSend: sending JSON responses

Install

python -m pip install attrbox

AttrDict

AttrDict features:

  • Attribute Syntax for dict similar to accessing properties in JavaScript: thing.prop means thing["prop"] for get / set / delete.

  • No KeyError: if a key is missing, just return None (like dict.get()).

  • Deep Indexing: use a list of keys and int to get and set deeply nested values. This is similar to lodash.get except that only the array-like syntax is supported and you must use actual int to index across list objects.

  • Deep Merge: combine two dict objects by extending deeply-nested keys where possible. This is different than the new dict union operator (PEP 584).

from attrbox import AttrDict

items = AttrDict(a=1, b=[{"c": {"d": 5}}], e={"f": {"g": 7}})
items.a
# => 1
items.x is None
# => True
items.x = 10
items['x']
# => 10
items.get(["b", 0, "c", "d"])
# => 5
items <<= {"e": {"f": {"g": 20, "h": [30, 40]}}}
items.e.f.g
# => 20
items[['e', 'f', 'h', 1]]
# => 40

Read more about AttrDict

AttrList

AttrList provides member broadcast: performing operations on the list performs the operation on all the items in the list. I typically use this to achieve the Composite design pattern.

from attrbox import AttrDict, AttrList

numbers = AttrList([complex(1, 2), complex(3, 4), complex(5, 6)])
numbers.real
# => [1.0, 3.0, 5.0]

words = AttrList(["Apple", "Bat", "Cat"])
words.lower()
# => ['apple', 'bat', 'cat']

items = AttrList([AttrDict(a=1, b=2), AttrDict(a=5)])
items.a
# => [1, 5]
items.b
# => [2, None]

Read more about AttrList

Environment

attrbox.env is similar to python-dotenv, but uses the AttrDict ability to do deep indexing to allow for things like dotted variable names. Typically, you'll use it by calling attrbox.load_env() which will find the nearest .env file and load it into os.environ.

Read more about attrbox.env

Configuration

attrbox supports loading configuration files from .json, .toml, and .env files. By default, load_config() looks for a key imports and will recursively import those files (relative to the current file) before loading the rest of the current file (data is merged using AttrDict). This allows you to create templates or smaller configurations that build up to a larger configuration.

For CLI applications, attrbox.parse_docopt() let's you use the power of docopt with the flexibility of AttrDict. By default, --config and <config> arguments will load the file using the load_config()

"""Usage: prog.py [--help] [--version] [-c CONFIG] --file FILE

Options:
  --help                show this message and exit
  --version             show the version number and exit
  -c, --config CONFIG   load this configuration file (supported: .toml, .json, .env)
  --file FILE           the file to process
"""

def main():
    args = parse_docopt(__doc__, version=__version__)
    args.file # has the value of --file

if __name__ == "__main__":
    main()

Building on top of docopt we strip off leading dashes and convert them to underscores so that we can access the arguments as AttrDict attributes.

Read more about attrbox.config

JSend

JSend is an approximate implementation of the JSend specification that makes it easy to create standard JSON responses. The main difference is that I added an ok attribute to make it easy to tell if there was a problem (fail or error).

from attrbox import JSend

def some_function(arg1):
    result = JSend() # default is "success"

    if not is_good(arg1):
        # fail = controlled problem
        return result.fail(message="You gone messed up.")

    try:
        result.success(data=process(arg1))
    except Exception:
        # error = uncontrolled problem
        return result.error(message="We have a problem.")

    return result

Because the JSend object is an AttrDict, it acts like a dict in every other respect (e.g., it is JSON-serializable).

Read more about JSend

License

MIT License

attrbox's People

Contributors

metaist avatar

Stargazers

 avatar

Watchers

 avatar

attrbox's Issues

update: LTS

  • .github/workflows/ci.yaml
  • .github/workflows/codeql.yml
  • .github/workflows/pypi.yaml
  • .github/FUNDING.yml
  • docs
  • .cspell.json
  • .editorconfig
  • .gitattributes
  • .gitignore
  • CHANGELOG.md
  • CONTRIBUTING.md
  • LICENSE.md
  • pyproject.toml
  • README.md
  • mascot

fix: load order for config

Currently, config values are loaded before CLI arguments (argv), which is correct. Except that CLI arguments also include default values. Which means that default values are superseding config values.

The order I'd like (later values winning):

  • default values
  • config values
  • CLI arguments

But this implies that we need to know which of the docopt values are default and which happen to have the same value as the default.

clean: top-level __init__.py

I only want the most-used classes and functions at the top-most level. Everything else can be accessed more directly, if needed, but I don't want to pollute the namespace.

add: functional API wrapper

@Pugio has often talked about having a nice functional wrapper (ala ruby) in a python context. My first attempt at this failed because I was trying to stick to the existing type system. Let's see if we can do better by just making a wrapper object.

constructors

  • __init__
  • copy
  • wrap
  • fromkeys

contents

  • __repr__
  • __len__
  • __contains__
  • __iter__
  • __eq__
  • keys
  • values
  • items
  • attr
  • count
  • get

select

  • first
  • last
  • take
  • drop
  • uniq
  • filter
  • index

apply

  • map
  • reduce
  • each
  • group

format

  • sort
  • reverse
  • compact
  • flatten
  • zip
  • join

add

  • push
  • append
  • concat
  • extend
  • insert
  • setdefault
  • __add__
  • __iadd__

remove

  • pop
  • popitem
  • delete
  • remove
  • clear

modify

  • update
  • __imul__
  • __rmul__

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.