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
anddelete
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:'#
- 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. Seezope.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"
orb"image/png"
. Defaultb"text/plain"
.charset –
Set to
b"utf-8"
if you want the data URL to contain ab"charset=utf-8"
component. Defaultb'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. (Seenti.property.dataurl
) To store them efficiently, we transform them into a Blob-basedzope.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 thefile_attr_name
.
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
- byte-size
- dotted-name
ZConfig.datatypes.DottedNameConversion
- dotted-suffix
ZConfig.datatypes.DottedNameSuffixConversion
- duration
- 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+
- float0
- 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+
- integer0
- ipaddr-or-hostname
ZConfig.datatypes.IpaddrOrHostname
- locale
- 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
- 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.