Example #1
0
    def test_write_skip_aggregations(self):
        required_config = Namespace()
        required_config.add_option(
            'minimal_version_for_understanding_refusal',
            doc='ignore the Thottleable protocol',
            default={'Firefox': '3.5.4'},
        )
        required_config.add_aggregation('an_agg', lambda x, y, z: 'I refuse')

        cm = ConfigurationManager(
            required_config,
            values_source_list=[],
        )

        config = cm.get_config()

        s = StringIO()

        @contextlib.contextmanager
        def s_opener():
            yield s

        cm.write_conf('py', s_opener)
        generated_python_module_text = s.getvalue()

        expected = """# generated Python configman file



# ignore the Thottleable protocol
minimal_version_for_understanding_refusal = {
    "Firefox": "3.5.4"
}
"""
        self.assertEqual(generated_python_module_text, expected)
Example #2
0
def define_config():
    definition = Namespace()
    # here we're setting up the minimal parameters required for connecting
    # to a database.
    definition.add_option(
      name='host',
      default='localhost',
      doc='the hostname of the database',
      short_form='h'
    )
    definition.add_option(
      name='dbname',
      default='',
      doc='the name of the database',
      short_form='d'
    )
    definition.add_option(
      name='user',
      default='',
      doc='the name of the user within the database',
      short_form='u'
    )
    definition.add_option(
      name='password',
      default='',
      doc='the name of the database',
      short_form='p'
    )
    # This final aggregation object is the most interesting one.  Its final
    # value depends on the final values of options within the same Namespace.
    # After configman is done doing all its value overlays, there is
    # one final pass through the option definitions with the sole purpose of
    # expanding the Aggregations.  To do so, the Aggregations' aggregation_fn
    # is called passing the whole config set to the function.  That function
    # can then use any values within the config values to come up with its
    # own value.  In this case, the function returns a factory function that
    # return functions that return database connections wrapped in
    # contextmanagers.
    definition.add_aggregation(
      name='db_transaction',
      function=transaction_context_factory
    )
    return definition
Example #3
0
    def test_write_skip_aggregations(self):
        required_config = Namespace()
        required_config.add_option(
          'minimal_version_for_understanding_refusal',
          doc='ignore the Thottleable protocol',
          default={'Firefox': '3.5.4'},
        )
        required_config.add_aggregation(
            'an_agg',
            lambda x, y, z: 'I refuse'
        )

        cm = ConfigurationManager(
            required_config,
            values_source_list=[],
        )

        config = cm.get_config()

        s = StringIO()

        @contextlib.contextmanager
        def s_opener():
            yield s

        cm.write_conf('py', s_opener)
        generated_python_module_text = s.getvalue()

        expected = """# generated Python configman file



# ignore the Thottleable protocol
minimal_version_for_understanding_refusal = {
    "Firefox": "3.5.4"
}
"""
        self.assertEqual(generated_python_module_text, expected)
Example #4
0
def _do_main(
    initial_app,
    values_source_list=None,
    config_path=None,
    config_manager_cls=ConfigurationManager
):
    if values_source_list is None:
        values_source_list = [
            ConfigFileFutureProxy,
            environment,
            command_line
        ]


    global restart
    restart = False
    if isinstance(initial_app, basestring):
        initial_app = class_converter(initial_app)

    if config_path is None:
        default = './config'
        config_path = os.environ.get(
            'DEFAULT_SOCORRO_CONFIG_PATH',
            default
        )
        if config_path != default:
            # you tried to set it, then it must be a valid directory
            if not os.path.isdir(config_path):
                raise IOError('%s is not a valid directory' % config_path)

    # the only config parameter is a special one that refers to a class or
    # module that defines an application.  In order to qualify, a class must
    # have a constructor that accepts a DotDict derivative as the sole
    # input parameter.  It must also have a 'main' function that accepts no
    # parameters.  For a module to be acceptable, it must have a main
    # function that accepts a DotDict derivative as its input parameter.
    app_definition = Namespace()
    app_definition.add_option(
        'application',
        doc='the fully qualified module or class of the application',
        default=initial_app,
        from_string_converter=class_converter
    )
    try:
        app_name = initial_app.app_name  # this will be used as the default
                                         # b
        app_version = initial_app.app_version
        app_description = initial_app.app_description
    except AttributeError as x:
        raise AppDetailMissingError(x)

    app_definition.add_aggregation(
        'logger',
        functools.partial(setup_logger, app_name)
    )

    definitions = (
        app_definition,
        logging_required_config(app_name)
    )

    config_manager = config_manager_cls(
        definitions,
        app_name=app_name,
        app_version=app_version,
        app_description=app_description,
        values_source_list=values_source_list,
        config_pathname=config_path
    )

    def fix_exit_code(code):
        # some apps don't return a code so you might get None
        # which isn't good enough to send to sys.exit()
        if code is None:
            return 0
        return code

    with config_manager.context() as config:
        #config.logger.config = config
        config.executor_identity = lambda: threading.currentThread().getName()
        config_manager.log_config(config.logger)

        # install the signal handler for SIGHUP to be the action defined in
        # 'respond_to_SIGHUP'
        respond_to_SIGHUP_with_logging = functools.partial(
            respond_to_SIGHUP,
            logger=config.logger
        )
        signal.signal(signal.SIGHUP, respond_to_SIGHUP_with_logging)


        # get the app class from configman.  Why bother since we have it aleady
        # with the 'initial_app' name?  In most cases initial_app == app,
        # it might not always be that way.  The user always has the ability
        # to specify on the command line a new app class that will override
        # 'initial_app'.
        app = config.application

        if isinstance(app, type):
            # invocation of the app if the app_object was a class
            instance = app(config)
            instance.config_manager = config_manager
            return_code = fix_exit_code(instance.main())
        elif inspect.ismodule(app):
            # invocation of the app if the app_object was a module
            return_code = fix_exit_code(app.main(config))
        elif inspect.isfunction(app):
            # invocation of the app if the app_object was a function
            return_code = fix_exit_code(app(config))
        config.logger.info('done.')
        return return_code

    raise NotImplementedError("The app did not have a callable main function")
Example #5
0
if __name__ == "__main__":

    definition_source = Namespace()
    # setup the option that will specify which database connection/transaction
    # factory will be used.  Condfig man will query the class for additional
    # config options for the database connection parameters.
    definition_source.add_option('database',
                                 default=PGTransaction,
                                 doc='the database connection source',
                                 short_form='d')
    # this Aggregation will actually instatiate the class in the preceding
    # option called 'database'.  Once instantiated, it will be available as
    # 'db_transaction'.  It will then be used as a source of database
    # connections cloaked as a context.
    definition_source.add_aggregation(name='db_transaction',
                                      function=transaction_factory)

    c = ConfigurationManager(definition_source,
                             app_name='demo4',
                             app_description=__doc__)
    with c.context() as config:
        print "\n**** First query will succeed"
        with config.db_transaction() as trans:
            trans.query('select * from life')
            trans.commit()

        print "\n**** Second query will fail"
        with config.db_transaction() as trans:
            trans.query('select * from life')

        print "\n**** about to leave the config context"
Example #6
0
def _do_main(initial_app,
             values_source_list=None,
             config_path=None,
             config_manager_cls=ConfigurationManager):
    if values_source_list is None:
        values_source_list = [ConfigFileFutureProxy, environment, command_line]

    global restart
    restart = False
    if isinstance(initial_app, basestring):
        initial_app = class_converter(initial_app)

    if config_path is None:
        default = './config'
        config_path = os.environ.get('DEFAULT_SOCORRO_CONFIG_PATH', default)
        if config_path != default:
            # you tried to set it, then it must be a valid directory
            if not os.path.isdir(config_path):
                raise IOError('%s is not a valid directory' % config_path)

    # the only config parameter is a special one that refers to a class or
    # module that defines an application.  In order to qualify, a class must
    # have a constructor that accepts a DotDict derivative as the sole
    # input parameter.  It must also have a 'main' function that accepts no
    # parameters.  For a module to be acceptable, it must have a main
    # function that accepts a DotDict derivative as its input parameter.
    app_definition = Namespace()
    app_definition.add_option(
        'application',
        doc='the fully qualified module or class of the application',
        default=initial_app,
        from_string_converter=class_converter)
    try:
        app_name = initial_app.app_name  # this will be used as the default
        # b
        app_version = initial_app.app_version
        app_description = initial_app.app_description
    except AttributeError as x:
        raise AppDetailMissingError(x)

    app_definition.add_aggregation('logger',
                                   functools.partial(setup_logger, app_name))

    definitions = (app_definition, logging_required_config(app_name))

    config_manager = config_manager_cls(definitions,
                                        app_name=app_name,
                                        app_version=app_version,
                                        app_description=app_description,
                                        values_source_list=values_source_list,
                                        config_pathname=config_path)

    def fix_exit_code(code):
        # some apps don't return a code so you might get None
        # which isn't good enough to send to sys.exit()
        if code is None:
            return 0
        return code

    with config_manager.context() as config:
        #config.logger.config = config
        config.executor_identity = lambda: threading.currentThread().getName()
        config_manager.log_config(config.logger)

        # install the signal handler for SIGHUP to be the action defined in
        # 'respond_to_SIGHUP'
        respond_to_SIGHUP_with_logging = functools.partial(
            respond_to_SIGHUP, logger=config.logger)
        signal.signal(signal.SIGHUP, respond_to_SIGHUP_with_logging)

        # get the app class from configman.  Why bother since we have it aleady
        # with the 'initial_app' name?  In most cases initial_app == app,
        # it might not always be that way.  The user always has the ability
        # to specify on the command line a new app class that will override
        # 'initial_app'.
        app = config.application

        if isinstance(app, type):
            # invocation of the app if the app_object was a class
            instance = app(config)
            instance.config_manager = config_manager
            return_code = fix_exit_code(instance.main())
        elif inspect.ismodule(app):
            # invocation of the app if the app_object was a module
            return_code = fix_exit_code(app.main(config))
        elif inspect.isfunction(app):
            # invocation of the app if the app_object was a function
            return_code = fix_exit_code(app(config))
        config.logger.info('done.')
        return return_code

    raise NotImplementedError("The app did not have a callable main function")
Example #7
0
required_config.add_option(
    'weather_underground_api_key',
    doc='the api key to access Weather Underground data',
    short_form="K",
    default="WEATHER UNDERGROUND ACCESS KEY")
required_config.add_option(
    'state_code',
    doc='the two letter state code',
    default="OR",
)
required_config.add_option(
    'city_name',
    doc='the name of the city',
    default="Waldport",
)
required_config.add_aggregation('target_url', function=create_url)
required_config.add_option(
    'seconds_for_timeout',
    doc='the number of seconds to allow for fetching weather data',
    default=10)
required_config.add_option(
    'things_gateway_auth_key',
    doc='the api key to access the Things Gateway',
    short_form="G",
    default='THINGS GATEWAY AUTH KEY',
)
required_config.add_option('thing_id',
                           doc='the id of the color bulb to control',
                           default="TIDE LIGHT THING ID")
required_config.update(logging_config)
Example #8
0
if __name__ == "__main__":

    definition_source = Namespace()
    # setup the option that will specify which database connection/transaction
    # factory will be used.  Condfig man will query the class for additional
    # config options for the database connection parameters.
    definition_source.add_option('database',
                                 default=PGTransaction,
                                 doc='the database connection source',
                                 short_form='d')
    # this Aggregation will actually instatiate the class in the preceding
    # option called 'database'.  Once instantiated, it will be available as
    # 'db_transaction'.  It will then be used as a source of database
    # connections cloaked as a context.
    definition_source.add_aggregation(
        name='db_transaction',
        function=transaction_factory
    )

    c = ConfigurationManager(definition_source,
                             app_name='demo4',
                             app_description=__doc__)
    with c.context() as config:
        print "\n**** First query will succeed"
        with config.db_transaction() as trans:
            trans.query('select * from life')
            trans.commit()

        print "\n**** Second query will fail"
        with config.db_transaction() as trans:
            trans.query('select * from life')
Example #9
0
)
tide_light_config.add_option('thing_id',
                             doc='the id of the color bulb to control',
                             default="TIDE LIGHT THING ID")


# this defines a constant based solely on configuration data
def create_url(config, local_namespace, args):
    """generate a URL to fetch local weather data from Weather Underground using
    configuration data"""
    return "http://api.wunderground.com/api/{}/tide/q/{}/{}.json".format(
        local_namespace.weather_underground_api_key,
        local_namespace.state_code, local_namespace.city_name)


tide_light_config.add_aggregation('target_url', function=create_url)

# these are the common configuration parameters that will be used for all tide
# lights.
base_required_config = Namespace()
base_required_config.add_option(
    'weather_underground_api_key',
    doc='the api key to access Weather Underground data',
    short_form="K",
    default="WEATHER UNDERGROUND ACCESS KEY")
base_required_config.add_option(
    'things_gateway_auth_key',
    doc='the api key to access the Things Gateway',
    short_form="G",
    default='THINGS GATEWAY AUTH KEY',
)