API#

nti.property.property#

alias(prop_name, doc=None)[source]#

Returns a property that is a read/write alias for another attribute of the object.

See dict_alias().

read_alias(prop_name, doc=None)[source]#

Returns a property that is a read-only alias for another attribute of the object.

See dict_read_alias().

dict_alias(key_name, doc=None)[source]#

Returns a property that is a read/write alias for a value in the instance’s dictionary.

See alias() for a more general version; this is a speed or access optimization.

dict_read_alias(key_name, doc=None)[source]#

Returns a property that is a read-only alias for a value in the instances dictionary.

See read_alias() for a more general version; this is a speed or access optimization.

class LazyOnClass(func)[source]#

Bases: object

Like zope.cachedescriptors.property.Lazy, but when it caches, it caches on the class itself, not the instance, thus sharing the value. Thus, the value should be immutable and independent of any other state.

annotation_alias(annotation_name, annotation_property=None, default=None, delete=False, delete_quiet=True, doc=None)[source]#

Returns a property that is a read/write alias for a value stored as a zope.annotation.interface.IAnnotations.

The object itself may be adaptable to an IAnnotations, or a property of the object may be what is adaptable to the annotation. The later is intended for use in adapters when the context object is what should be adapted.

Parameters:
  • delete (bool) – If True (not the default), then the property can be used to delete the annotation.

  • delete_quiet (bool) – If True and delete is also True, then the property will ignore key errors when deleting the annotation value.

  • annotation_property (str) – If set to a string, it is this property of the object that will be adapted to IAnnotations. Most often this will be context when used inside an adapter.

nti.property.schema#

Schema fields.

class DataURI(*args, **kw)[source]#

Bases: URI

A URI field that ensures and requires its value to be a data URI. The field value is a DataURL.

Pass in field values as keyword parameters.

Generally, you want to pass either a title and description, or a doc string. If you pass no doc string, it will be computed from the title and description. If you pass a doc string that follows the Python coding style (title line separated from the body by a blank line), the title and description will be computed from the doc string. Unfortunately, the doc string must be passed as a positional argument.

Here are some examples:

>>> from zope.schema._bootstrapfields import Field
>>> f = Field()
>>> f.__doc__, str(f.title), str(f.description)
('', '', '')
>>> f = Field(title=u'sample')
>>> str(f.__doc__), str(f.title), str(f.description)
('sample', 'sample', '')
>>> f = Field(title=u'sample', description=u'blah blah\nblah')
>>> str(f.__doc__), str(f.title), str(f.description)
('sample\n\nblah blah\nblah', 'sample', 'blah blah\nblah')
DATA = 'data:'#
classmethod is_valid_data_uri(value)[source]#
fromUnicode(value)[source]#
>>> from zope.schema import Text
>>> t = Text(constraint=lambda v: 'x' in v)
>>> t.fromUnicode(b"foo x spam") 
Traceback (most recent call last):
...
zope.schema._bootstrapinterfaces.WrongType:
    ('foo x spam', <type 'unicode'>, '')
>>> result = t.fromUnicode(u"foo x spam")
>>> isinstance(result, bytes)
False
>>> str(result)
'foo x spam'
>>> t.fromUnicode(u"foo spam") 
Traceback (most recent call last):
...
zope.schema._bootstrapinterfaces.ConstraintNotSatisfied:
    (u'foo spam', '')

nti.property.dataurl#

Objects for working with the RFC2397 data URL scheme:

data:[<MIME-type>][;charset=<encoding>][;base64],<data>

The encoding is indicated by ;base64. If it’s present the data is encoded as base64. Without it the data (as a sequence of octets) is represented using ASCII encoding for octets inside the range of safe URL characters and using the standard %xx hex encoding of URLs for octets outside that range. If <MIME-type> is omitted, it defaults to text/plain;charset=US-ASCII. (As a shorthand, the type can be omitted but the charset parameter supplied.)

decode(data_url)[source]#

Decodes a data URL into raw bytes and metadata.

Parameters:

data_url – The data url string. If a mime-type definition is missing in the metadata, text/plain;charset=US-ASCII will be used as default mime-type.

Returns:

A 2-tuple: (bytes, mime_type_string) The mime_type string will not be parsed. See zope.contenttype.parse.parse() for that.

class DataURL[source]#

Bases: str

Represents a data URL with convenient access to its raw bytes and mime type.

property data#
property mimeType#
encode(raw_bytes, mime_type='text/plain', charset=<object object>, encoder='base64')[source]#

Encodes raw bytes into a data URL scheme string.

Parameters:
  • raw_bytes – Raw bytes

  • mime_type – The mime type, e.g. b"text/css" or b"image/png". Default b"text/plain".

  • charset

    Set to b"utf-8" if you want the data URL to contain a b"charset=utf-8" component. Default b'US-ASCII'. This does not mean however, that your raw_bytes will be encoded by this function. You must ensure that if you specify, b"utf-8" (or anything else) as the encoding, you have encoded your raw data appropriately.

    Note

    This function employs a heuristic to know when to default this parameter (for example, it is not used for image mime types). To be absolutely sure, set it explicitly (None always meaning not to use it).

  • encoder – The string “base64” (the default) or None. If None, the data is directly output as quoted ASCII bytes.

Returns:

Data URL byte string

nti.property.urlproperty#

Support for URLs.

class UrlProperty(data_name=None, url_attr_name=None, file_attr_name=None, use_dict=False)[source]#

Bases: object

A data descriptor like property() for storing a URL value efficiently.

This is an interesting situation because of data: URLs, which can be quite large. (See nti.property.dataurl) To store them efficiently, we transform them into a Blob-based zope.file.interfaces.IFile. This means that we need up to two dictionary entries/attributes on the instance (one to store a URL string, one to store a IFile), although they can be the same; we will take care of type checking as needed.

Commonly, to be useful, the file will need to be reachable by traversal on the instance holding the property. This object will ensure that the file is located (in the zope.location sense) as a child of the instance, and that it has a name; it is the responsibility of the instance to arrange for it to be traversable (through implementing __getitem__ or ITraversable or an adapter).

Parameters:

use_dict (bool) – If set to True, then the instance dictionary will be used explicitly for access to the url and file data. This is necessary if this property is being assigned to the same value as one of the attr names.

max_file_size = None#
ignore_url_with_missing_host = False#
reject_url_with_missing_host = False#
url_attr_name = '_url'#
file_attr_name = '_file'#
data_name = 'data'#
make_getitem()[source]#

As a convenience and to help with traversability, the results of this method can be assigned to __getitem__ (if there is no other instance of this property, or no other getitem).

Caution

The traversal key must be equal to the data_name, but the returned dictionary key is the file_attr_name.

get_file(instance)[source]#

Return the zope.file.interfaces.IFile for the instance if there is one, otherwise None.

nti.property.tunables#

Property objects providing tunable settings.

The common use of these is to define them as class variables (constants), and access them as instance variables. Just once, the value of an environment variable is read, and that becomes the value of the property when accessed as an instance variable.

For example:

>>> import os
>>> from nti.property.tunables import Tunable
>>> os.environ['NTI_PROP_TEST'] = '42'
>>> class T:
...     PROP = Tunable(default='from class', env_name='NTI_PROP_TEST')
>>> T().PROP
42
>>> os.environ['NTI_PROP_TEST'] = '43'
>>> T().PROP
42

If you don’t supply an environment variable name, one is derived from the name of the class (including module) and property.

>>> __name__ = 'nti.property.tunables'
>>> class ACls:
...     PROP = Tunable(42)
>>> ACls.PROP.env_name
'NTI_PROPERTY_TUNABLES_ACLS_PROP'

The way in which environment variables are converted to Python objects is customizable. The best way to do this is to use named implementations of IEnvironGetter. A ZCML directive provided by this package will register these with the component system; this is automatically done when including this package.

>>> from nti.property.tunables import IEnvironGetter
>>> from zope import component
>>> from zope.configuration import xmlconfig
>>> _ = xmlconfig.string('''    <configure xmlns="http://namespaces.zope.org/zope"                xmlns:nntp="http://nextthought.com/ntp/property">          <include package="nti.property" />          <nntp:registerTunables />     </configure>     ''')
>>> component.getUtility(IEnvironGetter, name='byte-size') 
<function get_byte_size_from_environ at ...>
>>> component.getUtility(IEnvironGetter, name='dotted-name') 
<EnvironGetter 'dotted-name'=<ZConfig.datatypes.DottedNameConversion object at ...>>

There is a registry of fallback names that is used if the component system is not initialized. The same names are registered in both places. Known getters are:

basic-key

ZConfig.datatypes.BasicKeyConversion

boolean

get_boolean_from_environ()

byte-size

get_byte_size_from_environ()

dotted-name

ZConfig.datatypes.DottedNameConversion

dotted-suffix

ZConfig.datatypes.DottedNameSuffixConversion

duration

get_duration_from_environ()

existing-directory

ZConfig.datatypes.existing_directory()

existing-dirpath

ZConfig.datatypes.existing_dirpath()

existing-file

ZConfig.datatypes.existing_file()

existing-path

ZConfig.datatypes.existing_path()

float

ZConfig.datatypes.float_conversion()

float+

get_positive_float_from_environ()

float0

get_non_negative_float_from_environ()

identifier

ZConfig.datatypes.IdentifierConversion

inet-address

ZConfig.datatypes.InetAddress

inet-binding-address

ZConfig.datatypes.InetAddress

inet-connection-address

ZConfig.datatypes.InetAddress

integer

ZConfig.datatypes.integer()

integer+

get_positive_integer_from_environ()

integer0

get_non_negative_integer_from_environ()

ipaddr-or-hostname

ZConfig.datatypes.IpaddrOrHostname

locale

ZConfig.datatypes.MemoizedConversion

null

ZConfig.datatypes.null_conversion()

port-number

ZConfig.datatypes.RangeCheckedConversion.__call__()

socket-address

builtins.type

socket-binding-address

builtins.type

socket-connection-address

builtins.type

string

get_string_from_environ()

string-list

ZConfig.datatypes.string_list()

time-interval

ZConfig.datatypes.SuffixMultiplier

timedelta

ZConfig.datatypes.timedelta()

New in version 2.0.0.

ENVIRON_GETTERS = {'basic-key': <EnvironGetter 'basic-key'=<ZConfig.datatypes.BasicKeyConversion object>>, 'boolean': <function get_boolean_from_environ>, 'byte-size': <function get_byte_size_from_environ>, 'dotted-name': <EnvironGetter 'dotted-name'=<ZConfig.datatypes.DottedNameConversion object>>, 'dotted-suffix': <EnvironGetter 'dotted-suffix'=<ZConfig.datatypes.DottedNameSuffixConversion object>>, 'duration': <function get_duration_from_environ>, 'existing-directory': <EnvironGetter 'existing-directory'=<function existing_directory>>, 'existing-dirpath': <EnvironGetter 'existing-dirpath'=<function existing_dirpath>>, 'existing-file': <EnvironGetter 'existing-file'=<function existing_file>>, 'existing-path': <EnvironGetter 'existing-path'=<function existing_path>>, 'float': <EnvironGetter 'float'=<function float_conversion>>, 'float+': <function get_positive_float_from_environ>, 'float0': <function get_non_negative_float_from_environ>, 'identifier': <EnvironGetter 'identifier'=<ZConfig.datatypes.IdentifierConversion object>>, 'inet-address': <EnvironGetter 'inet-address'=<ZConfig.datatypes.InetAddress object>>, 'inet-binding-address': <EnvironGetter 'inet-binding-address'=<ZConfig.datatypes.InetAddress object>>, 'inet-connection-address': <EnvironGetter 'inet-connection-address'=<ZConfig.datatypes.InetAddress object>>, 'integer': <EnvironGetter 'integer'=<function integer>>, 'integer+': <function get_positive_integer_from_environ>, 'integer0': <function get_non_negative_integer_from_environ>, 'ipaddr-or-hostname': <EnvironGetter 'ipaddr-or-hostname'=<ZConfig.datatypes.IpaddrOrHostname object>>, 'locale': <EnvironGetter 'locale'=<ZConfig.datatypes.MemoizedConversion object>>, 'null': <EnvironGetter 'null'=<function null_conversion>>, 'port-number': <EnvironGetter 'port-number'=<bound method RangeCheckedConversion.__call__ of <ZConfig.datatypes.RangeCheckedConversion object>>>, 'socket-address': <EnvironGetter 'socket-address'=<class 'ZConfig.datatypes.SocketAddress'>>, 'socket-binding-address': <EnvironGetter 'socket-binding-address'=<class 'ZConfig.datatypes.SocketBindingAddress'>>, 'socket-connection-address': <EnvironGetter 'socket-connection-address'=<class 'ZConfig.datatypes.SocketConnectionAddress'>>, 'string': <function get_string_from_environ>, 'string-list': <EnvironGetter 'string-list'=<function string_list>>, 'time-interval': <EnvironGetter 'time-interval'=<ZConfig.datatypes.SuffixMultiplier object>>, 'timedelta': <EnvironGetter 'timedelta'=<function timedelta>>}#

The mapping from string names to getter functions used when there are no components registered.

get_string_from_environ(environ_name, default, logger=None, target=None)[source]#

A getter function that returns the environment value unchanged. In particular, this does no string stripping on trimming, so whitespace is preserved.

>>> import os
>>> from nti.property.tunables import get_string_from_environ
>>> _ = os.environ.pop('RS_TEST_VAL', None)
>>> get_string_from_environ('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = ' <a string> '
>>> get_string_from_environ('RS_TEST_VAL', None)
' <a string> '
get_positive_integer_from_environ(environ_name, default, logger=None, target=None)[source]#

A getter function that returns a positive integer from the environment (positive integers are those greater than or equal to 1). Other values are ignored.

>>> import os
>>> from nti.property.tunables import get_positive_integer_from_environ as fut
>>> _ = os.environ.pop('RS_TEST_VAL', None)
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '1982'
>>> fut('RS_TEST_VAL', 42)
1982
>>> os.environ['RS_TEST_VAL'] = '1'
>>> fut('RS_TEST_VAL', 42)
1
>>> os.environ['RS_TEST_VAL'] = '0'
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '-1492'
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '<a string>'
>>> fut('RS_TEST_VAL', 42)
42
get_positive_float_from_environ(environ_name, default, logger=None, target=None)[source]#

A getter function that returns a positive decimal from the environment (positive decimals are those greater than or equal to 1). Other values are ignored.

>>> import os
>>> from nti.property.tunables import get_positive_float_from_environ as fut
>>> _ = os.environ.pop('RS_TEST_VAL', None)
>>> fut('RS_TEST_VAL', 42.0)
42.0
>>> os.environ['RS_TEST_VAL'] = '1982.0'
>>> fut('RS_TEST_VAL', 42)
1982.0
>>> os.environ['RS_TEST_VAL'] = '1'
>>> fut('RS_TEST_VAL', 42)
1.0
>>> os.environ['RS_TEST_VAL'] = '0.0'
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '-1492'
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '<a string>'
>>> fut('RS_TEST_VAL', 42)
42
get_non_negative_integer_from_environ(environ_name, default, logger=None, target=None)[source]#

A getter function that returns a non-negative integer from the environment (non-negative integers are those greater than or equal to 0). Other values are ignored.

>>> import os
>>> from nti.property.tunables import get_non_negative_integer_from_environ as fut
>>> _ = os.environ.pop('RS_TEST_VAL', None)
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '1982'
>>> fut('RS_TEST_VAL', 42)
1982
>>> os.environ['RS_TEST_VAL'] = '1'
>>> fut('RS_TEST_VAL', 42)
1
>>> os.environ['RS_TEST_VAL'] = '0'
>>> fut('RS_TEST_VAL', 42)
0
>>> os.environ['RS_TEST_VAL'] = '-1492'
>>> fut('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = '<a string>'
>>> fut('RS_TEST_VAL', 42)
42
get_non_negative_float_from_environ(environ_name, default, logger=None, target=None)[source]#
>>> import os
>>> from nti.property.tunables import get_non_negative_float_from_environ
>>> os.environ['RS_TEST_VAL'] = '2.3'
>>> get_non_negative_float_from_environ('RS_TEST_VAL', None)
2.3
>>> os.environ['RS_TEST_VAL'] = '-2.3'
>>> get_non_negative_float_from_environ('RS_TEST_VAL', 1.0)
1.0
parse_boolean(val)[source]#
>>> from nti.property.tunables import parse_boolean
>>> parse_boolean('0')
False
>>> parse_boolean('1')
True
>>> parse_boolean('yes')
True
>>> parse_boolean('no')
False
>>> parse_boolean('on')
True
>>> parse_boolean('off')
False

See also

ZConfig.datatypes.asBoolean()

get_boolean_from_environ(environ_name, default, logger=None, target=None)[source]#
>>> from nti.property.tunables import get_boolean_from_environ
>>> import os
>>> os.environ['RS_TEST_VAL'] = 'on'
>>> get_boolean_from_environ('RS_TEST_VAL', None)
True

See also

parse_boolean For accepted values.

get_duration_from_environ(environ_name, default, logger=None, target=None)[source]#

Return a floating-point number of seconds from the environment environ_name, or default.

Examples: 1.24s, 3m, 1m 3.6s:

>>> import os
>>> from nti.property.tunables import get_duration_from_environ
>>> os.environ['RS_TEST_VAL'] = '2.3'
>>> get_duration_from_environ('RS_TEST_VAL', None)
2.3
>>> os.environ['RS_TEST_VAL'] = '5.4s'
>>> get_duration_from_environ('RS_TEST_VAL', None)
5.4
>>> os.environ['RS_TEST_VAL'] = '1m 3.2s'
>>> get_duration_from_environ('RS_TEST_VAL', None)
63.2
>>> os.environ['RS_TEST_VAL'] = 'Invalid' # No time specifier
>>> get_duration_from_environ('RS_TEST_VAL', 42)
42
>>> os.environ['RS_TEST_VAL'] = 'Invalids' # The 's' time specifier
>>> get_duration_from_environ('RS_TEST_VAL', 42)
42
get_byte_size_from_environ(environ_name, default, logger=None, target=None)[source]#

Return a byte quantity from the environment variable environ_name, or default.

Values can be specified in bytes without a suffix, or with a KB, MB, or GB suffix (case and spacing insensitive).

No constraints are applied to the value by this function.

>>> import os
>>> from nti.property.tunables import get_byte_size_from_environ
>>> os.environ['RS_TEST_VAL'] = '1024'
>>> get_byte_size_from_environ('RS_TEST_VAL', None)
1024
>>> os.environ['RS_TEST_VAL'] = '1 kB'
>>> get_byte_size_from_environ('RS_TEST_VAL', None)
1024
class Tunable(default, env_name=None, getter=<function get_positive_integer_from_environ>, logger=None)[source]#

Bases: object

A non-data descriptor that either returns the default, or a value from the environment.

The value from the environment is only checked the first time the object is used. When used as a class variable, this is the first time the variable is used on any instane of the class (that is, class variable tunables only check the environment once, not per-instance).

The object has a string value useful in documentation.

Caution

Some version of Sphinx has stopped actually documenting these things for reasons I have yet to figure out, so you should list the default value in the docstring.

Instances have a value property that is set when the instance is accessed:

>>> from nti.property.tunables import Tunable
>>> import os
>>> _ = os.environ.pop('RS_TEST_VAL', None)
>>> tunable = Tunable(42, 'RS_TEST_VAL')
>>> tunable
<Default: 42 Environment Variable: 'RS_TEST_VAL'>
>>> tunable.value
42
>>> os.environ['RS_TEST_VAL'] = '12'
>>> tunable.value
42

The usual usage is as a class variable:

>>> class T:
...     PROP = Tunable(42, 'RS_TEST_VAL')
>>> T().PROP
12
>>> T.PROP
<Default: 42 Environment Variable: 'RS_TEST_VAL'>
>>> T.PROP.value
12

Many named datatypes are available:

>>> os.environ['RS_TEST_VAL'] = '1'
>>> tunable = Tunable(0, 'RS_TEST_VAL', 'boolean')
>>> tunable.value
True
>>> os.environ['RS_TEST_VAL'] = '192.168.1.1:80'
>>> tunable = Tunable(0, 'RS_TEST_VAL', 'inet-address')
>>> tunable.value
('192.168.1.1', 80)

You can supply a logger, or one will be found for you by looking for a ‘logger’ in the calling frames:

>>> from nti.property.tunables import default_logger
>>> logger = None
>>> Tunable(0, 'RS_TEST_VAL').logger is default_logger
True
>>> logger = "from parent frame"
>>> Tunable(0, 'RS_TEST_VAL').logger
'from parent frame'
>>> Tunable(0, 'RS_TEST_VAL', logger=42).logger
42
>>> class WithTunable:
...   TUNABLE = Tunable(0, 'RS_TEST_VAL')
>>> WithTunable.TUNABLE.logger
'from parent frame'

If the closest logger that we find isn’t a real logger, but we find one farther away that _is_ a real logger, we’ll use that one:

>>> import logging
>>> real_logger = logging.getLogger('real.logger')
>>> def make_class():
...    logger = real_logger
...    def do_it():
...        logger = 'not a real logger'
...        class WithTunable:
...           TUNABLE = Tunable(0, 'RS_TEST_VAL')
...        return WithTunable
...    return do_it()
>>> WithTunable = make_class()
>>> WithTunable.TUNABLE.logger is real_logger
True
Parameters:
  • env_name (str) – When an instance is used as a class variable (the usual use), an environment variable name is generated from the name of the class, the name of the module it is in, and the name of the class variable (e.g., THE_MODULE_ACLASS_AN_ATTR). This is used to override that. You must set this if using in a context outside of a class variable.

  • getter (IEnvironGetter) –

    One of the get_ family of functions from this module, or something implementing the same interface. The default is to get an integer. If you provide a string instead of a callable object, a utility providing that interface and having that name will be searched for; as a fallback, the hard-coded list of utilities in this module will be used.

    All of the named datatypes supported by ZConfig.datatypes are available to use as names.

  • logger – The logger used to record information about the value being used. If not given, tries to find the variable named “logger” in a calling frame that looks-like a logger.

Changed in version 2.0.2: Now searches harder up the call chain to find a logger, and accepts the first one that looks-like a logger. If no real logger can be found, then the first ‘logger’ variable we see is used.

property value#

Invoke this property if you want to get the value when accessing the variable through the class attribute instead of an instance.