Package attrbox
Attribute-based data structures.
Why?
I have common use cases where I want to improve python's dict
and list
:
AttrDict
: attribute-baseddict
with better merge and deep value accessAttrList
: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
meansthing["prop"]
for get / set / delete. -
No
KeyError
: if a key is missing, just returnNone
(likedict.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 actualint
to index acrosslist
objects. -
Deep Merge: combine two
dict
objects by extending deeply-nested keys where possible. This is different than the newdict
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
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]
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 load_env()
which will find the nearest .env
file and load it into os.environ
.
Configuration
attrbox
supports loading configuration files from .json
, .toml
, and .attrbox.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, 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).
License
Expand source code
"""Attribute-based data structures.
.. include:: ../../README.md
:start-line: 2
"""
# pkg
from .attrdict import __pdoc__ as doc1
from .attrdict import AttrDict
from .attrlist import __pdoc__ as doc2
from .attrlist import AttrList
from .jsend import JSend
from .env import load_env
from .config import load_config
from .config import parse_docopt
__version__ = "0.1.5"
__all__ = (
"__version__",
"AttrDict",
"AttrList",
"JSend",
"load_env",
"load_config",
"parse_docopt",
)
# Update pdoc at all levels.
__pdoc__ = {}
for doc in [doc1, doc2]:
__pdoc__.update(doc)
Sub-modules
attrbox.attrdict
-
dict
with attribute access, deep selectors, and merging. attrbox.attrlist
-
list
that broadcasts attribute access to its elements. attrbox.config
-
Configuration loading and parsing.
attrbox.env
-
Load configuration from environment files …
attrbox.fn
-
Generally useful functions.
attrbox.jsend
-
Standard JSON responses.
Functions
def load_env(path: Union[pathlib.Path, str, ForwardRef(None)] = None) ‑> Dict[str, str]
-
Load an environment file.
We recursively search for a
.attrbox.env
file from the path given or the current working directory, if omitted.Args
path
:PathStr
, optional- starting path. If
None
, start from the current working directory. Defaults toNone
.
Raises
FileNotFoundError
- If not
.attrbox.env
file is found.
Returns
Dict[str, str]
- configuration values
Examples
>>> load_env() # our .env doesn't have any values {}
If no
.attrbox.env
can be found, aFileNotFoundError
is raised:>>> load_env("/") Traceback (most recent call last): ... FileNotFoundError: Cannot find .env file to load.
Expand source code
def load_env(path: Optional[PathStr] = None) -> Dict[str, str]: """Load an environment file. We recursively search for a `.env` file from the path given or the current working directory, if omitted. Args: path (PathStr, optional): starting path. If `None`, start from the current working directory. Defaults to `None`. Raises: FileNotFoundError: If not `.env` file is found. Returns: Dict[str, str]: configuration values Examples: >>> load_env() # our .env doesn't have any values {} If no `.env` can be found, a `FileNotFoundError` is raised: >>> load_env("/") Traceback (most recent call last): ... FileNotFoundError: Cannot find .env file to load. """ path = find_env(path) if not path or not path.exists(): raise FileNotFoundError("Cannot find .env file to load.") return loads(path.read_text(encoding="utf-8"), update_env=True, dotted_keys=True)
def load_config(path: pathlib.Path, /, *, load_imports: bool = True, loaders: Optional[Mapping[str, Callable[[str], Any]]] = None, done: Optional[List[pathlib.Path]] = None) ‑> Dict[str, Any]
-
Load a configuration file from
path
using configurationloaders
.Args
path
:Path
- file to load.
load_imports
:bool
, optional- If
True
, recursively load any files located at theimports
key. Defaults toTrue
. loaders
:Mapping[str, LoaderFunc]
, optional- mapping of file suffixes
to to loader functions. If
None
, uses the globalLOADERS
. Defaults toNone
. done
:List[Path]
, optional- If provided, a list of paths to ignore when
doing recursive loading. Defaults to
None
.
Returns
Dict[str, Any]
- keys/values from the configuration file
Examples
>>> root = Path(__file__).parent.parent.parent >>> expected = {'section': {'key': 'value1', "env": "loaded", ... "json": "loaded", "toml": "loaded"}} >>> load_config(root / "test/config_1.toml") == expected True
Expand source code
def load_config( path: Path, /, *, load_imports: bool = True, loaders: Optional[Mapping[str, LoaderFunc]] = None, done: Optional[List[Path]] = None, ) -> Dict[str, Any]: """Load a configuration file from `path` using configuration `loaders`. Args: path (Path): file to load. load_imports (bool, optional): If `True`, recursively load any files located at the `imports` key. Defaults to `True`. loaders (Mapping[str, LoaderFunc], optional): mapping of file suffixes to to loader functions. If `None`, uses the global `LOADERS`. Defaults to `None`. done (List[Path], optional): If provided, a list of paths to ignore when doing recursive loading. Defaults to `None`. Returns: Dict[str, Any]: keys/values from the configuration file Examples: >>> root = Path(__file__).parent.parent.parent >>> expected = {'section': {'key': 'value1', "env": "loaded", ... "json": "loaded", "toml": "loaded"}} >>> load_config(root / "test/config_1.toml") == expected True """ result = AttrDict() path = path.resolve() done = done or [] loader = (loaders or LOADERS)[path.suffix] data = loader(path.read_text()) if load_imports and "imports" in data: imports = [(path.parent / p).resolve() for p in data.pop("imports")] for file in imports: if file in done: continue result <<= load_config( file, load_imports=True, loaders=loaders, done=done + imports, ) result <<= data return result
def parse_docopt(doc: str, /, argv: Optional[Sequence[str]] = None, *, version: str = '1.0.0', options_first: bool = False, read_config: bool = True) ‑> AttrDict
-
Parse docopt args and load config.
Args
doc
:str
- docstring with description of command
argv
:Sequence[str]
, optional- arguments to parse against the
doc. If
None
, will default tosys.argv[1:]
. Defaults toNone
. version
:str
, optional- program version. Defaults to
"1.0.0"
. options_first
:bool
- If
True
, options must come before positional arguments. Defaults toFalse
. read_config
:bool
- If
True
, a<config>
argument or--config
option will be automatically loaded before args are parsed. Defaults toTrue
.
Returns
AttrDict[str, Any]
- mapping of options to values
Examples
>>> usage = "Usage: test.py [--debug]" >>> parse_docopt(usage, argv=["--debug"]) {'debug': True}
>>> root = Path(__file__).parent.parent.parent >>> path = str(root / "test/config_1.toml") >>> usage = "Usage: test.py <config> [--section.key=VAL]" >>> argv = [path, "--section.key=overwrite"] >>> expected = {"section": { ... "key": "overwrite", # overwritten by argv ... "env": "loaded", "json": "loaded", "toml": "loaded" ... }, "config": path} >>> parse_docopt(usage, argv=argv) == expected True
Expand source code
def parse_docopt( doc: str, /, argv: Optional[Sequence[str]] = None, *, version: str = "1.0.0", options_first: bool = False, read_config: bool = True, ) -> AttrDict: """Parse docopt args and load config. Args: doc (str): docstring with description of command argv (Sequence[str], optional): arguments to parse against the doc. If `None`, will default to `sys.argv[1:]`. Defaults to `None`. version (str, optional): program version. Defaults to `"1.0.0"`. options_first (bool): If `True`, options must come before positional arguments. Defaults to `False`. read_config (bool): If `True`, a `<config>` argument or `--config` option will be automatically loaded before args are parsed. Defaults to `True`. Returns: AttrDict[str, Any]: mapping of options to values Examples: >>> usage = "Usage: test.py [--debug]" >>> parse_docopt(usage, argv=["--debug"]) {'debug': True} >>> root = Path(__file__).parent.parent.parent >>> path = str(root / "test/config_1.toml") >>> usage = "Usage: test.py <config> [--section.key=VAL]" >>> argv = [path, "--section.key=overwrite"] >>> expected = {"section": { ... "key": "overwrite", # overwritten by argv ... "env": "loaded", "json": "loaded", "toml": "loaded" ... }, "config": path} >>> parse_docopt(usage, argv=argv) == expected True """ result = AttrDict() args = { optvar(k, shadow_builtins=True): v for k, v in docopt( cleandoc(doc), argv=argv, help=True, version=version, options_first=options_first, ).items() } if read_config and "config" in args: result <<= load_config(Path(args["config"])) for key, val in args.items(): key = optvar(key, shadow_builtins=True) result[key.split(".")] = val return result
Classes
class AttrDict (*args, **kwargs)
-
Like a
dict
, but with attribute syntax.NOTE: We do not throw
AttributeError
orKeyError
because accessing a non-existent key or attribute returnsNone
.Examples
>>> AttrDict({"a": 1}).a 1
Expand source code
class AttrDict(Dict[str, Any]): """Like a `dict`, but with attribute syntax. NOTE: We do not throw `AttributeError` or `KeyError` because accessing a non-existent key or attribute returns `None`. Examples: >>> AttrDict({"a": 1}).a 1 """ def copy(self: Self) -> Self: """Return a shallow copy. Examples: >>> items = AttrDict(a=1) >>> clone = items.copy() >>> items == clone # same contents True >>> items is not clone # different pointers True >>> clone.a = 5 >>> items != clone True """ return self.__class__(super().copy()) def __contains__(self, key: Any) -> bool: """Return `True` if `key` is a key. Args: key (Any): typically a `AnyIndex`. If it's a string, the check proceeds as usual. If it's a `Sequence`, the checks are performed using `.get()`. Returns: bool: `True` if the `key` is a valid key, `False` otherwise. Examples: Normal checking works as expected: >>> items = AttrDict(a=1, b=2) >>> 'a' in items True >>> 'x' in items False Nested checks are also possible: >>> items = AttrDict(a=[{"b": 2}, {"b": 3}]) >>> ['a', 0, 'b'] in items True >>> ['a', 1, 'x'] in items False """ return self.get(key, NOT_FOUND) is not NOT_FOUND def __getattr__(self, name: str) -> Optional[Any]: """Return the value of the attribute or `None`. Args: name (str): attribute name Returns: Any: attribute value or `None` if the attribute is missing Examples: Typically, attributes are the same as key/values: >>> item = AttrDict(a=1) >>> item.a 1 >>> item['a'] 1 However, instance attributes supersede key/values: >>> item = AttrDict(b=1) >>> object.__setattr__(item, 'b', 2) >>> item.b 2 >>> item['b'] 1 Missing attributes return `None`: >>> item = AttrDict(a=1) >>> item.b is None True """ # NOTE: This method is only called when the attribute cannot be found. return self[name] def __setattr__(self, name: str, value: Any) -> None: """Set the value of an attribute. Args: name (str): attribute name value (Any): value to set Examples: Setting an attribute is usually the same as a key/value: >>> item = AttrDict() >>> item.a = 1 >>> item.a 1 >>> item['a'] 1 However, instance attributes supersede key/values: >>> item = AttrDict() >>> object.__setattr__(item, 'b', 2) >>> item.b 2 >>> item.b = 3 # instance attribute updated >>> item.b 3 >>> item['b'] is None True """ try: super().__getattribute__(name) # is real? super().__setattr__(name, value) except AttributeError: # use key/value self[name] = value def __delattr__(self, name: str) -> None: """Delete an attribute. Args: name (str): attribute name Examples: Deleting an attribute usually deletes the underlying key/value: >>> item = AttrDict(a=1) >>> del item.a >>> item {} However, instance attributes supersede key/values: >>> item = AttrDict(b=1) >>> object.__setattr__(item, 'b', 2) >>> item.b # real attribute supersedes key/value 2 >>> del item.b # deletes real attribute >>> object.__getattribute__(item, 'b') # instance attribute gone Traceback (most recent call last): ... AttributeError: 'AttrDict' object has no attribute 'b' >>> item {'b': 1} """ try: super().__getattribute__(name) # is real? super().__delattr__(name) except AttributeError: # use key/value del self[name] def __getitem__(self, key: AnyIndex) -> Optional[Any]: """Return the value of the key. Args: key (AnyIndex): key name or Sequence of the path to a key Returns: Any: value of the key or `None` if it cannot be found Examples: >>> item = AttrDict(a=1) >>> item['a'] 1 >>> item['b'] is None True """ return self.get(key) def __setitem__(self, key: AnyIndex, value: Any) -> None: """Set the value of a key. Args: key (AnyIndex): key name value (Any): key value Examples: >>> item = AttrDict(a=1) >>> item['a'] = 5 >>> item['a'] 5 >>> item[['a', 'b']] = 10 >>> item.a.b 10 """ self.set(key, value) def __delitem__(self, key: str) -> None: """Delete a key. Args: key (str): key name Examples: >>> item = AttrDict(a=1) >>> del item['a'] >>> item {} Deleting missing keys has no effect: >>> item = AttrDict(a=1) >>> del item['b'] >>> item {'a': 1} """ try: super().__delitem__(key) except KeyError: pass def get(self, path: AnyIndex, default: Optional[Any] = None, /) -> Optional[Any]: """Return the value at `path` or `default` if it cannot be found. Args: path (AnyIndex): path to the value default (Any, optional): value to return if `name` is not found. Defaults to `None`. Returns: Optional[Any]: key value or `default` if the key is not found Examples: Normal `.get` functions work: >>> items = AttrDict(a=dict(b=[{"c": 3}, {"c": -10}]), d=4) >>> items.get('d') == 4 True >>> items.get('e') is None True >>> items.get('e', 5) == 5 True But you can also indexing into nested `list` and `dict`: >>> items.get(['a', 'b', 1, 'c']) == -10 True Bad indexing will just return the default: >>> items.get(['e']) is None # key doesn't exist True >>> items.get(['a', 'b', 5]) is None # index unreachable True >>> items.get(['d', 'e']) is None # int is not indexable True """ if isinstance(path, str): return super().get(path, default) return get_path(self, path, default) def set(self: Self, path: AnyIndex, value: Optional[Any] = None, /) -> Self: """Set key at `path` to `value`. Args: path (AnyIndex): path to the key to set. value (Any, optional): value to set. Defaults to `None`. Returns: Self: for chaining Examples: >>> items = AttrDict({"a": 1}) >>> items.set("b", 2) {'a': 1, 'b': 2} You can set values deeper: >>> items.set(["b", "c", "d"], 5) {'a': 1, 'b': {'c': {'d': 5}}} You can also use a `tuple` (or other Sequence): >>> items.set(("b", "c", "d"), 10) {'a': 1, 'b': {'c': {'d': 10}}} An empty `Sequence` performs no action: >>> items.set((), 20) {'a': 1, 'b': {'c': {'d': 10}}} """ if isinstance(path, str): super().__setitem__(path, value) return self set_path(self, path, value, self.__class__) return self def __lshift__(self: Self, other: Mapping[str, Any]) -> Self: """Merge `other` into `self`. NOTE: Any nested dictionaries will be converted to `AttrDict` objects. Args: other (Mapping[str, Any]): other dictionary to merge Returns: AttrDict: merged dictionary Examples: >>> item = AttrDict(a=1, b=2) >>> item <<= {"b": 3} >>> item.b 3 >>> item << {"b": 2, "c": {"d": 4}} << {"c": {"d": {"e": 5}}} {'a': 1, 'b': 2, 'c': {'d': {'e': 5}}} >>> item.c.d.e 5 """ dict_merge(self, other, cls_dict=self.__class__) return self
Ancestors
- builtins.dict
- typing.Generic
Subclasses
Methods
def copy(self: Self) ‑> ~Self
-
Return a shallow copy.
Examples
>>> items = AttrDict(a=1) >>> clone = items.copy() >>> items == clone # same contents True >>> items is not clone # different pointers True >>> clone.a = 5 >>> items != clone True
Expand source code
def copy(self: Self) -> Self: """Return a shallow copy. Examples: >>> items = AttrDict(a=1) >>> clone = items.copy() >>> items == clone # same contents True >>> items is not clone # different pointers True >>> clone.a = 5 >>> items != clone True """ return self.__class__(super().copy())
def __contains__(self, key: Any) ‑> bool
-
Return
True
ifkey
is a key.Args
key
:Any
- typically a
AnyIndex
. If it's a string, the check proceeds as usual. If it's aSequence
, the checks are performed using.get()
.
Returns
bool
True
if thekey
is a valid key,False
otherwise.
Examples
Normal checking works as expected:
>>> items = AttrDict(a=1, b=2) >>> 'a' in items True >>> 'x' in items False
Nested checks are also possible:
>>> items = AttrDict(a=[{"b": 2}, {"b": 3}]) >>> ['a', 0, 'b'] in items True >>> ['a', 1, 'x'] in items False
Expand source code
def __contains__(self, key: Any) -> bool: """Return `True` if `key` is a key. Args: key (Any): typically a `AnyIndex`. If it's a string, the check proceeds as usual. If it's a `Sequence`, the checks are performed using `.get()`. Returns: bool: `True` if the `key` is a valid key, `False` otherwise. Examples: Normal checking works as expected: >>> items = AttrDict(a=1, b=2) >>> 'a' in items True >>> 'x' in items False Nested checks are also possible: >>> items = AttrDict(a=[{"b": 2}, {"b": 3}]) >>> ['a', 0, 'b'] in items True >>> ['a', 1, 'x'] in items False """ return self.get(key, NOT_FOUND) is not NOT_FOUND
def __getattr__(self, name: str) ‑> Optional[Any]
-
Return the value of the attribute or
None
.Args
name
:str
- attribute name
Returns
Any
- attribute value or
None
if the attribute is missing
Examples
Typically, attributes are the same as key/values:
>>> item = AttrDict(a=1) >>> item.a 1 >>> item['a'] 1
However, instance attributes supersede key/values:
>>> item = AttrDict(b=1) >>> object.__setattr__(item, 'b', 2) >>> item.b 2 >>> item['b'] 1
Missing attributes return
None
:>>> item = AttrDict(a=1) >>> item.b is None True
Expand source code
def __getattr__(self, name: str) -> Optional[Any]: """Return the value of the attribute or `None`. Args: name (str): attribute name Returns: Any: attribute value or `None` if the attribute is missing Examples: Typically, attributes are the same as key/values: >>> item = AttrDict(a=1) >>> item.a 1 >>> item['a'] 1 However, instance attributes supersede key/values: >>> item = AttrDict(b=1) >>> object.__setattr__(item, 'b', 2) >>> item.b 2 >>> item['b'] 1 Missing attributes return `None`: >>> item = AttrDict(a=1) >>> item.b is None True """ # NOTE: This method is only called when the attribute cannot be found. return self[name]
def __setattr__(self, name: str, value: Any) ‑> None
-
Set the value of an attribute.
Args
name
:str
- attribute name
value
:Any
- value to set
Examples
Setting an attribute is usually the same as a key/value:
>>> item = AttrDict() >>> item.a = 1 >>> item.a 1 >>> item['a'] 1
However, instance attributes supersede key/values:
>>> item = AttrDict() >>> object.__setattr__(item, 'b', 2) >>> item.b 2 >>> item.b = 3 # instance attribute updated >>> item.b 3 >>> item['b'] is None True
Expand source code
def __setattr__(self, name: str, value: Any) -> None: """Set the value of an attribute. Args: name (str): attribute name value (Any): value to set Examples: Setting an attribute is usually the same as a key/value: >>> item = AttrDict() >>> item.a = 1 >>> item.a 1 >>> item['a'] 1 However, instance attributes supersede key/values: >>> item = AttrDict() >>> object.__setattr__(item, 'b', 2) >>> item.b 2 >>> item.b = 3 # instance attribute updated >>> item.b 3 >>> item['b'] is None True """ try: super().__getattribute__(name) # is real? super().__setattr__(name, value) except AttributeError: # use key/value self[name] = value
def __delattr__(self, name: str) ‑> None
-
Delete an attribute.
Args
name
:str
- attribute name
Examples
Deleting an attribute usually deletes the underlying key/value:
>>> item = AttrDict(a=1) >>> del item.a >>> item {}
However, instance attributes supersede key/values:
>>> item = AttrDict(b=1) >>> object.__setattr__(item, 'b', 2) >>> item.b # real attribute supersedes key/value 2 >>> del item.b # deletes real attribute >>> object.__getattribute__(item, 'b') # instance attribute gone Traceback (most recent call last): ... AttributeError: 'AttrDict' object has no attribute 'b' >>> item {'b': 1}
Expand source code
def __delattr__(self, name: str) -> None: """Delete an attribute. Args: name (str): attribute name Examples: Deleting an attribute usually deletes the underlying key/value: >>> item = AttrDict(a=1) >>> del item.a >>> item {} However, instance attributes supersede key/values: >>> item = AttrDict(b=1) >>> object.__setattr__(item, 'b', 2) >>> item.b # real attribute supersedes key/value 2 >>> del item.b # deletes real attribute >>> object.__getattribute__(item, 'b') # instance attribute gone Traceback (most recent call last): ... AttributeError: 'AttrDict' object has no attribute 'b' >>> item {'b': 1} """ try: super().__getattribute__(name) # is real? super().__delattr__(name) except AttributeError: # use key/value del self[name]
def __getitem__(self, key: AnyIndex) ‑> Optional[Any]
-
Return the value of the key.
Args
key
:AnyIndex
- key name or Sequence of the path to a key
Returns
Any
- value of the key or
None
if it cannot be found
Examples
>>> item = AttrDict(a=1) >>> item['a'] 1 >>> item['b'] is None True
Expand source code
def __getitem__(self, key: AnyIndex) -> Optional[Any]: """Return the value of the key. Args: key (AnyIndex): key name or Sequence of the path to a key Returns: Any: value of the key or `None` if it cannot be found Examples: >>> item = AttrDict(a=1) >>> item['a'] 1 >>> item['b'] is None True """ return self.get(key)
def __setitem__(self, key: AnyIndex, value: Any) ‑> None
-
Set the value of a key.
Args
key
:AnyIndex
- key name
value
:Any
- key value
Examples
>>> item = AttrDict(a=1) >>> item['a'] = 5 >>> item['a'] 5
>>> item[['a', 'b']] = 10 >>> item.a.b 10
Expand source code
def __setitem__(self, key: AnyIndex, value: Any) -> None: """Set the value of a key. Args: key (AnyIndex): key name value (Any): key value Examples: >>> item = AttrDict(a=1) >>> item['a'] = 5 >>> item['a'] 5 >>> item[['a', 'b']] = 10 >>> item.a.b 10 """ self.set(key, value)
def __delitem__(self, key: str) ‑> None
-
Delete a key.
Args
key
:str
- key name
Examples
>>> item = AttrDict(a=1) >>> del item['a'] >>> item {}
Deleting missing keys has no effect:
>>> item = AttrDict(a=1) >>> del item['b'] >>> item {'a': 1}
Expand source code
def __delitem__(self, key: str) -> None: """Delete a key. Args: key (str): key name Examples: >>> item = AttrDict(a=1) >>> del item['a'] >>> item {} Deleting missing keys has no effect: >>> item = AttrDict(a=1) >>> del item['b'] >>> item {'a': 1} """ try: super().__delitem__(key) except KeyError: pass
def get(self, path: AnyIndex, default: Optional[Any] = None, /) ‑> Optional[Any]
-
Return the value at
path
ordefault
if it cannot be found.Args
path
:AnyIndex
- path to the value
default
:Any
, optional- value to return if
name
is not found. Defaults toNone
.
Returns
Optional[Any]
- key value or
default
if the key is not found
Examples
Normal
.get
functions work:>>> items = AttrDict(a=dict(b=[{"c": 3}, {"c": -10}]), d=4) >>> items.get('d') == 4 True >>> items.get('e') is None True >>> items.get('e', 5) == 5 True
But you can also indexing into nested
list
anddict
:>>> items.get(['a', 'b', 1, 'c']) == -10 True
Bad indexing will just return the default:
>>> items.get(['e']) is None # key doesn't exist True >>> items.get(['a', 'b', 5]) is None # index unreachable True >>> items.get(['d', 'e']) is None # int is not indexable True
Expand source code
def get(self, path: AnyIndex, default: Optional[Any] = None, /) -> Optional[Any]: """Return the value at `path` or `default` if it cannot be found. Args: path (AnyIndex): path to the value default (Any, optional): value to return if `name` is not found. Defaults to `None`. Returns: Optional[Any]: key value or `default` if the key is not found Examples: Normal `.get` functions work: >>> items = AttrDict(a=dict(b=[{"c": 3}, {"c": -10}]), d=4) >>> items.get('d') == 4 True >>> items.get('e') is None True >>> items.get('e', 5) == 5 True But you can also indexing into nested `list` and `dict`: >>> items.get(['a', 'b', 1, 'c']) == -10 True Bad indexing will just return the default: >>> items.get(['e']) is None # key doesn't exist True >>> items.get(['a', 'b', 5]) is None # index unreachable True >>> items.get(['d', 'e']) is None # int is not indexable True """ if isinstance(path, str): return super().get(path, default) return get_path(self, path, default)
def set(self: Self, path: AnyIndex, value: Optional[Any] = None, /) ‑> ~Self
-
Set key at
path
tovalue
.Args
path
:AnyIndex
- path to the key to set.
value
:Any
, optional- value to set. Defaults to
None
.
Returns
Self
- for chaining
Examples
>>> items = AttrDict({"a": 1}) >>> items.set("b", 2) {'a': 1, 'b': 2}
You can set values deeper:
>>> items.set(["b", "c", "d"], 5) {'a': 1, 'b': {'c': {'d': 5}}}
You can also use a
tuple
(or other Sequence):>>> items.set(("b", "c", "d"), 10) {'a': 1, 'b': {'c': {'d': 10}}}
An empty
Sequence
performs no action:>>> items.set((), 20) {'a': 1, 'b': {'c': {'d': 10}}}
Expand source code
def set(self: Self, path: AnyIndex, value: Optional[Any] = None, /) -> Self: """Set key at `path` to `value`. Args: path (AnyIndex): path to the key to set. value (Any, optional): value to set. Defaults to `None`. Returns: Self: for chaining Examples: >>> items = AttrDict({"a": 1}) >>> items.set("b", 2) {'a': 1, 'b': 2} You can set values deeper: >>> items.set(["b", "c", "d"], 5) {'a': 1, 'b': {'c': {'d': 5}}} You can also use a `tuple` (or other Sequence): >>> items.set(("b", "c", "d"), 10) {'a': 1, 'b': {'c': {'d': 10}}} An empty `Sequence` performs no action: >>> items.set((), 20) {'a': 1, 'b': {'c': {'d': 10}}} """ if isinstance(path, str): super().__setitem__(path, value) return self set_path(self, path, value, self.__class__) return self
def __lshift__(self: Self, other: Mapping[str, Any]) ‑> ~Self
-
Merge
other
intoself
.NOTE: Any nested dictionaries will be converted to
AttrDict
objects.Args
other
:Mapping[str, Any]
- other dictionary to merge
Returns
AttrDict
- merged dictionary
Examples
>>> item = AttrDict(a=1, b=2) >>> item <<= {"b": 3} >>> item.b 3
>>> item << {"b": 2, "c": {"d": 4}} << {"c": {"d": {"e": 5}}} {'a': 1, 'b': 2, 'c': {'d': {'e': 5}}} >>> item.c.d.e 5
Expand source code
def __lshift__(self: Self, other: Mapping[str, Any]) -> Self: """Merge `other` into `self`. NOTE: Any nested dictionaries will be converted to `AttrDict` objects. Args: other (Mapping[str, Any]): other dictionary to merge Returns: AttrDict: merged dictionary Examples: >>> item = AttrDict(a=1, b=2) >>> item <<= {"b": 3} >>> item.b 3 >>> item << {"b": 2, "c": {"d": 4}} << {"c": {"d": {"e": 5}}} {'a': 1, 'b': 2, 'c': {'d': {'e': 5}}} >>> item.c.d.e 5 """ dict_merge(self, other, cls_dict=self.__class__) return self
class AttrList (*args, **kwargs)
-
Collection that broadcasts attribute, index, and function calls to its members.
Attribute access (
.
) is broadcast to all members. An exception is an attribute that exists on the list instance itself.>>> nums = AttrList([complex(1, 2), complex(3, 4), complex(5, 6)]) >>> nums.real [1.0, 3.0, 5.0]
Array access (
[]
) withint
andslice
indexes works as usual by returning a portion of the list.string
indexes, however, are broadcast to each member.>>> items = AttrList(["Apple", "Bat", "Cat"]) >>> items[0] 'Apple' >>> items["0"] ['A', 'B', 'C']
Calling the list (
()
) broadcasts the call to all members. Usually, this is combined with attribute access:>>> items = AttrList(["Apple", "Bat", "Cat"]) >>> items.lower() ['apple', 'bat', 'cat']
Expand source code
class AttrList(List[Any]): """Collection that broadcasts attribute, index, and function calls to its members. Attribute access (`.`) is broadcast to all members. An exception is an attribute that exists on the list instance itself. >>> nums = AttrList([complex(1, 2), complex(3, 4), complex(5, 6)]) >>> nums.real [1.0, 3.0, 5.0] Array access (`[]`) with `int` and `slice` indexes works as usual by returning a portion of the list. `string` indexes, however, are broadcast to each member. >>> items = AttrList(["Apple", "Bat", "Cat"]) >>> items[0] 'Apple' >>> items["0"] ['A', 'B', 'C'] Calling the list (`()`) broadcasts the call to all members. Usually, this is combined with attribute access: >>> items = AttrList(["Apple", "Bat", "Cat"]) >>> items.lower() ['apple', 'bat', 'cat'] """ def __getattr__(self, name: str) -> AttrList: """Return an attribute from all members. Args: name (str): attribute name Returns: (list[any]): value of attribute for each member or `None` if missing Examples: >>> from . import AttrDict >>> items = AttrList([AttrDict(a=1), AttrDict(a=2), AttrDict(a=3, b=4)]) >>> items.a [1, 2, 3] >>> items.b [None, None, 4] Note that instance attributes supersede member attributes: >>> object.__setattr__(items, "b", 5) >>> items.b 5 """ # NOTE: This method is only called when the attribute cannot be found. # We delegate this call to every member. result = self.__class__() for member in self: # if isinstance(member, AttrDict): # check if name is defined # result.append(member[name] if name in member else None) # else: result.append(getattr(member, name, None)) return result def __setattr__(self, name: str, value: Any) -> None: """Set an attribute on all members (or the list itself). Args: name (str): attribute name value (any): attribute value Examples: >>> from . import AttrDict >>> items = AttrList([AttrDict(a=1), AttrDict(b=2)]) >>> items.a = 5 >>> items [{'a': 5}, {'b': 2, 'a': 5}] Note that instance attributes supersede member attributes: >>> object.__setattr__(items, "b", 5) >>> items.b = 7 >>> items.b 7 """ try: super().__getattribute__(name) # is real? super().__setattr__(name, value) except AttributeError: # use members for member in self: setattr(member, name, value) def __delattr__(self, name: str) -> None: """Delete an attribute from all members (or the list itself). Args: name (str): attribute name Examples: >>> from . import AttrDict >>> items = AttrList([AttrDict(a=1, b=2), AttrDict(a=2, c=3), dict(d=4)]) >>> del items.a >>> items [{'b': 2}, {'c': 3}, {'d': 4}] Deleting an instance attribute works: >>> object.__setattr__(items, 'b', 5) >>> items.b = 7 >>> items.b 7 >>> del items.b # deletes instance attribute >>> items [{'b': 2}, {'c': 3}, {'d': 4}] """ try: super().__getattribute__(name) # is real? super().__delattr__(name) except AttributeError: # use members for member in self: try: delattr(member, name) except AttributeError: pass def __getitem__(self: Self, index: AttrListKey) -> Union[Self, Any]: """Return an item from all members (or the list itself). Args: index (int|slice|str): item index Returns: (any): the item at the index in each member (`str` index) or the member at the given index (`int` or `slice`) Examples: A string index is applied to all members: >>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> items["-1"] [3, 6, 9] Normal indexing works as expected: >>> items = AttrList([1, 2, 3]) >>> items[1] 2 >>> items[0:2] [1, 2] Weird indexing throws a `TypeError`: >>> items = AttrList([1, 2, 3]) >>> items[{"a": 1}] Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict """ result = self if isinstance(index, str): index = str2index(index) result = self.__class__(item[index] for item in self) elif isinstance(index, slice): result = self.__class__(super().__getitem__(index)) else: result = super().__getitem__(index) return result def __setitem__(self, index: AttrListKey, value: Any) -> None: """Set an item in all members (or the list itself). Args: index (int|slice|str): item index value (any): item value Examples: A string index is applied to all members: >>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> items["0"] = 100 >>> items [[100, 2, 3], [100, 5, 6], [100, 8, 9]] Immutable members are untouched: >>> items = AttrList(["Cat", [1, 2, 3]]) >>> items["0"] = "A" >>> items ['Cat', ['A', 2, 3]] Normal indexing works as expected: >>> items = AttrList([1, 2, 3]) >>> items[1] = 5 >>> items [1, 5, 3] Weird indexing throws a `TypeError`: >>> items = AttrList([1, 2, 3]) >>> items[{"a": 1}] = 100 Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict """ if isinstance(index, str): index = str2index(index) for member in self: if hasattr(member, "__setitem__"): member.__setitem__(index, value) else: super().__setitem__(index, value) def __delitem__(self, index: AttrListKey) -> None: """Delete an item from all members (or the list itself). Args: index (int|slice|str): item index Examples: A string index is applied to all members: >>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> del items["0"] >>> items [[2, 3], [5, 6], [8, 9]] Immutable members are untouched: >>> items = AttrList(["Cat", [1, 2, 3]]) >>> del items["-1"] >>> items ['Cat', [1, 2]] Normal indexing works as expected: >>> items = AttrList([1, 2, 3]) >>> del items[1] >>> items [1, 3] Weird indexing throws a `TypeError`: >>> items = AttrList([1, 2, 3]) >>> del items[{"a": 1}] Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict """ if isinstance(index, str): index = str2index(index) for member in self: if hasattr(member, "__delitem__"): member.__delitem__(index) else: super().__delitem__(index) def __call__(self: Self, *args: Any, **kwargs: Any) -> Self: """Return a new list with the result of calling all callables in the list. Args: args (*any): arguments to the callable kwargs (**any): keyword arguments to the callable Returns: (Self): new list with results of the callable Examples: >>> items = AttrList(["a", "b", "c"]) >>> items.upper() ['A', 'B', 'C'] >>> items = AttrList([lambda x: x + 2, lambda y: y + 5, "Z"]) >>> items(1) [3, 6, 'Z'] """ return self.__class__( item(*args, **kwargs) if callable(item) else item for item in self )
Ancestors
- builtins.list
- typing.Generic
Methods
def __getattr__(self, name: str) ‑> AttrList
-
Return an attribute from all members.
Args
name
:str
- attribute name
Returns
(list[any]): value of attribute for each member or
None
if missingExamples
>>> from . import AttrDict >>> items = AttrList([AttrDict(a=1), AttrDict(a=2), AttrDict(a=3, b=4)]) >>> items.a [1, 2, 3] >>> items.b [None, None, 4]
Note that instance attributes supersede member attributes:
>>> object.__setattr__(items, "b", 5) >>> items.b 5
Expand source code
def __getattr__(self, name: str) -> AttrList: """Return an attribute from all members. Args: name (str): attribute name Returns: (list[any]): value of attribute for each member or `None` if missing Examples: >>> from . import AttrDict >>> items = AttrList([AttrDict(a=1), AttrDict(a=2), AttrDict(a=3, b=4)]) >>> items.a [1, 2, 3] >>> items.b [None, None, 4] Note that instance attributes supersede member attributes: >>> object.__setattr__(items, "b", 5) >>> items.b 5 """ # NOTE: This method is only called when the attribute cannot be found. # We delegate this call to every member. result = self.__class__() for member in self: # if isinstance(member, AttrDict): # check if name is defined # result.append(member[name] if name in member else None) # else: result.append(getattr(member, name, None)) return result
def __setattr__(self, name: str, value: Any) ‑> None
-
Set an attribute on all members (or the list itself).
Args
name
:str
- attribute name
value
:any
- attribute value
Examples
>>> from . import AttrDict >>> items = AttrList([AttrDict(a=1), AttrDict(b=2)]) >>> items.a = 5 >>> items [{'a': 5}, {'b': 2, 'a': 5}]
Note that instance attributes supersede member attributes:
>>> object.__setattr__(items, "b", 5) >>> items.b = 7 >>> items.b 7
Expand source code
def __setattr__(self, name: str, value: Any) -> None: """Set an attribute on all members (or the list itself). Args: name (str): attribute name value (any): attribute value Examples: >>> from . import AttrDict >>> items = AttrList([AttrDict(a=1), AttrDict(b=2)]) >>> items.a = 5 >>> items [{'a': 5}, {'b': 2, 'a': 5}] Note that instance attributes supersede member attributes: >>> object.__setattr__(items, "b", 5) >>> items.b = 7 >>> items.b 7 """ try: super().__getattribute__(name) # is real? super().__setattr__(name, value) except AttributeError: # use members for member in self: setattr(member, name, value)
def __delattr__(self, name: str) ‑> None
-
Delete an attribute from all members (or the list itself).
Args
name
:str
- attribute name
Examples
>>> from . import AttrDict >>> items = AttrList([AttrDict(a=1, b=2), AttrDict(a=2, c=3), dict(d=4)]) >>> del items.a >>> items [{'b': 2}, {'c': 3}, {'d': 4}]
Deleting an instance attribute works:
>>> object.__setattr__(items, 'b', 5) >>> items.b = 7 >>> items.b 7 >>> del items.b # deletes instance attribute >>> items [{'b': 2}, {'c': 3}, {'d': 4}]
Expand source code
def __delattr__(self, name: str) -> None: """Delete an attribute from all members (or the list itself). Args: name (str): attribute name Examples: >>> from . import AttrDict >>> items = AttrList([AttrDict(a=1, b=2), AttrDict(a=2, c=3), dict(d=4)]) >>> del items.a >>> items [{'b': 2}, {'c': 3}, {'d': 4}] Deleting an instance attribute works: >>> object.__setattr__(items, 'b', 5) >>> items.b = 7 >>> items.b 7 >>> del items.b # deletes instance attribute >>> items [{'b': 2}, {'c': 3}, {'d': 4}] """ try: super().__getattribute__(name) # is real? super().__delattr__(name) except AttributeError: # use members for member in self: try: delattr(member, name) except AttributeError: pass
def __getitem__(self: Self, index: AttrListKey) ‑> Union[~Self, Any]
-
Return an item from all members (or the list itself).
Args
index (int|slice|str): item index
Returns
(any): the item at the index in each member (
str
index) or the member at the given index (int
orslice
)Examples
A string index is applied to all members:
>>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> items["-1"] [3, 6, 9]
Normal indexing works as expected:
>>> items = AttrList([1, 2, 3]) >>> items[1] 2 >>> items[0:2] [1, 2]
Weird indexing throws a
TypeError
:>>> items = AttrList([1, 2, 3]) >>> items[{"a": 1}] Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict
Expand source code
def __getitem__(self: Self, index: AttrListKey) -> Union[Self, Any]: """Return an item from all members (or the list itself). Args: index (int|slice|str): item index Returns: (any): the item at the index in each member (`str` index) or the member at the given index (`int` or `slice`) Examples: A string index is applied to all members: >>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> items["-1"] [3, 6, 9] Normal indexing works as expected: >>> items = AttrList([1, 2, 3]) >>> items[1] 2 >>> items[0:2] [1, 2] Weird indexing throws a `TypeError`: >>> items = AttrList([1, 2, 3]) >>> items[{"a": 1}] Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict """ result = self if isinstance(index, str): index = str2index(index) result = self.__class__(item[index] for item in self) elif isinstance(index, slice): result = self.__class__(super().__getitem__(index)) else: result = super().__getitem__(index) return result
def __setitem__(self, index: AttrListKey, value: Any) ‑> None
-
Set an item in all members (or the list itself).
Args
- index (int|slice|str): item index
value
:any
- item value
Examples
A string index is applied to all members:
>>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> items["0"] = 100 >>> items [[100, 2, 3], [100, 5, 6], [100, 8, 9]]
Immutable members are untouched:
>>> items = AttrList(["Cat", [1, 2, 3]]) >>> items["0"] = "A" >>> items ['Cat', ['A', 2, 3]]
Normal indexing works as expected:
>>> items = AttrList([1, 2, 3]) >>> items[1] = 5 >>> items [1, 5, 3]
Weird indexing throws a
TypeError
:>>> items = AttrList([1, 2, 3]) >>> items[{"a": 1}] = 100 Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict
Expand source code
def __setitem__(self, index: AttrListKey, value: Any) -> None: """Set an item in all members (or the list itself). Args: index (int|slice|str): item index value (any): item value Examples: A string index is applied to all members: >>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> items["0"] = 100 >>> items [[100, 2, 3], [100, 5, 6], [100, 8, 9]] Immutable members are untouched: >>> items = AttrList(["Cat", [1, 2, 3]]) >>> items["0"] = "A" >>> items ['Cat', ['A', 2, 3]] Normal indexing works as expected: >>> items = AttrList([1, 2, 3]) >>> items[1] = 5 >>> items [1, 5, 3] Weird indexing throws a `TypeError`: >>> items = AttrList([1, 2, 3]) >>> items[{"a": 1}] = 100 Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict """ if isinstance(index, str): index = str2index(index) for member in self: if hasattr(member, "__setitem__"): member.__setitem__(index, value) else: super().__setitem__(index, value)
def __delitem__(self, index: AttrListKey) ‑> None
-
Delete an item from all members (or the list itself).
Args
index (int|slice|str): item index
Examples
A string index is applied to all members:
>>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> del items["0"] >>> items [[2, 3], [5, 6], [8, 9]]
Immutable members are untouched:
>>> items = AttrList(["Cat", [1, 2, 3]]) >>> del items["-1"] >>> items ['Cat', [1, 2]]
Normal indexing works as expected:
>>> items = AttrList([1, 2, 3]) >>> del items[1] >>> items [1, 3]
Weird indexing throws a
TypeError
:>>> items = AttrList([1, 2, 3]) >>> del items[{"a": 1}] Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict
Expand source code
def __delitem__(self, index: AttrListKey) -> None: """Delete an item from all members (or the list itself). Args: index (int|slice|str): item index Examples: A string index is applied to all members: >>> items = AttrList([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> del items["0"] >>> items [[2, 3], [5, 6], [8, 9]] Immutable members are untouched: >>> items = AttrList(["Cat", [1, 2, 3]]) >>> del items["-1"] >>> items ['Cat', [1, 2]] Normal indexing works as expected: >>> items = AttrList([1, 2, 3]) >>> del items[1] >>> items [1, 3] Weird indexing throws a `TypeError`: >>> items = AttrList([1, 2, 3]) >>> del items[{"a": 1}] Traceback (most recent call last): ... TypeError: list indices must be integers or slices, not dict """ if isinstance(index, str): index = str2index(index) for member in self: if hasattr(member, "__delitem__"): member.__delitem__(index) else: super().__delitem__(index)
def __call__(self: Self, *args: Any, **kwargs: Any) ‑> ~Self
-
Return a new list with the result of calling all callables in the list.
Args
args (any): arguments to the callable kwargs (*any): keyword arguments to the callable
Returns
(Self): new list with results of the callable
Examples
>>> items = AttrList(["a", "b", "c"]) >>> items.upper() ['A', 'B', 'C']
>>> items = AttrList([lambda x: x + 2, lambda y: y + 5, "Z"]) >>> items(1) [3, 6, 'Z']
Expand source code
def __call__(self: Self, *args: Any, **kwargs: Any) -> Self: """Return a new list with the result of calling all callables in the list. Args: args (*any): arguments to the callable kwargs (**any): keyword arguments to the callable Returns: (Self): new list with results of the callable Examples: >>> items = AttrList(["a", "b", "c"]) >>> items.upper() ['A', 'B', 'C'] >>> items = AttrList([lambda x: x + 2, lambda y: y + 5, "Z"]) >>> items(1) [3, 6, 'Z'] """ return self.__class__( item(*args, **kwargs) if callable(item) else item for item in self )
class JSend (*args: Any, **kwargs: Dict[str, Any])
-
Service response object.
This object loosely conforms to the JSend specification.
Construct a JSend object.
Examples
>>> result = JSend() >>> result.ok True >>> result.status == 'success' True >>> result.data is None True >>> result == {'ok': True, 'status': 'success', 'data': None} True
Expand source code
class JSend(AttrDict): """Service response object. This object loosely conforms to the [JSend specification](https://labs.omniti.com/labs/jsend). """ def __init__(self, *args: Any, **kwargs: Dict[str, Any]): """Construct a JSend object. Examples: >>> result = JSend() >>> result.ok True >>> result.status == 'success' True >>> result.data is None True >>> result == {'ok': True, 'status': 'success', 'data': None} True """ self.update(ok=True, status=STATUS_SUCCESS, data=None) super().__init__(*args, **kwargs) def fail(self, message: Msg = None) -> JSend: """Indicate a controlled failure. Args: message (str): human-readable explanation of the failure Returns: JSend: self for chaining Examples: >>> result = JSend() >>> msg = 'Missing a phone number.' >>> result.fail(msg) is result True >>> result.ok is False True >>> result.status == 'fail' True >>> result.message == msg True """ self.update(ok=False, status=STATUS_FAIL, message=message) return self def error(self, message: Msg = None, code: Optional[Any] = None) -> JSend: """Indicate an uncontrolled error. Args: message (str): human-readable explanation of the error code (Any, optional): technical indication of the error Returns: JSend: self for chaining Examples: >>> result = JSend() >>> msg = 'No such file [file.text].' >>> code = 13 >>> result.error(msg, code) is result True >>> result.ok is False True >>> result.status == 'error' True >>> result.message == msg True >>> result.code == code True """ self.update(ok=False, status=STATUS_ERROR, message=message, code=code) return self def success(self, data: Optional[Any] = None) -> JSend: """Indicate a successful response. Args: data (Any, optional): response payload Returns: JSend: self for chaining Examples: >>> data = "Works" >>> result = JSend() >>> result.success(data) is result True >>> result.data == data True """ self.update(ok=True, status=STATUS_SUCCESS, data=data) return self
Ancestors
- AttrDict
- builtins.dict
- typing.Generic
Methods
def fail(self, message: Msg = None) ‑> JSend
-
Indicate a controlled failure.
Args
message
:str
- human-readable explanation of the failure
Returns
JSend
- self for chaining
Examples
>>> result = JSend() >>> msg = 'Missing a phone number.' >>> result.fail(msg) is result True >>> result.ok is False True >>> result.status == 'fail' True >>> result.message == msg True
Expand source code
def fail(self, message: Msg = None) -> JSend: """Indicate a controlled failure. Args: message (str): human-readable explanation of the failure Returns: JSend: self for chaining Examples: >>> result = JSend() >>> msg = 'Missing a phone number.' >>> result.fail(msg) is result True >>> result.ok is False True >>> result.status == 'fail' True >>> result.message == msg True """ self.update(ok=False, status=STATUS_FAIL, message=message) return self
def error(self, message: Msg = None, code: Optional[Any] = None) ‑> JSend
-
Indicate an uncontrolled error.
Args
message
:str
- human-readable explanation of the error
code
:Any
, optional- technical indication of the error
Returns
JSend
- self for chaining
Examples
>>> result = JSend() >>> msg = 'No such file [file.text].' >>> code = 13 >>> result.error(msg, code) is result True >>> result.ok is False True >>> result.status == 'error' True >>> result.message == msg True >>> result.code == code True
Expand source code
def error(self, message: Msg = None, code: Optional[Any] = None) -> JSend: """Indicate an uncontrolled error. Args: message (str): human-readable explanation of the error code (Any, optional): technical indication of the error Returns: JSend: self for chaining Examples: >>> result = JSend() >>> msg = 'No such file [file.text].' >>> code = 13 >>> result.error(msg, code) is result True >>> result.ok is False True >>> result.status == 'error' True >>> result.message == msg True >>> result.code == code True """ self.update(ok=False, status=STATUS_ERROR, message=message, code=code) return self
def success(self, data: Optional[Any] = None) ‑> JSend
-
Indicate a successful response.
Args
data
:Any
, optional- response payload
Returns
JSend
- self for chaining
Examples
>>> data = "Works" >>> result = JSend() >>> result.success(data) is result True >>> result.data == data True
Expand source code
def success(self, data: Optional[Any] = None) -> JSend: """Indicate a successful response. Args: data (Any, optional): response payload Returns: JSend: self for chaining Examples: >>> data = "Works" >>> result = JSend() >>> result.success(data) is result True >>> result.data == data True """ self.update(ok=True, status=STATUS_SUCCESS, data=data) return self
def copy(self: Self) ‑> ~Self
-
Return a shallow copy …
def __contains__(self, key: Any) ‑> bool
-
Inherited from:
AttrDict
.__contains__
Return
True
ifkey
is a key … def __getattr__(self, name: str) ‑> Optional[Any]
-
Inherited from:
AttrDict
.__getattr__
Return the value of the attribute or
None
… def __setattr__(self, name: str, value: Any) ‑> None
-
Inherited from:
AttrDict
.__setattr__
Set the value of an attribute …
def __delattr__(self, name: str) ‑> None
-
Inherited from:
AttrDict
.__delattr__
Delete an attribute …
def __getitem__(self, key: AnyIndex) ‑> Optional[Any]
-
Inherited from:
AttrDict
.__getitem__
Return the value of the key …
def __setitem__(self, key: AnyIndex, value: Any) ‑> None
-
Inherited from:
AttrDict
.__setitem__
Set the value of a key …
def __delitem__(self, key: str) ‑> None
-
Inherited from:
AttrDict
.__delitem__
Delete a key …
def get(self, path: AnyIndex, default: Optional[Any] = None, /) ‑> Optional[Any]
-
Return the value at
path
ordefault
if it cannot be found … def set(self: Self, path: AnyIndex, value: Optional[Any] = None, /) ‑> ~Self
-
Set key at
path
tovalue
… def __lshift__(self: Self, other: Mapping[str, Any]) ‑> ~Self
-
Inherited from:
AttrDict
.__lshift__
Merge
other
intoself
…