more libraries

This commit is contained in:
2024-09-28 22:58:22 -07:00
parent 1973934e95
commit b6e3e24aec
1099 changed files with 326677 additions and 208 deletions

View File

@@ -0,0 +1,534 @@
""" Munch is a subclass of dict with attribute-style access.
>>> b = Munch()
>>> b.hello = 'world'
>>> b.hello
'world'
>>> b['hello'] += "!"
>>> b.hello
'world!'
>>> b.foo = Munch(lol=True)
>>> b.foo.lol
True
>>> b.foo is b['foo']
True
It is safe to import * from this module:
__all__ = ('Munch', 'munchify','unmunchify')
un/munchify provide dictionary conversion; Munches can also be
converted via Munch.to/fromDict().
"""
from collections.abc import Mapping
__version__ = "2.5.0"
VERSION = tuple(map(int, __version__.split('.')[:3]))
__all__ = ('Munch', 'munchify', 'DefaultMunch', 'DefaultFactoryMunch', 'unmunchify')
class Munch(dict):
""" A dictionary that provides attribute-style access.
>>> b = Munch()
>>> b.hello = 'world'
>>> b.hello
'world'
>>> b['hello'] += "!"
>>> b.hello
'world!'
>>> b.foo = Munch(lol=True)
>>> b.foo.lol
True
>>> b.foo is b['foo']
True
A Munch is a subclass of dict; it supports all the methods a dict does...
>>> sorted(b.keys())
['foo', 'hello']
Including update()...
>>> b.update({ 'ponies': 'are pretty!' }, hello=42)
>>> print (repr(b))
Munch({'ponies': 'are pretty!', 'foo': Munch({'lol': True}), 'hello': 42})
As well as iteration...
>>> sorted([ (k,b[k]) for k in b ])
[('foo', Munch({'lol': True})), ('hello', 42), ('ponies', 'are pretty!')]
And "splats".
>>> "The {knights} who say {ni}!".format(**Munch(knights='lolcats', ni='can haz'))
'The lolcats who say can haz!'
See unmunchify/Munch.toDict, munchify/Munch.fromDict for notes about conversion.
"""
def __init__(self, *args, **kwargs): # pylint: disable=super-init-not-called
self.update(*args, **kwargs)
# only called if k not found in normal places
def __getattr__(self, k):
""" Gets key if it exists, otherwise throws AttributeError.
nb. __getattr__ is only called if key is not found in normal places.
>>> b = Munch(bar='baz', lol={})
>>> b.foo
Traceback (most recent call last):
...
AttributeError: foo
>>> b.bar
'baz'
>>> getattr(b, 'bar')
'baz'
>>> b['bar']
'baz'
>>> b.lol is b['lol']
True
>>> b.lol is getattr(b, 'lol')
True
"""
try:
# Throws exception if not in prototype chain
return object.__getattribute__(self, k)
except AttributeError:
try:
return self[k]
except KeyError:
raise AttributeError(k)
def __setattr__(self, k, v):
""" Sets attribute k if it exists, otherwise sets key k. A KeyError
raised by set-item (only likely if you subclass Munch) will
propagate as an AttributeError instead.
>>> b = Munch(foo='bar', this_is='useful when subclassing')
>>> hasattr(b.values, '__call__')
True
>>> b.values = 'uh oh'
>>> b.values
'uh oh'
>>> b['values']
Traceback (most recent call last):
...
KeyError: 'values'
"""
try:
# Throws exception if not in prototype chain
object.__getattribute__(self, k)
except AttributeError:
try:
self[k] = v
except:
raise AttributeError(k)
else:
object.__setattr__(self, k, v)
def __delattr__(self, k):
""" Deletes attribute k if it exists, otherwise deletes key k. A KeyError
raised by deleting the key--such as when the key is missing--will
propagate as an AttributeError instead.
>>> b = Munch(lol=42)
>>> del b.lol
>>> b.lol
Traceback (most recent call last):
...
AttributeError: lol
"""
try:
# Throws exception if not in prototype chain
object.__getattribute__(self, k)
except AttributeError:
try:
del self[k]
except KeyError:
raise AttributeError(k)
else:
object.__delattr__(self, k)
def toDict(self):
""" Recursively converts a munch back into a dictionary.
>>> b = Munch(foo=Munch(lol=True), hello=42, ponies='are pretty!')
>>> sorted(b.toDict().items())
[('foo', {'lol': True}), ('hello', 42), ('ponies', 'are pretty!')]
See unmunchify for more info.
"""
return unmunchify(self)
@property
def __dict__(self):
return self.toDict()
def __repr__(self):
""" Invertible* string-form of a Munch.
>>> b = Munch(foo=Munch(lol=True), hello=42, ponies='are pretty!')
>>> print (repr(b))
Munch({'ponies': 'are pretty!', 'foo': Munch({'lol': True}), 'hello': 42})
>>> eval(repr(b))
Munch({'ponies': 'are pretty!', 'foo': Munch({'lol': True}), 'hello': 42})
>>> with_spaces = Munch({1: 2, 'a b': 9, 'c': Munch({'simple': 5})})
>>> print (repr(with_spaces))
Munch({'a b': 9, 1: 2, 'c': Munch({'simple': 5})})
>>> eval(repr(with_spaces))
Munch({'a b': 9, 1: 2, 'c': Munch({'simple': 5})})
(*) Invertible so long as collection contents are each repr-invertible.
"""
return '{0}({1})'.format(self.__class__.__name__, dict.__repr__(self))
def __dir__(self):
return list(self.keys())
def __getstate__(self):
""" Implement a serializable interface used for pickling.
See https://docs.python.org/3.6/library/pickle.html.
"""
return {k: v for k, v in self.items()}
def __setstate__(self, state):
""" Implement a serializable interface used for pickling.
See https://docs.python.org/3.6/library/pickle.html.
"""
self.clear()
self.update(state)
__members__ = __dir__ # for python2.x compatibility
@classmethod
def fromDict(cls, d):
""" Recursively transforms a dictionary into a Munch via copy.
>>> b = Munch.fromDict({'urmom': {'sez': {'what': 'what'}}})
>>> b.urmom.sez.what
'what'
See munchify for more info.
"""
return munchify(d, cls)
def copy(self):
return type(self).fromDict(self)
def update(self, *args, **kwargs):
"""
Override built-in method to call custom __setitem__ method that may
be defined in subclasses.
"""
for k, v in dict(*args, **kwargs).items():
self[k] = v
def get(self, k, d=None):
"""
D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.
"""
if k not in self:
return d
return self[k]
def setdefault(self, k, d=None):
"""
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
"""
if k not in self:
self[k] = d
return self[k]
class AutoMunch(Munch):
def __setattr__(self, k, v):
""" Works the same as Munch.__setattr__ but if you supply
a dictionary as value it will convert it to another Munch.
"""
if isinstance(v, Mapping) and not isinstance(v, (AutoMunch, Munch)):
v = munchify(v, AutoMunch)
super(AutoMunch, self).__setattr__(k, v)
class DefaultMunch(Munch):
"""
A Munch that returns a user-specified value for missing keys.
"""
def __init__(self, *args, **kwargs):
""" Construct a new DefaultMunch. Like collections.defaultdict, the
first argument is the default value; subsequent arguments are the
same as those for dict.
"""
# Mimic collections.defaultdict constructor
if args:
default = args[0]
args = args[1:]
else:
default = None
super(DefaultMunch, self).__init__(*args, **kwargs)
self.__default__ = default
def __getattr__(self, k):
""" Gets key if it exists, otherwise returns the default value."""
try:
return super(DefaultMunch, self).__getattr__(k)
except AttributeError:
return self.__default__
def __setattr__(self, k, v):
if k == '__default__':
object.__setattr__(self, k, v)
else:
super(DefaultMunch, self).__setattr__(k, v)
def __getitem__(self, k):
""" Gets key if it exists, otherwise returns the default value."""
try:
return super(DefaultMunch, self).__getitem__(k)
except KeyError:
return self.__default__
def __getstate__(self):
""" Implement a serializable interface used for pickling.
See https://docs.python.org/3.6/library/pickle.html.
"""
return (self.__default__, {k: v for k, v in self.items()})
def __setstate__(self, state):
""" Implement a serializable interface used for pickling.
See https://docs.python.org/3.6/library/pickle.html.
"""
self.clear()
default, state_dict = state
self.update(state_dict)
self.__default__ = default
@classmethod
def fromDict(cls, d, default=None):
# pylint: disable=arguments-differ
return munchify(d, factory=lambda d_: cls(default, d_))
def copy(self):
return type(self).fromDict(self, default=self.__default__)
def __repr__(self):
return '{0}({1!r}, {2})'.format(
type(self).__name__, self.__undefined__, dict.__repr__(self))
class DefaultFactoryMunch(Munch):
""" A Munch that calls a user-specified function to generate values for
missing keys like collections.defaultdict.
>>> b = DefaultFactoryMunch(list, {'hello': 'world!'})
>>> b.hello
'world!'
>>> b.foo
[]
>>> b.bar.append('hello')
>>> b.bar
['hello']
"""
def __init__(self, default_factory, *args, **kwargs):
super(DefaultFactoryMunch, self).__init__(*args, **kwargs)
self.default_factory = default_factory
@classmethod
def fromDict(cls, d, default_factory):
# pylint: disable=arguments-differ
return munchify(d, factory=lambda d_: cls(default_factory, d_))
def copy(self):
return type(self).fromDict(self, default_factory=self.default_factory)
def __repr__(self):
factory = self.default_factory.__name__
return '{0}({1}, {2})'.format(
type(self).__name__, factory, dict.__repr__(self))
def __setattr__(self, k, v):
if k == 'default_factory':
object.__setattr__(self, k, v)
else:
super(DefaultFactoryMunch, self).__setattr__(k, v)
def __missing__(self, k):
self[k] = self.default_factory()
return self[k]
# While we could convert abstract types like Mapping or Iterable, I think
# munchify is more likely to "do what you mean" if it is conservative about
# casting (ex: isinstance(str,Iterable) == True ).
#
# Should you disagree, it is not difficult to duplicate this function with
# more aggressive coercion to suit your own purposes.
def munchify(x, factory=Munch):
""" Recursively transforms a dictionary into a Munch via copy.
>>> b = munchify({'urmom': {'sez': {'what': 'what'}}})
>>> b.urmom.sez.what
'what'
munchify can handle intermediary dicts, lists and tuples (as well as
their subclasses), but ymmv on custom datatypes.
>>> b = munchify({ 'lol': ('cats', {'hah':'i win again'}),
... 'hello': [{'french':'salut', 'german':'hallo'}] })
>>> b.hello[0].french
'salut'
>>> b.lol[1].hah
'i win again'
nb. As dicts are not hashable, they cannot be nested in sets/frozensets.
"""
# Munchify x, using `seen` to track object cycles
seen = dict()
def munchify_cycles(obj):
# If we've already begun munchifying obj, just return the already-created munchified obj
try:
return seen[id(obj)]
except KeyError:
pass
# Otherwise, first partly munchify obj (but without descending into any lists or dicts) and save that
seen[id(obj)] = partial = pre_munchify(obj)
# Then finish munchifying lists and dicts inside obj (reusing munchified obj if cycles are encountered)
return post_munchify(partial, obj)
def pre_munchify(obj):
# Here we return a skeleton of munchified obj, which is enough to save for later (in case
# we need to break cycles) but it needs to filled out in post_munchify
if isinstance(obj, Mapping):
return factory({})
elif isinstance(obj, list):
return type(obj)()
elif isinstance(obj, tuple):
type_factory = getattr(obj, "_make", type(obj))
return type_factory(munchify_cycles(item) for item in obj)
else:
return obj
def post_munchify(partial, obj):
# Here we finish munchifying the parts of obj that were deferred by pre_munchify because they
# might be involved in a cycle
if isinstance(obj, Mapping):
partial.update((k, munchify_cycles(obj[k])) for k in obj.keys())
elif isinstance(obj, list):
partial.extend(munchify_cycles(item) for item in obj)
elif isinstance(obj, tuple):
for (item_partial, item) in zip(partial, obj):
post_munchify(item_partial, item)
return partial
return munchify_cycles(x)
def unmunchify(x):
""" Recursively converts a Munch into a dictionary.
>>> b = Munch(foo=Munch(lol=True), hello=42, ponies='are pretty!')
>>> sorted(unmunchify(b).items())
[('foo', {'lol': True}), ('hello', 42), ('ponies', 'are pretty!')]
unmunchify will handle intermediary dicts, lists and tuples (as well as
their subclasses), but ymmv on custom datatypes.
>>> b = Munch(foo=['bar', Munch(lol=True)], hello=42,
... ponies=('are pretty!', Munch(lies='are trouble!')))
>>> sorted(unmunchify(b).items()) #doctest: +NORMALIZE_WHITESPACE
[('foo', ['bar', {'lol': True}]), ('hello', 42), ('ponies', ('are pretty!', {'lies': 'are trouble!'}))]
nb. As dicts are not hashable, they cannot be nested in sets/frozensets.
"""
# Munchify x, using `seen` to track object cycles
seen = dict()
def unmunchify_cycles(obj):
# If we've already begun unmunchifying obj, just return the already-created unmunchified obj
try:
return seen[id(obj)]
except KeyError:
pass
# Otherwise, first partly unmunchify obj (but without descending into any lists or dicts) and save that
seen[id(obj)] = partial = pre_unmunchify(obj)
# Then finish unmunchifying lists and dicts inside obj (reusing unmunchified obj if cycles are encountered)
return post_unmunchify(partial, obj)
def pre_unmunchify(obj):
# Here we return a skeleton of unmunchified obj, which is enough to save for later (in case
# we need to break cycles) but it needs to filled out in post_unmunchify
if isinstance(obj, Mapping):
return dict()
elif isinstance(obj, list):
return type(obj)()
elif isinstance(obj, tuple):
type_factory = getattr(obj, "_make", type(obj))
return type_factory(unmunchify_cycles(item) for item in obj)
else:
return obj
def post_unmunchify(partial, obj):
# Here we finish unmunchifying the parts of obj that were deferred by pre_unmunchify because they
# might be involved in a cycle
if isinstance(obj, Mapping):
partial.update((k, unmunchify_cycles(obj[k])) for k in obj.keys())
elif isinstance(obj, list):
partial.extend(unmunchify_cycles(v) for v in obj)
elif isinstance(obj, tuple):
for (value_partial, value) in zip(partial, obj):
post_unmunchify(value_partial, value)
return partial
return unmunchify_cycles(x)
# Serialization
try:
try:
import json
except ImportError:
import simplejson as json
def toJSON(self, **options):
""" Serializes this Munch to JSON. Accepts the same keyword options as `json.dumps()`.
>>> b = Munch(foo=Munch(lol=True), hello=42, ponies='are pretty!')
>>> json.dumps(b) == b.toJSON()
True
"""
return json.dumps(self, **options)
def fromJSON(cls, stream, *args, **kwargs):
""" Deserializes JSON to Munch or any of its subclasses.
"""
factory = lambda d: cls(*(args + (d,)), **kwargs)
return munchify(json.loads(stream), factory=factory)
Munch.toJSON = toJSON
Munch.fromJSON = classmethod(fromJSON)
except ImportError:
pass

View File

@@ -0,0 +1,298 @@
"""Snuggs are s-expressions for Numpy."""
# This file is a modified version of snuggs 1.4.7. The numpy
# requirement has been removed and support for keyword arguments in
# expressions has been added.
#
# The original license follows.
#
# Copyright (c) 2014 Mapbox
#
# 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.
from collections import OrderedDict
import functools
import operator
import re
from typing import Mapping
from pyparsing import ( # type: ignore
Keyword,
oneOf,
Literal,
QuotedString,
ParseException,
Forward,
Group,
OneOrMore,
ParseResults,
Regex,
ZeroOrMore,
alphanums,
pyparsing_common,
replace_with,
)
__all__ = ["eval"]
__version__ = "1.4.7"
class Context(object):
def __init__(self):
self._data = OrderedDict()
def add(self, name, val):
self._data[name] = val
def get(self, name):
return self._data[name]
def lookup(self, index, subindex=None):
s = list(self._data.values())[int(index) - 1]
if subindex:
return s[int(subindex) - 1]
else:
return s
def clear(self):
self._data = OrderedDict()
_ctx = Context()
class ctx(object):
def __init__(self, kwd_dict=None, **kwds):
self.kwds = kwd_dict or kwds
def __enter__(self):
_ctx.clear()
for k, v in self.kwds.items():
_ctx.add(k, v)
return self
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
self.kwds = None
_ctx.clear()
class ExpressionError(SyntaxError):
"""A Snuggs-specific syntax error."""
filename = "<string>"
lineno = 1
op_map = {
"*": lambda *args: functools.reduce(lambda x, y: operator.mul(x, y), args),
"+": lambda *args: functools.reduce(lambda x, y: operator.add(x, y), args),
"/": lambda *args: functools.reduce(lambda x, y: operator.truediv(x, y), args),
"-": lambda *args: functools.reduce(lambda x, y: operator.sub(x, y), args),
"&": lambda *args: functools.reduce(lambda x, y: operator.and_(x, y), args),
"|": lambda *args: functools.reduce(lambda x, y: operator.or_(x, y), args),
"<": operator.lt,
"<=": operator.le,
"==": operator.eq,
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
"truth": operator.truth,
"is": operator.is_,
"not": operator.not_,
}
def compose(f, g):
"""Compose two functions.
compose(f, g)(x) = f(g(x)).
"""
return lambda x, *args, **kwds: f(g(x))
func_map: Mapping = {}
higher_func_map: Mapping = {
"compose": compose,
"map": map,
"partial": functools.partial,
"reduce": functools.reduce,
"attrgetter": operator.attrgetter,
"methodcaller": operator.methodcaller,
"itemgetter": operator.itemgetter,
}
nil = Keyword("null").set_parse_action(replace_with(None))
true = Keyword("true").set_parse_action(replace_with(True))
false = Keyword("false").set_parse_action(replace_with(False))
def resolve_var(source, loc, toks):
try:
return _ctx.get(toks[0])
except KeyError:
err = ExpressionError("name '{}' is not defined".format(toks[0]))
err.text = source
err.offset = loc + 1
raise err
var = pyparsing_common.identifier.set_parse_action(resolve_var)
string = QuotedString("'") | QuotedString('"')
lparen = Literal("(").suppress()
rparen = Literal(")").suppress()
op = oneOf(" ".join(op_map.keys())).set_parse_action(
lambda source, loc, toks: op_map[toks[0]]
)
def resolve_func(source, loc, toks):
try:
return func_map[toks[0]]
except (AttributeError, KeyError):
err = ExpressionError("'{}' is not a function or operator".format(toks[0]))
err.text = source
err.offset = loc + 1
raise err
# The look behind assertion is to disambiguate between functions and
# variables.
func = Regex(r"(?<=\()[{}]+".format(alphanums + "_")).set_parse_action(resolve_func)
higher_func = oneOf(" ".join(higher_func_map.keys())).set_parse_action(
lambda source, loc, toks: higher_func_map[toks[0]]
)
func_expr = Forward()
higher_func_expr = Forward()
expr = higher_func_expr | func_expr
class KeywordArg:
def __init__(self, name):
self.name = name
kwarg = Regex(r":[{}]+".format(alphanums + "_")).set_parse_action(
lambda source, loc, toks: KeywordArg(toks[0][1:])
)
operand = (
higher_func_expr
| func_expr
| true
| false
| nil
| var
| kwarg
| pyparsing_common.sci_real
| pyparsing_common.real
| pyparsing_common.signed_integer
| string
)
func_expr << Group(
lparen + (higher_func_expr | op | func) + OneOrMore(operand) + rparen
)
higher_func_expr << Group(
lparen
+ higher_func
+ (nil | higher_func_expr | op | func | OneOrMore(operand))
+ ZeroOrMore(operand)
+ rparen
)
def processArg(arg):
if isinstance(arg, ParseResults):
return processList(arg)
else:
return arg
def processList(lst):
items = [processArg(x) for x in lst[1:]]
args = []
kwds = {}
# An iterator is used instead of implicit iteration to allow
# skipping ahead in the keyword argument case.
itemitr = iter(items)
for item in itemitr:
if isinstance(item, KeywordArg):
# The next item after the keyword arg marker is its value.
# This advances the iterator in a way that is compatible
# with the for loop.
val = next(itemitr)
key = item.name
kwds[key] = val
else:
args.append(item)
func = processArg(lst[0])
# list and tuple are two builtins that take a single argument,
# whereas args is a list. On a KeyError, the call is retried
# without arg unpacking.
try:
return func(*args, **kwds)
except TypeError:
return func(args, **kwds)
def handleLine(line):
try:
result = expr.parseString(line)
return processList(result[0])
except ParseException as exc:
text = str(exc)
m = re.search(r"(Expected .+) \(at char (\d+)\), \(line:(\d+)", text)
msg = m.group(1)
if "map|partial" in msg:
msg = "expected a function or operator"
err = ExpressionError(msg)
err.text = line
err.offset = int(m.group(2)) + 1
raise err
def eval(source, kwd_dict=None, **kwds):
"""Evaluate a snuggs expression.
Parameters
----------
source : str
Expression source.
kwd_dict : dict
A dict of items that form the evaluation context. Deprecated.
kwds : dict
A dict of items that form the valuation context.
Returns
-------
object
"""
kwd_dict = kwd_dict or kwds
with ctx(kwd_dict):
return handleLine(source)