-
-
Notifications
You must be signed in to change notification settings - Fork 463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Global nameko config #520
Global nameko config #520
Conversation
Config made accessible outside container and workers - mainly in the service definition, e.g.: ```python from nameko.messaging import Consumer from nameko import config class Service: @consume( queue=Queue( exchange=config.MY_EXCHANGE, routing_key=config.MY_ROUTING_KEY, name=config.MY_QUEUE_NAME ), prefetch_count=config.MY_CONSUMER_PREFETCH_COUNT ) def consume(self, payload): pass ```
Example:: $ nameko run service \ --define AMQP_URI=pyamqp://guest:guest@localhost/yo \ --define YO="{'yo': True}"
Added the possibility to define config entries from console (overrides possible config file entries):
|
Remaining ChangesIn order to fully move to the global config, some additional things need to be solved, such as backward compatibility so the change will not brake 3rd-party extensions. Also pytest fixtures should keep existing config related features. Backward compatible Config on ContainerBuilt-in extensions should be changed to use from nameko import config
class Database(DependencyProvider):
def setup(self):
db_uris = config[DB_URIS_KEY]
# ... For making the changes backward compatible, the container config if accessed should raise a deprecation warning saying that the The class Database(DependencyProvider):
def setup(self):
db_uris = self.container.config[DB_URIS_KEY] # <- should still work
# ... Only issuing a warning:
Config setup helpersMainly for tests, but also for custom runners requiring in-program config setup, it would be good to have a helper function for setting up config in a context... from nameko import config_setup
with config_setup(my_config):
ServiceContainer(Service) ...rolling back config changes on the context exit. The context manager allows for sync use of multiple different configs per program which I think has practical use only in tests. The Container and RunnerOther places documented as part of Nameko API and using config are service and container runners. ServiceContainer(Service, config={})
ServiceRunner(config={}) I would propose removing the config passing completely as a breaking change: ServiceContainer(Service)
ServiceRunner() If a specific config should be used, it can be achieved by using StandaloneSame approach as with the other runners, but reusing the existing context manager in case of standalone RPC proxy. By making the from nameko.standalone.rpc import ClusterRpcProxy
config = {
'AMQP_URI': AMQP_URI # e.g. "pyamqp://guest:guest@localhost"
}
with ClusterRpcProxy(config) as cluster_rpc:
cluster_rpc.service_x.remote_method("hellø") # "hellø-x-y" If a config is passed, the proxy context is also wrapped in In case of the (so far undocumented) standalone event publisher, the dispatcher can be also made context manager with optional config: from nameko.standalone.events import event_dispatcher
config = {
'AMQP_URI': AMQP_URI # e.g. "pyamqp://guest:guest@localhost"
}
with event_dispatcher(config) as dispatcher:
dispatcher.dispatch('some_service', 'some_event', {'some': 'payload'}) or by using from nameko import config_setup
from nameko.standalone.events import event_dispatcher
with config_setup(config_cluster_foo):
dispatcher = event_dispatcher()
dispatcher.dispatch('some_service', 'some_event', {'some': 'payload'})
with config_setup(config_cluster_bar):
dispatcher = event_dispatcher()
dispatcher.dispatch('some_other_service', 'some_event', {'some': 'payload'}) or simply as as a function (in a script or when bootstrapping something more complex than a simple script): from nameko import config_setup
from nameko.standalone.events import event_dispatcher
config = {
'AMQP_URI': AMQP_URI # e.g. "pyamqp://guest:guest@localhost"
}
config_setup(config):
dispatcher = event_dispatcher()
dispatcher.dispatch('some_service', 'some_event', {'some': 'payload'}) Maybe all three should work. The last two should definitely work. TestingConfig FixturesExisting Usage of existing config fixtures remain the same except they will not be passed anymore to container or runner factories: def test_service_x_y_integration(runner_factory, rabbit_config):
runner = runner_factory(ServiceX, ServiceY)
runner.start()
# ... Not sure what is the most correct pytest way if a fixture is not used inside the test function, maybe the use_fixture decorator is then better: @pytest.mark.usefixtures('rabbit_config')
def test_service_x_y_integration(runner_factory):
runner = runner_factory(ServiceX, ServiceY)
runner.start()
# ... Users can define test config for their services reusing existing nameko config fixtures:: from nameko import config_setup
@pytest.fixture(autouse=True)
def config(web_config, rabbit_config):
config = {
# some testing config, defined in place or loaded from file ...
}
config.update(web_config)
config.update(rabbit_config)
with config_update(config):
yield Factory fixturesBoth @pytest.mark.usefixtures('rabbit_config')
def test_event_interface(container_factory):
container = container_factory(ServiceB)
container.start()
# ... Deprecating Config Dependency ProviderGlobal config would make the Config DD obsolete and deprecated. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an excellent suggestion and a diligent summary of all the required changes to make use of the global configuration. I am in favour of all of your suggestions.
A global config object has been requested before and I pushed back based on the importance of dependency injection in Nameko. But configuration is clearly different and this change will make it much easier to write extensions, runners and other enhancements without compromising the general principle of injecting dependencies.
Re: mutability of the config object, I don't really mind. If there is some out of the box pattern that makes immutability easy, we could use it. If it's not easy I'd prefer to leave things permissive and allow people to abuse it at their own risk.
nameko/cli/run.py
Outdated
AMQP_URI_CONFIG_KEY: args.broker | ||
} | ||
if AMQP_URI_CONFIG_KEY not in config: | ||
config.update({AMQP_URI_CONFIG_KEY: args.broker}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's deprecate the --broker
flag arg with this change. You'll be able to use --define AMQP_URI=whatever
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 deprecated - raising a warning, but still setting the broker uri.
nameko/cli/shell.py
Outdated
broker_from = "" | ||
else: | ||
broker_from = " (from --config)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could actually be from --define
now. I'm not sure this message is helpful enough to keep.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I removed it from the banner.
|
||
def _command(*argv): | ||
with patch.object(sys, 'argv', argv): | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very neat!
@iky can you give a quick update on the state of this branch? |
@mattbennett I should get back to it very very soon! |
Keep the config pproperty for backward compatibility but raise a warning if accessed. Also remove config passing from the service runner.
Config fixtures now use config_setup and config_update context managers.
Now once the test tooling is migrated we can make container and service runner tests pass
This would include connection error which is caught and wrapped into generic exception. We can get connection refused if sqs requests mock leaks to the vhost deleting green thread.
This is now ready to be reviewed. Besides adding
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Just a few small comments, then we should get this merged in 👍
I don't think they do any harm in the main namespace. There may well be non-testing usecases for them, so perhaps they should stay where they are. |
What do you think about |
... even if some options come as arguments.
Yep, agree, will change it ... |
Done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Brilliant. Thank you @iky!
A draft of config made accessible outside container and workers - also in the service
definition, e.g.:
It will also help with setting optionals by simply passing them to the constructor:
Extensions could load from config only entries necessary for them to run (for example in the Database case it would be the DB connection). Anything optional can be passed on instantiation using settings loaded from config. Also this way users are free to structure configs their way.