amethyst.core.util

cached_property([meth, name, delegate]) Lazy Attribute Memoization
coalesce(*args) Returns first argument which is not None.
dict_of(conv[, key_conv, set_key, package, …]) An amethyst.core.obj.Attr helper function which will validate a dict of values and optionally keys.
get_class(name[, package, frame]) Load a class (or function or other package attribute) from a string name.
identity(x) The identity function, returns its (single) argument
list_of(conv[, container, package, frame]) An amethyst.core.obj.Attr helper function which will validate a list of values.
set_of(conv[, package, frame]) Like list_of(), but uses a set container rather than a list container.
smartmatch(val, other) Smart match against a value
tupley(thingun) Make sure thingun is like a tuple - a list, set, tuple.
amethyst.core.util.identity(x)

The identity function, returns its (single) argument

amethyst.core.util.list_of(conv, container=<class 'list'>, package=None, frame=1)

An amethyst.core.obj.Attr helper function which will validate a list of values. Sample usage:

class MyObject(Object):
    foo = Attr(list_of(float))

obj = MyObject(foo="23")
print(obj.foo)            # [ 23 ]        -  a list with an int

obj.foo = (1, 2, "23")
print(obj.foo)            # [ 1, 2, 23 ]  -  a list not a tuple
Parameters:conv – The conversion function or class. If a class, objects in

the list which are not already objects of this class will be inflated using the class. If passed a string, it will be converted to a class (or function) using the get_class() function.

Parameters:container – Constructor which can take a generator and return

your desired list-like object. For instance, the set_of() function passes container=set.

Parameters:package – String or package object to use as the base for

relative imports. When specified, is passed unmodified to get_class().

Parameters:frame – Frame depth, as described in get_class().
amethyst.core.util.set_of(conv, package=None, frame=1)

Like list_of(), but uses a set container rather than a list container.

amethyst.core.util.dict_of(conv, key_conv=<function identity>, set_key=None, package=None, frame=1)

An amethyst.core.obj.Attr helper function which will validate a dict of values and optionally keys. Sample usage:

class MyObject(Object):
    name = Attr()
    foo = Attr(dict_of("MyObject"))

obj1 = MyObject(name="Alice")
obj2 = MyObject(foo={ "a": obj1, "b": dict(name="Bob") })

In the example, obj2.foo will be a dictionary with two items. Both values will be MyObject objects, the “b” item having been auto-inflated.

Warning

The produced attribute value is a normal python dict. Automatic inflation only occurs when initially setting the attribute. Normal accesses to the attribute dictionary will not validate or auto-inflate. For instance, obj2.foo["c"] = dict(name="Carol") will store a python dict to key “c”, not a MyObject.

Parameters:conv – The conversion function or class. If a class, values in

the dict which are not already objects of this class will be inflated using the class. If passed a string, it will be converted to a class (or function) using the get_class() function.

Parameters:key_conv – Conversion function for keys, defaults to identity

function.

Parameters:set_key – Optional callable passed key name and inflated value

object. Can be used to set an attribute on the value objects based on keys. For instance, we might use set_key=lambda k, v: setattr(v, "name", v.name or k) to set default “name” attributes.

Parameters:package – String or package object to use as the base for

relative imports. When specified, is passed unmodified to get_class().

Parameters:frame – Frame depth, as described in get_class().
amethyst.core.util.get_class(name, package=None, frame=1)

Load a class (or function or other package attribute) from a string name. Automatically imports required package. Requested name may be relative. If relative and no package is passed, the call stack is examined at the frame counter and imports are relative to that package.

get_class("foo.Bar")                     # Bar class from package foo
get_class(".Foo")  or  get_class("Foo")  # Foo class from current package
get_class(".Foo", frame=2)               # Foo class from caller's package
Parameters:name (str) – A string containing a package name and class or object

name joined by a dot. The package will be loaded and the attribute will be returned. From the name of the function, the intention if for automatic loading of classes. For example, get_class(“amethyst.core.Object”), however, python doesn’t really distinguish between loading a class and loading a function or other package variable, so the object after the last dot can really be anything available in the package – even unrelated imports to the package! For this reason, if classes or packages are imported for user or configuration input, it is a good idea to verify that the imported object matches some expected base class.

Parameters:package – String or package object to use as the base for

relative imports.

Parameters:frame – Frame depth, for the default base package. When set to 1,

relative class names are looked up relative to the caller’s package. When set to a larger value, will look up relative to the caller’s caller’s … package. Set to 0 or None (or set an explicit value for package to disable automatically selecting a base package.

amethyst.core.util.tupley(thingun)

Make sure thingun is like a tuple - a list, set, tuple. If not, wraps thingun into a single-item or empty (when None) tuple.

amethyst.core.util.coalesce(*args)

Returns first argument which is not None. If no non-None argumenst, then will return None. Also returns None if argument list is empty.

amethyst.core.util.smartmatch(val, other)

Smart match against a value

Convenient function to use in attribute validation. Attempts to determine if a value is like other values. Behavior depends on type of the other object:

  • list, tuple, set, frozenset: Test membership and return the value unmodified.
  • dict: Look up the item and return the hashed value.
  • compiled regex: call other.search(val). Remember to anchor your search if that is desired!
  • callable: call other(val) and return the result
  • type, NoneType: Test val is other and, if true, return value
  • anything else: Test val == other and, if true, return value

If none of the above match, raises a ValueError

class amethyst.core.util.cached_property(meth=None, name=None, delegate=None)

Lazy Attribute Memoization

Note

functools in python 3.8 includes a cached_property decorator.

It should be used in place of this for most cases.

Creates properties with deferred calculation. Once calculated, the result is stored and returned from cache on subsequent access. Useful for expensive operations which may not be needed, or to ensure just-in-time construction (I like using this for database connections or building subwidgets in GUI classes, see examples below).

Decorator Usage (most common):

class Foo(object):
    @cached_property
    def bar(self):
        print("Computing...")
        return 42   # or expensive_calculation()

foo = Foo()

print(foo.bar)      # Computing...  42
print(foo.bar)      # 42

foo.bar = 12
print(foo.bar)      # 12

del foo.bar         # Clears the cache
print(foo.bar)      # Computing...  42

Direct use allows calculation to be closure or dynamically chosen. The bar attribute will behave the same as above:

class Foo(object):
    def __init__(self, **kwargs):
        def expensive_calculation():
            ...

        self.bar = cached_property(expensive_calculation, "bar")

Example: Automatic, thread-safe, database connections:

import threading
import sqlite3
from amethyst.core import cached_property

class MyObject(object):
    def __init__(self, **kwargs):
        self._thr_local = threading.local()

    @cached_property(delegate="_thr_local")
    def db(self):
        conn = sqlite3.connect("mydb.sqlite3")
        conn.execute("PRAGMA foreign_keys=ON")
        return conn

# obj.db will be a different connection in each thread
# and will only connect if used in the thread

Example: GUI widget building:

import wx
from amethyst.core import cached_property as widget

class SimpleWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(SimpleWindow, self).__init__(*args, **kwargs)
        self.sizer.Add(self.button1)
        self.sizer.Add(self.button_exit)

    @widget
    def sizer(self):
        widget = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(widget)
        return widget

    @widget
    def button1(self):
        widget = wx.Button(self, wx.ID_ANY, "Do Something")
        widget.Bind(wx.EVT_BUTTON, self.on_click1)
        return widget

    @widget
    def button_exit(self):
        widget = wx.Button(self, wx.ID_ANY, "Exit")
        widget.Bind(wx.EVT_BUTTON, lambda evt: wx.Exit())
        return widget

    def on_click1(self, evt):
        print("Ouch!")

class MyApp(wx.App):
    def OnInit(self):
        self.mainwindow.Show(True)
        self.SetTopWindow(self.mainwindow)
        return True

    @widget
    def mainwindow(self):
        return SimpleWindow(None, -1, "This is a test")

app = MyApp(0)
app.MainLoop()