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()