class SettingsWithDefaults(Settings): schema = { 'complex_property': fields.Dictionary({ 'string_property': fields.UnicodeString(), 'int_property': fields.Integer(), 'kwargs': fields.Dictionary({ 'foo': fields.Integer(), 'bar': fields.UnicodeString(), }), }), 'simple_property': fields.Integer(), } defaults = { 'simple_property': 0, 'complex_property': { 'string_property': 'default_string', 'kwargs': { 'foo': 1, }, }, }
class SettingsTwo(Settings): schema = { 'bar': fields.Integer(), 'baz': fields.Dictionary( { 'inner_foo': fields.UnicodeString(), 'inner_bar': fields.Boolean(), 'inner_baz': fields.List(fields.Integer()), 'inner_qux': fields.Dictionary( { 'most_inner_foo': fields.Boolean(), 'most_inner_bar': fields.UnicodeString(), }, ), }, ), } # type: SettingsSchema defaults = { 'bar': 1, 'baz': { 'inner_foo': 'Default inner', 'inner_qux': { 'most_inner_bar': 'Default most inner' } }, } # type: SettingsData
class MockingTestAction(Action): request_schema = fields.Dictionary({ 'min': fields.Integer(), 'max': fields.Integer(), 'kwargs': fields.SchemalessDictionary(key_type=fields.UnicodeString()), }) response_schema = fields.Dictionary({ 'random': fields.Integer(), 'response': fields.SchemalessDictionary(), 'extra': fields.UnicodeString(), }) def run(self, request): try: # noinspection PyUnresolvedReferences return { 'random': random.randint(request.body['min'], request.body['max']), 'response': function_which_shall_be_mocked( request.body['max'], request.body['min'], **request.body['kwargs'] ), 'extra': function_which_shall_be_mocked.extra.value().for_me, } except AttributeError: raise ActionError(errors=[Error('ATTRIBUTE_ERROR', 'An attribute error was raised')]) except BytesWarning: raise ActionError(errors=[Error('BYTES_WARNING', 'A bytes warning was raised')]) except ExpectedException: raise ActionError(errors=[Error('EXPECTED_EXCEPTION', 'An expected exception was raised')])
class SettingsToTest(settings.Settings): schema: settings.SettingsSchema = { 'one': fields.Dictionary({ 'a': fields.ClassConfigurationSchema(base_class=ClassUsingAttrs27HintsToTest, description='Nifty schema.'), 'b': fields.PythonPath(value_schema=fields.UnicodeString(), description='Must be a path, yo.'), 'c': fields.TypeReference(base_classes=ClassHoldingSigsToTest, description='Refer to that thing!'), }), 'two': fields.SchemalessDictionary(key_type=fields.UnicodeString(), value_type=fields.Boolean()), 'three': fields.List(fields.Integer()), 'four': fields.Nullable(fields.Set(fields.ByteString())), 'five': fields.Any(fields.Integer(), fields.Float()), 'six': fields.ObjectInstance(valid_type=ClassUsingAttrs27HintsToTest, description='Y u no instance?'), 'seven': fields.Polymorph( 'thing', { 'thing1': fields.Dictionary({'z': fields.Boolean()}, allow_extra_keys=True), 'thing2': fields.Dictionary({'y': fields.Boolean()}, allow_extra_keys=True, optional_keys=('y', )), }, ), } defaults: settings.SettingsData = { 'one': { 'b': 'foo.bar:Class', }, 'three': [1, 5, 7], }
def test_schema_correct(self): assert SettingsTwoFour.schema == { 'bar': fields.Integer(), 'baz': fields.Dictionary( { 'inner_foo': fields.UnicodeString(), 'inner_bar': fields.Boolean(), 'inner_baz': fields.List(fields.Integer()), 'inner_qux': fields.Dictionary( { 'most_inner_foo': fields.Boolean(), 'most_inner_bar': fields.UnicodeString(), }, ), }, ), 'qux': fields.Decimal(), 'new': fields.ByteString(), 'old': fields.UnicodeString(), }
class SettingsWithSimpleSchema(Settings): schema = { 'required_property': fields.Integer(), 'property_with_default': fields.Integer(), } defaults = { 'property_with_default': 0, }
class RootAction(Action): request_schema = fields.Dictionary( {'number': fields.Integer(), 'base': fields.Integer()}, optional_keys=('base', ), ) response_schema = fields.Dictionary({'number_root': fields.Integer()}) def run(self, request): base = request.body.get('base', 2) return {'number_root': int(round(request.body['number'] ** (1 / float(base))))}
class Http2TransportSchema(fields.Dictionary): contents = { 'backend_layer_kwargs': fields.Dictionary( { 'http_host': fields.UnicodeString(), 'http_port': fields.UnicodeString(), }, optional_keys=(), allow_extra_keys=False, ), 'backend_type': fields.Constant( *HTTP2_BACKEND_TYPES, description= 'Which backend (hyper-h2 or twisted) should be used for this Http2 transport' ), 'message_expiry_in_seconds': fields.Integer( description= 'How long after a message is sent that it is considered expired, dropped from queue', ), 'queue_capacity': fields.Integer( description= 'The capacity of the message queue to which this transport will send messages', ), 'queue_full_retries': fields.Integer( description= 'How many times to retry sending a message to a full queue before giving up', ), 'receive_timeout_in_seconds': fields.Integer( description='How long to block waiting on a message to be received', ), 'default_serializer_config': fields.ClassConfigurationSchema( base_class=BaseSerializer, description= 'The configuration for the serializer this transport should use.', ), } optional_keys = ( 'backend_layer_kwargs', 'message_expiry_in_seconds', 'queue_capacity', 'queue_full_retries', 'receive_timeout_in_seconds', 'default_serializer_config', ) description = 'The constructor kwargs for the Http2 client and server transports.'
class ServerSettings(SOASettings): """ Settings specific to servers """ schema = { 'transport': BasicClassSchema(BaseServerTransport), 'middleware': fields.List(BasicClassSchema(ServerMiddleware)), 'client_routing': fields.SchemalessDictionary(), 'logging': fields.SchemalessDictionary(), 'harakiri': fields.Dictionary({ 'timeout': fields.Integer( gte=0 ), # seconds of inactivity before harakiri is triggered, 0 to disable 'shutdown_grace': fields.Integer( gte=0 ), # seconds to gracefully shutdown after harakiri is triggered }), } defaults = { 'client_routing': {}, 'logging': { 'version': 1, 'formatters': { 'console': { 'format': '%(asctime)s %(levelname)7s: %(message)s' }, }, 'handlers': { 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'console', }, }, 'root': { 'handlers': ['console'], 'level': 'INFO', }, }, 'harakiri': { 'timeout': 300, 'shutdown_grace': 30, }, }
class MySettings(SettingsWithDefaults): schema = { 'complex_property': fields.Dictionary({ 'another_property': fields.Integer(), }), }
def test_schema_correct(self): assert SettingsThreeTwoOne.schema == { 'foo': fields.UnicodeString(), 'bar': fields.Integer(), 'baz': fields.List(fields.Float()), 'qux': fields.Float(), }
class TypesEchoAction(Action): request_schema = fields.Dictionary( { 'an_int': fields.Integer(), 'a_float': fields.Float(), 'a_bool': fields.Boolean(), 'a_bytes': fields.ByteString(), 'a_string': fields.UnicodeString(), 'a_datetime': fields.DateTime(), 'a_date': fields.Date(), 'a_time': fields.Time(), 'a_list': fields.List(fields.Anything(), max_length=0), 'a_dict': fields.Nullable(fields.Dictionary({})), }, optional_keys=( 'an_int', 'a_float', 'a_bool', 'a_bytes', 'a_string', 'a_datetime', 'a_date', 'a_time', 'a_list', 'a_dict', ), ) response_schema = fields.Dictionary( {'r_{}'.format(k): v for k, v in six.iteritems(request_schema.contents)}, optional_keys=('r_{}'.format(k) for k in request_schema.optional_keys), ) def run(self, request): return {'r_{}'.format(k): v for k, v in six.iteritems(request.body)}
def test_schema_correct(self): assert SettingsOneTwoThree.schema == { 'foo': fields.UnicodeString(), 'bar': fields.Boolean(), 'baz': fields.Dictionary( { 'inner_foo': fields.UnicodeString(), 'inner_bar': fields.Boolean(), 'inner_baz': fields.List(fields.Integer()), 'inner_qux': fields.Dictionary( { 'most_inner_foo': fields.Boolean(), 'most_inner_bar': fields.UnicodeString(), }, ), }, ), 'qux': fields.Float(), }
def test_schema_correct(self): assert SettingsTwoFourWithOverrides.schema == { 'bar': fields.Integer(), 'baz': fields.ByteString(), 'qux': fields.Decimal(), 'new': fields.ByteString(), 'old': fields.UnicodeString(), }
class HelloAction(Action): request_schema = fields.Dictionary( { 'name': fields.UnicodeString(), 'optional': fields.Integer(), 'errors': fields.Integer() }, optional_keys=('optional', 'errors')) def run(self, request): if request.body.get('errors') == 1: raise ActionError([Error('FOO', 'Foo error')]) if request.body.get('errors') == 2: raise ActionError( [Error('BAZ', 'Baz error'), Error('QUX', 'Qux error')]) return {'salutation': 'Hello, {}'.format(request.body['name'])}
class WalkAction(Action): request_schema = fields.Dictionary({'value': fields.Any(fields.Integer(), fields.Float())}) response_schema = request_schema add = 1 def run(self, request): return {'value': request.body['value'] + self.add}
class ClientSettings(SOASettings): """Generic settings for a Client.""" schema = { 'middleware': fields.List(BasicClassSchema(ClientMiddleware)), 'transport': BasicClassSchema(BaseClientTransport), 'transport_cache_time_in_seconds': fields.Integer( gte=0, description='If enabled, uses a per-service transport cache that is keyed off the service name and ' 'transport settings, persists across all clients in memory, and expires after this number of ' 'seconds. By default, a new transport is created for every new client.', ), } defaults = { 'transport_cache_time_in_seconds': 0, }
def __init__(self, valid_currencies=None, gt=None, gte=None, lt=None, lte=None, *args, **kwargs): super(AmountDictionary, self).__init__( { 'currency': fields.Constant( *(valid_currencies or currint.currencies.keys())), 'value': fields.Integer(gt=gt, gte=gte, lt=lt, lte=lte), }, *args, **kwargs)
class EchoAction(Action): request_schema = fields.SchemalessDictionary() response_schema = fields.Dictionary( { 'request_body': fields.SchemalessDictionary(), 'request_context': fields.SchemalessDictionary(), 'request_switches': fields.List(fields.Integer()), 'request_control': fields.SchemalessDictionary(), }, ) def run(self, request): return { 'request_body': request.body, 'request_context': request.context, 'request_switches': sorted(list(request.switches)), 'request_control': request.control, }
future.add_done_callback(callback) def test_join_stops_and_closes_loop(self): self.thread.join() assert not self.thread.loop.is_running() assert self.thread.loop.is_closed() before_call_trace = [] # type: List[str] create_call_trace = [] # type: List[str] run_call_trace_pre = [] # type: List[str] run_call_trace_post = [] # type: List[str] @fields.ClassConfigurationSchema.provider( fields.Dictionary({'var_value': fields.Integer()}), ) class SpecialCoroutineMiddleware(CoroutineMiddleware): def __init__(self, var_value: int): self.var_value = var_value def before_run_coroutine(self): before_call_trace.append('SpecialCoroutineMiddleware') def coroutine(self, coroutine): create_call_trace.append('SpecialCoroutineMiddleware') # noinspection PyCompatibility async def wrapper(): var = contextvars.ContextVar( 'middleware_var') # type: contextvars.ContextVar[int] var.set(self.var_value)
from pysoa.common.transport.errors import ( MessageReceiveTimeout, TransientPySOATransportError, ) from pysoa.common.transport.redis_gateway.backend.base import BaseRedisClient from pysoa.common.transport.redis_gateway.constants import ProtocolVersion from pysoa.common.transport.redis_gateway.core import RedisTransportClientCore from pysoa.common.transport.redis_gateway.settings import RedisTransportSchema from pysoa.common.transport.redis_gateway.utils import make_redis_queue_name @fields.ClassConfigurationSchema.provider(RedisTransportSchema().extend( contents={ 'protocol_version': fields.Any( fields.Integer(), fields.ObjectInstance(valid_type=ProtocolVersion), description= 'The default protocol version between clients and servers was Version 1 prior to PySOA ' '0.67.0, Version 2 as of 0.67.0, and will be Version 3 as of 1.0.0. The server can only ' 'detect what protocol the client is speaking and respond with the same protocol. However, ' 'the client cannot pre-determine what protocol the server is speaking. So, if you need to ' 'differ from the default (currently Version 2), use this setting to tell the client which ' 'protocol to speak.', ), }, optional_keys=('protocol_version', ), description='The constructor kwargs for the Redis client transport.', )) class RedisClientTransport(ClientTransport): def __init__(self, service_name, metrics, **kwargs):
class ServerSettings(SOASettings): """ Settings specific to servers """ schema = { 'transport': BasicClassSchema(BaseServerTransport), 'middleware': fields.List( BasicClassSchema(ServerMiddleware), description= 'The list of all `ServerMiddleware` objects that should be applied to requests processed by ' 'this server', ), 'client_routing': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.SchemalessDictionary(), description= 'Client settings for sending requests to other services; keys should be service names, and ' 'values should be the corresponding configuration dicts, which will be validated using the ' 'PolymorphicClientSettings schema', ), 'logging': fields.Dictionary( { 'version': fields.Integer(gte=1, lte=1), 'formatters': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary( { 'format': fields.UnicodeString(), 'datefmt': fields.UnicodeString(), }, optional_keys=('datefmt', ), ), ), 'filters': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary( { '()': fields.Anything( description='The optional filter class'), 'name': fields.UnicodeString( description='The optional filter name'), }, optional_keys=('()', 'name'), ), ), 'handlers': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary( { 'class': fields.UnicodeString(), 'level': fields.UnicodeString(), 'formatter': fields.UnicodeString(), 'filters': fields.List(fields.UnicodeString()), }, optional_keys=('level', 'formatter', 'filters'), allow_extra_keys=True, ), ), 'loggers': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=_logger_schema, ), 'root': _logger_schema, 'incremental': fields.Boolean(), 'disable_existing_loggers': fields.Boolean(), }, optional_keys=( 'version', 'formatters', 'filters', 'handlers', 'root', 'loggers', 'incremental', ), description= 'Settings for service logging, which should follow the standard Python logging configuration', ), 'harakiri': fields.Dictionary( { 'timeout': fields.Integer( gte=0, description= 'Seconds of inactivity before harakiri is triggered; 0 to disable, defaults to 300', ), 'shutdown_grace': fields.Integer( gt=0, description= 'Seconds to forcefully shutdown after harakiri is triggered if shutdown does not occur', ), }, description= 'Instructions for automatically terminating a server process when request processing takes ' 'longer than expected.', ), 'request_log_success_level': log_level_schema( description= 'The logging level at which full request and response contents will be logged for successful ' 'requests', ), 'request_log_error_level': log_level_schema( description= 'The logging level at which full request and response contents will be logged for requests ' 'whose responses contain errors (setting this to a more severe level than ' '`request_log_success_level` will allow you to easily filter for unsuccessful requests)', ), 'heartbeat_file': fields.Nullable( fields.UnicodeString( description= 'If specified, the server will create a heartbeat file at the specified path on startup, ' 'update the timestamp in that file after the processing of every request or every time ' 'idle operations are processed, and delete the file when the server shuts down. The file name ' 'can optionally contain the specifier {{pid}}, which will be replaced with the server process ' 'PID.', )), 'extra_fields_to_redact': fields.Set( fields.UnicodeString(), description= 'Use this field to supplement the set of fields that are automatically redacted/censored in ' 'request and response fields with additional fields that your service needs redacted.', ), } defaults = { 'client_routing': {}, 'logging': { 'version': 1, 'formatters': { 'console': { 'format': '%(asctime)s %(levelname)7s %(correlation_id)s %(request_id)s: %(message)s' }, 'syslog': { 'format': ('%(service_name)s_service: %(name)s %(levelname)s %(module)s %(process)d ' 'correlation_id %(correlation_id)s request_id %(request_id)s %(message)s' ), }, }, 'filters': { 'pysoa_logging_context_filter': { '()': 'pysoa.common.logging.PySOALogContextFilter', }, }, 'handlers': { 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'console', 'filters': ['pysoa_logging_context_filter'], }, 'syslog': { 'level': 'INFO', 'class': 'logging.handlers.SysLogHandler', 'facility': SysLogHandler.LOG_LOCAL7, 'address': ('localhost', 514), 'formatter': 'syslog', 'filters': ['pysoa_logging_context_filter'], }, }, 'loggers': {}, 'root': { 'handlers': ['console'], 'level': 'INFO', }, 'disable_existing_loggers': False, }, 'harakiri': { 'timeout': 300, 'shutdown_grace': 30, }, 'request_log_success_level': 'INFO', 'request_log_error_level': 'INFO', 'heartbeat_file': None, 'extra_fields_to_redact': set(), }
IP_HEADER_BYTES = 20 UDP_HEADER_BYTES = 8 MAX_IPV4_PACKET_SIZE_BYTES = 65535 MAX_GIG_E_MTU_BYTES = 9000 MAX_FAST_E_MTU_BYTES = 1518 MAX_IPV4_PAYLOAD_SIZE_BYTES = MAX_IPV4_PACKET_SIZE_BYTES - IP_HEADER_BYTES - UDP_HEADER_BYTES MAX_GIG_E_PAYLOAD_SIZE_BYTES = MAX_GIG_E_MTU_BYTES - IP_HEADER_BYTES - UDP_HEADER_BYTES MAX_FAST_E_PAYLOAD_SIZE_BYTES = MAX_FAST_E_MTU_BYTES - IP_HEADER_BYTES - UDP_HEADER_BYTES @fields.ClassConfigurationSchema.provider(fields.Dictionary( { 'host': fields.UnicodeString(description='The host name or IP address on which the Statsd server is listening'), 'port': fields.Integer(description='The port number on which the Statsd server is listening'), 'maximum_packet_size': fields.Integer( description='The maximum packet size to send (packets will be fragmented above this limit), defaults to ' '65000 bytes.', ), 'network_timeout': fields.Any(fields.Float(gt=0.0), fields.Integer(gt=0), description='The network timeout'), }, optional_keys=('maximum_packet_size', 'network_timeout'), )) class StatsdPublisher(MetricsPublisher): """ A publisher that emits UDP metrics packets to a Statsd consumer over a network connection. For Statsd metric type suffixes, see https://github.com/etsy/statsd/blob/master/docs/metric_types.md. """
from pysoa.common.transport.errors import ( InvalidMessageError, MessageReceiveTimeout, ) from pysoa.common.transport.redis_gateway.core import RedisTransportServerCore from pysoa.common.transport.redis_gateway.settings import RedisTransportSchema from pysoa.common.transport.redis_gateway.utils import make_redis_queue_name @fields.ClassConfigurationSchema.provider(RedisTransportSchema().extend( contents={ 'chunk_messages_larger_than_bytes': fields.Integer( description= 'If set, responses larger than this setting will be chunked and sent back to the client in ' 'pieces, to prevent blocking single-threaded Redis for long periods of time to handle large ' 'responses. When set, this value must be greater than or equal to 102400, and ' '`maximum_message_size_in_bytes` must also be set and must be at least 5 times greater than ' 'this value (because `maximum_message_size_in_bytes` is still enforced).', ), }, optional_keys=('chunk_messages_larger_than_bytes', ), description='The constructor kwargs for the Redis server transport.', )) class RedisServerTransport(ServerTransport): def __init__(self, service_name, metrics, **kwargs): # type: (six.text_type, MetricsRecorder, **Any) -> None """ In addition to the two named positional arguments, this constructor expects keyword arguments abiding by the Redis transport settings schema. :param service_name: The name of the service for which this transport will receive requests and send responses
class MySettings(SettingsWithSimpleSchema): schema = { 'another_property': fields.Integer(), }
class RedisTransportSchema(BasicClassSchema): contents = { 'path': fields.UnicodeString( description='The path to the Redis client or server transport, in the format `module.name:ClassName`', ), 'kwargs': fields.Dictionary( { 'backend_layer_kwargs': fields.Dictionary( { 'connection_kwargs': fields.SchemalessDictionary( description='The arguments used when creating all Redis connections (see Redis-Py docs)', ), 'hosts': fields.List( fields.Any( fields.Tuple(fields.UnicodeString(), fields.Integer()), fields.UnicodeString(), ), description='The list of Redis hosts, where each is a tuple of `("address", port)` or the ' 'simple string address.', ), 'redis_db': fields.Integer( description='The Redis database, a shortcut for putting this in `connection_kwargs`.', ), 'redis_port': fields.Integer( description='The port number, a shortcut for putting this on all hosts', ), 'sentinel_failover_retries': fields.Integer( description='How many times to retry (with a delay) getting a connection from the Sentinel ' 'when a master cannot be found (cluster is in the middle of a failover); ' 'should only be used for Sentinel backend type' ), 'sentinel_services': fields.List( fields.UnicodeString(), description='A list of Sentinel services (will be discovered by default); should only be ' 'used for Sentinel backend type', ), }, optional_keys=[ 'connection_kwargs', 'hosts', 'redis_db', 'redis_port', 'sentinel_failover_retries', 'sentinel_services', ], allow_extra_keys=False, description='The arguments passed to the Redis connection manager', ), 'backend_type': fields.Constant( *REDIS_BACKEND_TYPES, description='Which backend (standard or sentinel) should be used for this Redis transport' ), 'log_messages_larger_than_bytes': fields.Integer( description='By default, messages larger than 100KB that do not trigger errors (see ' '`maximum_message_size_in_bytes`) will be logged with level WARNING to a logger named ' '`pysoa.transport.oversized_message`. To disable this behavior, set this setting to ' '0. Or, you can set it to some other number to change the threshold that triggers ' 'logging.', ), 'maximum_message_size_in_bytes': fields.Integer( description='The maximum message size, in bytes, that is permitted to be transmitted over this ' 'transport (defaults to 100KB on the client and 250KB on the server)', ), 'message_expiry_in_seconds': fields.Integer( description='How long after a message is sent that it is considered expired, dropped from queue', ), 'queue_capacity': fields.Integer( description='The capacity of the message queue to which this transport will send messages', ), 'queue_full_retries': fields.Integer( description='How many times to retry sending a message to a full queue before giving up', ), 'receive_timeout_in_seconds': fields.Integer( description='How long to block waiting on a message to be received', ), 'serializer_config': BasicClassSchema( object_type=BaseSerializer, description='The configuration for the serializer this transport should use', ), }, optional_keys=[ 'backend_layer_kwargs', 'log_messages_larger_than_bytes', 'maximum_message_size_in_bytes', 'message_expiry_in_seconds', 'queue_capacity', 'queue_full_retries', 'receive_timeout_in_seconds', 'serializer_config', ], allow_extra_keys=False, ), } optional_keys = () description = 'The settings for the Redis transport'
class RedisTransportSchema(BasicClassSchema): contents = { 'path': fields.UnicodeString(), 'kwargs': fields.Dictionary( { 'backend_layer_kwargs': fields.Dictionary( { 'connection_kwargs': fields.SchemalessDictionary( description='The arguments used when creating all Redis connections (see Redis-Py docs)', ), 'hosts': fields.List( fields.Any( fields.Tuple(fields.UnicodeString(), fields.Integer()), fields.UnicodeString(), ), description='The list of Redis hosts', ), 'redis_db': fields.Integer( description='The Redis database, a shortcut for putting this in `connection_kwargs`.', ), 'redis_port': fields.Integer( description='The port number, a shortcut for putting this on all hosts', ), 'sentinel_failover_retries': fields.Integer( description='How many times to retry (with a delay) getting a connection from the Sentinel ' 'when a master cannot be found (cluster is in the middle of a failover); ' 'should only be used for Sentinel backend type' ), 'sentinel_refresh_interval': fields.Integer( description='Deprecated; unused; to be removed before final release.', ), 'sentinel_services': fields.List( fields.UnicodeString(), description='A list of Sentinel services (will be discovered by default); should only be ' 'used for Sentinel backend type', ), }, optional_keys=[ 'connection_kwargs', 'hosts', 'redis_db', 'redis_port', 'sentinel_failover_retries', 'sentinel_refresh_interval', 'sentinel_services', ], allow_extra_keys=False, description='The arguments passed to the Redis connection manager', ), 'backend_type': fields.Constant( *REDIS_BACKEND_TYPES, description='Which backend (standard or sentinel) should be used for this Redis transport' ), 'message_expiry_in_seconds': fields.Integer( description='How long after a message is sent that it is considered expired, dropped from queue', ), 'queue_capacity': fields.Integer( description='The capacity of the message queue to which this transport will send messages', ), 'queue_full_retries': fields.Integer( description='How many times to retry sending a message to a full queue before giving up', ), 'receive_timeout_in_seconds': fields.Integer( description='How long to block waiting on a message to be received', ), 'serializer_config': BasicClassSchema( object_type=BaseSerializer, description='The configuration for the serializer this transport should use', ), }, optional_keys=[ 'backend_layer_kwargs', 'message_expiry_in_seconds', 'queue_capacity', 'queue_full_retries', 'receive_timeout_in_seconds', 'serializer_config', ], allow_extra_keys=False, ), }
def test_schema_correct(self): assert SettingsTwoOneWithOverrides.schema == { 'foo': fields.UnicodeString(), 'bar': fields.Integer(), 'baz': fields.ByteString(), }
) PYTHON_LOGGER_SCHEMA = PYTHON_ROOT_LOGGER_SCHEMA.extend( contents={ 'propagate': fields.Boolean( description= 'Whether logging events handled by this logger should propagate to other loggers and/or the ' 'root logger. Defaults to `True`.'), }, optional_keys=('propagate', ), ) PYTHON_LOGGING_CONFIG_SCHEMA = fields.Dictionary( collections.OrderedDict(( ('version', fields.Integer(gte=1, lte=1)), ('formatters', fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary( { 'format': fields.UnicodeString( description='The format string for this formatter (see ' 'https://docs.python.org/3/library/logging.html#logrecord-attributes).', ), 'datefmt': fields.UnicodeString( description= 'The optional date format used when formatting dates in the log output (see ' 'https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior).',
class ServerSettings(SOASettings): """ Settings specific to servers """ schema = { 'transport': BasicClassSchema(BaseServerTransport), 'middleware': fields.List(BasicClassSchema(ServerMiddleware)), 'client_routing': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.SchemalessDictionary(), description='Client settings for sending requests to other services; keys should be service names, and ' 'values should be the corresponding configuration dicts, which will be validated using the ' 'PolymorphicClientSettings schema', ), 'logging': fields.Dictionary( { 'version': fields.Integer(gte=1, lte=1), 'formatters': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary( { 'format': fields.UnicodeString(), 'datefmt': fields.UnicodeString(), }, optional_keys=('datefmt', ), ), ), 'filters': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary({'name': fields.UnicodeString()}, optional_keys=('name', )), ), 'handlers': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=fields.Dictionary( { 'class': fields.UnicodeString(), 'level': fields.UnicodeString(), 'formatter': fields.UnicodeString(), 'filters': fields.List(fields.UnicodeString()), }, optional_keys=('level', 'formatter', 'filters'), allow_extra_keys=True, ), ), 'loggers': fields.SchemalessDictionary( key_type=fields.UnicodeString(), value_type=_logger_schema, ), 'root': _logger_schema, 'incremental': fields.Boolean(), 'disable_existing_loggers': fields.Boolean(), }, optional_keys=( 'version', 'formatters', 'filters', 'handlers', 'root', 'loggers', 'incremental', 'disable_existing_loggers', ), description='Settings for service logging, which should follow the standard Python logging configuration', ), 'harakiri': fields.Dictionary( { 'timeout': fields.Integer( gte=0, description='Seconds of inactivity before harakiri is triggered; 0 to disable, defaults to 300', ), 'shutdown_grace': fields.Integer( gt=0, description='Seconds to forcefully shutdown after harakiri is triggered if shutdown does not occur', ), }, ), } defaults = { 'client_routing': {}, 'logging': { 'version': 1, 'formatters': { 'console': { 'format': '%(asctime)s %(levelname)7s: %(message)s' }, }, 'handlers': { 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'console', }, }, 'root': { 'handlers': ['console'], 'level': 'INFO', }, }, 'harakiri': { 'timeout': 300, 'shutdown_grace': 30, }, }