Skip to main content

Some Python nicieties

Project description

I use the excellent Funcy library for Python a lot. This is my collection of extras that I have designed to work closely together with funcy. Funcy Kingston (Reference, see here).

Kingston is auto-formatted using yapf.

Nice things

dig()

CSS selector like deep value grabbing from almost any object.

>>> from kingston import dig
>>> dig.xget((1, 2, 3), 1)
2
>>> dig.xget({'foo': 'bar'}, 'foo')
'bar'
>>> dig.dig({'foo': 1, 'bar': [1,2,3]}, 'bar.1')
2
>>> dig.dig({'foo': 1, 'bar': [1,{'baz':'jox'},3]}, 'bar.1.baz')
'jox'
>>>

The difference between dig.dig() and funcy.get_in() is that you can use shell-like blob patterns to get several values keyed by similar names:

>>> from kingston import dig
>>> res = dig.dig({'foo': 1, 'foop': 2}, 'f*')
>>> res
[foo=1:int, foop=2:int]
>>> # (textual representation of an indexable object)
>>> res[0]
foo=1:int
>>> res[1]
foop=2:int
>>>

Pattern matching using extended dict’s

match.Match objects are callable objects using a dict semantic that also matches calls based on the type of the calling parameters:

>>> from kingston import match
>>> foo = match.Match({int: lambda x: x*100, str: lambda x: f'Hello {x}'})
>>> foo(10)
1000
>>> foo('bar')
'Hello bar'
>>>
>>> from kingston import match
>>> foo = match.Match({
...     int: lambda x: x * 100,
...     str: lambda x: f'Hello {x}',
...     (int, int): lambda a, b: a + b
... })
>>> foo(10)
1000
>>> foo('bar')
'Hello bar'
>>>
>>> foo(1, 2)
3
>>>

You can use typing.Any as a wildcard:

>>> from typing import Any
>>> from kingston import match
>>> foo = match.Match({
...     int: lambda x: x * 100,
...     str: (lambda x: f"Hello {x}"),
...     (int, Any): (lambda num, x: num * x)
... })
>>> foo(10)
1000
>>> foo('bar')
'Hello bar'
>>> foo(3, 'X')
'XXX'
>>> foo(10, 10)
100
>>>

Match by value(s)

match.VMatch will use the values of the parameters to do the same as as match.Match:

>>> from kingston import match
>>> foo = match.VMatch({'x': (lambda: 'An x!'), ('x', 'y'): (lambda x,y: 3*(x+y))})
>>> foo('x')
'An x!'
>>> foo('x', 'y')
'xyxyxy'
>>>

Same as with the type matcher above, typing.Any works as a wildcard with the value matcher as well:

>>> from kingston import match
>>> from typing import Any
>>> foo = match.VMatch({
...     'x': lambda x: 'An X!',
...     ('y', Any): lambda x, y: 3 * (x + y)
... })
>>> foo('x')
'An X!'
>>> foo('y', 'x')
'yxyxyx'
>>>

Narrowable collections

Uses indexes to narrow collections to fewer values. You can narrow by type, a predicate function or value equality. The return value is always a new Narrowable derived type from the initial value. Therefore, you can chain several narrowing operations in the same expression.

Errors raised by the narrowing predicates are considered misses.

Some examples:

Narrow by type

>>> from kingston.primitives import narrowable
>>> narrowable((1,2,3,'foo', 'bar'))[int]
(1, 2, 3)
>>>

Narrow by callable

>>> from kingston.primitives import narrowable
>>> narrowable((1, 2, 3))[lambda x: x > 1]
(2, 3)
>>>
>>> narrowable((1,2,3,'foo', 'bar'))[int]
(1, 2, 3)
>>> narrowable((1,2,3,'foo', 'bar'))[lambda x: x > 1]
(2, 3)
>>> # Note, swallows ValueError raised by 'foo' > 1 etc
>>>

Supress empty iterable objects

>>> from kingston.primitives import narrowable
>>> narrowable([[1], [2], [], []])[lambda x: x[0]]
[[1], [2]]
>>>

Narrow using exact match

>>> from kingston.primitives import narrowable
>>> narrowable((1, 2, 3, 'foo'))['foo']
('foo',)
>>>

Narrow using a regexp

>>> from kingston.primitives import narrowable
>>> import re
>>> narrowable(('foo', 'fom', 'jox', 8, 'fim'))[re.compile('fo.*').match]
('foo', 'fom')
>>>

Combine

>>> from kingston.primitives import narrowable
>>> narrowable((1,2,3,'foo', 'bar'))[str]['foo']
('foo',)
>>>

Go deeper

>>> from kingston.primitives import narrowable
>>> narrowable((1, 2, 3, (41, 42, 43)))[tuple][0][lambda x: x > 41]
(42, 43)
>>>

No matches found

If no element matches, an empty version of the collection parameter will be returned:

>>> from kingston.primitives import narrowable
>>> narrowable((1,2,3))[lambda x: x > 3]
()
>>>

Programmatic class creation

Programmatic creation of arbitrary named classes in module definition, add methods using a decorator notation:

>>> from kingston import lang
>>> mystuff = (('Foo', 1), ('Bar', 2))
>>> for name, num in mystuff: locals()[name] = lang.mkclass(name, **{'num': num})
>>> Foo
<class 'kingston.lang.Foo'>
>>> Foo.num
1
>>> \
... @Foo.classmethod
... def myclassmethod(cls, x):
...     return x + 1
>>> Foo.myclassmethod(1)
2
>>>
>>> \
... @Foo.staticmethod
... def mystaticmethod(x, y):
...     return x + y
>>> Foo.mystaticmethod(1, 2)
3
>>> \
... @Foo.method
... def mymethod(self, x):
...     self.y = self.num + x
...     return self.y
>>> foo = Foo()
>>> foo.mymethod(1)
2
>>> foo.y
2
>>>

kingston module with developer convenience tools

The kingston.microscope module contains utilities that aid development. It has to ways to inspect live objects:

  1. Via ‘AbneuYAML’

    AbneuYAML is “Almost, but not entirely unlike YAML”. Objects dumped to ‘AbneuYAML’ should be easy to get a visual overview of for humans.

    To dump any object:

    >>> from kingston import microscope
    >>> class Cls: pass
    ...
    >>> c = Cls()
    >>> c.foo, c.bar = 1, 2
    >>> c.sub = Cls()
    >>> c.sub.foo, c.sub.bar, c.sub.baz = 3, 4, [1, 2]
    >>> encoded = microscope.abneuyaml(c)
    >>> print(encoded) #doctest: +ELLIPSIS
    <__main__.Cls object at 0x...>:Cls
      foo=1:int
      bar=2:int
      sub=<__main__.Cls object at 0x...>:Cls
        foo=3:int
        bar=4:int
        baz=[1, 2]:list
    >>>
    

A simple way of creating small DSL’s using Python operator overloading.

>>> from kingston import lang
>>> \
... class PipingExample(lang.Piping):
...     def __add__(self, value) -> lang.Piping:
...         self.queue(lambda a, b: a + b, value)
...         return self
...
>>> simplest_pipe = PipingExample(10)
>>> res = simplest_pipe + 10 + 20
>>> res()
40
>>>

Mostly, you’ll want to use the pipe operator to define simple composition:

>>> from kingston import lang
>>> incr = lambda x: x + 1
>>> showr = "It is {}!".format
>>> (lang.ComposePiping(5) >> incr >> incr >> showr)()
'It is 7!'
>>>

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for kingston, version 0.6.4
Filename, size File type Python version Upload date Hashes
Filename, size kingston-0.6.4.tar.gz (17.6 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page