Example #1
0
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
Example #3
0
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(),
     }
Example #6
0
class SettingsWithSimpleSchema(Settings):
    schema = {
        'required_property': fields.Integer(),
        'property_with_default': fields.Integer(),
    }

    defaults = {
        'property_with_default': 0,
    }
Example #7
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))))}
Example #8
0
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.'
Example #9
0
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,
        },
    }
Example #10
0
 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(),
     }
Example #12
0
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(),
     }
Example #15
0
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'])}
Example #16
0
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}
Example #17
0
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,
    }
Example #18
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)
Example #21
0
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):
Example #22
0
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(),
    }
Example #23
0

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.
    """
Example #24
0
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
Example #25
0
 class MySettings(SettingsWithSimpleSchema):
     schema = {
         'another_property': fields.Integer(),
     }
Example #26
0
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'
Example #27
0
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(),
     }
Example #29
0
)

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).',
Example #30
0
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,
        },
    }