Skip to main content

Toolbox with useful Python classes and type magic.

Project description

nr.types

CircleCI

The nr.types package provides a number os useful data types for day-to-day Python programming. It is compatible with Python 2.7 and modern versions of Python 3.

Installation

pip install nr.types

Run Tests

pip install -e .[test]
pytest --cov=./src/nr

API

nr.types.NotSet

The NotSet singleton is useful in cases where None is an acceptable value for a parameter so there needs to be an additional state that defines the parameter as "not set".

nr.types.abc

An alias for collections.abc or collections (the six module does not provide a move for these modules).

nr.types.functools

Tools to help with Python function internals such as closures, code and function objects.

Example:
import nr.types.functools as ft
def test(value):
  def x():
    return value
  return x
x = test(42)
assert x() == 42
y = ft.copy_function(x, closure={'value': 99})
assert y() == 99

nr.types.generic

Allows you to implement generic types, ie. classes with parameters.

Example:
from nr.types import generic
class HashDict(generic.Generic['key_hash']):
  def __init__(self):
    generic.assert_initialized(self)
    self.data = {}
  def __getitem__(self, key):
    return self.data[self.key_hash(key)]
  def __setitem__(self, key, value):
    self.data[self.key_hash(key)] = value
UnsafeHashDict = HashDict[hash]

nr.types.interface

Similar to zope.interface, but Python 3 compatible and less magical.

Example:
from nr.types.interface import Interface, Implementation, implements, attr

class IFoo(Interface):
  """ The foo interface. """

  x = attr("""Some attribute.""")

  def bar(self, q, r=None):
    """ The bar function. """

assert set(IFoo) == set(['x', 'bar'])
assert not hasattr(IFoo, 'x')
assert not hasattr(IFoo, 'bar')
assert IFoo['x'].name == 'x'
assert IFoo['bar'].name == 'bar'

@implements(IFoo)
class Foo(object):

  def __init__(self, x=None):
    self.x = x

  def bar(self, q, r=None):
    return q, r, self.x

assert issubclass(Foo, Implementation)
assert IFoo.implemented_by(Foo)
assert IFoo.provided_by(Foo())
assert list(IFoo.implementations()) == [Foo]
assert Foo(42).x == 42

nr.types.maps

Provides the following mapping (and mapping-related) implementations:

  • OrderedDict
  • ObjectAsDict
  • ObjectFromDict
  • ChainDict (use maps.chain())
  • HashDict[hash_func]
  • ValueIterableDict

nr.types.meta

Provides useful metaclasses, such as InlineMetaclass.

Example:
from nr.types.meta import InlineMetaclassBase
class MyClass(InlineMetaclassBase):
  def __metainit__(self, name, bases, attr):
    print('MyClass constructed!')
    self.value = 'foo'
assert MyClass.value == 'foo'

nr.types.moduletools

Provides some tools for working with modules. Currently only provides the make_inheritable() function which can be used from within your module to make the module object itself usable as a parent class.

# myclass.py
class MyClass(object): pass
make_inheritable(__name__)

# test.py
import myclass
class MySubclass(myclass): pass
assert issubclass(MySubclass, myclass.MyClass)

nr.types.proxy

Provides the proxy class which is a wrapper for a callable. Any kind of access to the proxy object is redirected to the object returned by the callable.

Example for `proxy` class:
from nr.types import proxy

count = 0

@proxy
def auto_increment():
  global count
  count += 1
  return count

assert auto_increment == 1
assert auto_increment == 2
assert auto_increment + 10 == 13
Example for `proxy` class:
from nr.types.proxy import lazy_proxy

count = 0

@lazy_proxy
def not_incrementing():
  global count
  count += 1
  return count

assert not_incrementing == 1
assert not_incrementing == 1
assert not_incrementing == 1

nr.types.record

Similar to namedtuple but mutable, with support for keyword arguments, type declarations and default values. Supports multiple forms of declaring a record, eg. via Python 3.6+ class-level annotations, specifying a class-level __fields__ member or declaring attributes by creating record.Field() objects.

Example:
import random
from nr.types import record

class Person(record.Record):
  name: str
  mail: str = None
  age: int = lambda: random.randint(10, 50)

p = Person('John Smith')
assert p.name == 'John Smith'
assert p.mail is None
assert 10 <= p.age <= 50
Alternatives:
import random
from nr.types import record

class Person(record.Record):
  name = record.Field(str)
  mail = record.Field(str, None)
  age = record.Field(str, lambda: random.randint(10, 50))

class Person(record.Record):
  __fields__ = [
    ('name', str),
    ('mail', str, None),
    ('age', str, lambda: random.randint(10, 50)),
  ]

Person = record.create_record('Person', [
  ('name', str),
  record.Field.with_name('mail', str, None),
  ('age', str, lambda: random.randint(10, 50))
])

Person = record.create_record('Person', {
  'name': record.Field(str),
  'mail': record.Field(str, None),
  'age': record.Field(str, lambda: random.randint(10, 50))
})

assert list(Person.__fields__.keys()) == ['name', 'mail', 'age']

nr.types.sets

Currently only provides an OrderedSet implementation.

nr.types.stream

Example:
from nr.types import stream
stream(range(10)).map(lambda x: x*2)
stream.map(range(10), lambda x: x*2)

nr.types.structured

Example:
from nr.types import structured

Person = structured.ForwardDecl('Person')
People = structured.translate_field_type({Person})
class Person(structured.Object):
  name = structured.ObjectKeyField()
  age = structured.Field(int)
  numbers = structured.Field([str])

data = {
  'John': {'age': 52, 'numbers': ['+1 123 5423435']},
  'Barbara': {'age': 29, 'numbers': ['+44 1523/5325323']}
}
people = structured.extract(data, People)
assert people['John'] == Person('John', 52, ['+1 123 5423435'])
assert people['Barbara'] == Person('Barbara', 29, ['+44 1523/5325323'])

nr.types.sumtype

Example:
from nr.types import record, sumtype

class Filter(sumtype):
  # Three ways to define constructors.
  # 1)
  Date = sumtype.constructor(record.create_record('Date', 'min,max'))
  # 2)
  Keyword = sumtype.constructor('text')
  # 3)
  @sumtype.constructor
  class Duration(sumtype.record):
    value = sumtype.field(int, default=3600)
    def to_hours(self):
      return self.value / 3600.0

  # Enrich constructors with members.
  @sumtype.member_of([Date, Keyword])
  def only_on_date_or_keyword(self):
    return 'The answer is 42'

f = Filter.Keyword('building')
assert isinstance(f, Filter)
assert f.is_keyword()
assert f.text == 'building'
assert hasattr(f, 'only_on_date_or_keyword')
assert f.only_on_date_or_keyword() == 'The answer is 42'

f = Filter.Date(10, 42)
assert isinstance(f, Filter)
assert f.is_date()
assert (f.min, f.max) == (10, 42)
assert hasattr(f, 'only_on_date_or_keyword')
assert f.only_on_date_or_keyword() == 'The answer is 42'

f = Filter.Duration()
assert isinstance(f, Filter)
assert f.is_duration()
assert f.value == 3600
assert not hasattr(f, 'only_on_date_or_keyword')

f = Filter.Duration(value=4759)
assert f.value == 4759
assert f.to_hours() == (4759 / 3600.0)

Copyright © Niklas Rosenstein 2019

Project details


Download files

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

Source Distribution

nr.types-2.5.3.tar.gz (38.9 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page