Parsing the configuration file

Parsing the configuration file requires three distinct parts, as shown previously in the reactivity diagram for the application:

  • Parsing the command-line arguments
  • Reading the configuration file
  • Parsing the configuration file and converting it to a Python object

Command-line arguments are retrieved with the argv driver of the cyclotron-std package, as already seen with the cyclotron echo example. The application accepts only one argument, named config. The Python standard library contains a module dedicated to the parsing of arguments: argparse. Moreover, the cyclotron-std package contains a helper to use the argparse module with an observable of arguments as an input. So, all these elements can be used as follows:

    read_config_file = (
sources.argv.argv.skip(1)
.let(argparse.argparse,
parser=Observable.just(
argparse.Parser(description="audio encode server")),
arguments=Observable.from_([
argparse.ArgumentDef(
name='--config',
help="Path of the server configuration file")
]))
.filter(lambda i: i.key == 'config')
.map(lambda i: file.Read(id='config', path=i.value))
)

Let's decompose this code line by line. The whole result of this chain is saved in the read_config_file observable variable. This observable is used as a sink for the file driver. It contains requests to read the content of the configuration file. The first step consists of retrieving the argv observable from the argv driver. The first item is skipped because it contains the name of the Python script being launched. The arguments start from the second item of this observable.

After that are the parsing arguments. The Cyclotron argparse function is used to provide the arguments observable to the parser. The argparse function takes an observable as input and returns an observable. Any function with such a prototype can be used like a custom operator thanks to the let operator. The argparse function takes two additional arguments as parameters: an observable containing one Parser item, and an observable containing some ArgumentDef items. There must be one ArgumentDef item per argument to support. Here, only the config argument is allowed, so there is only one item in the arguments parameter. The observable returned by the argparse function contains Argument items composed of two fields: key and value. The key field contains the name of the parsed argument (without the leading --), and value contains the value of this argument.

After this is a sanity check, with only the config argument being used. Finally, the value provided in the config argument is wrapped in a Read request so that its content can be read by the file driver. When the content of the file has been read, it is available in the source of the file driver. It is now ready to be parsed. This is done in a dedicated function:

def parse_config(file_data):
config = (
file_data
.filter(lambda i: i.id == "config")
.flat_map(lambda i: i.data)
.map(lambda i: json.loads(
i,
object_hook=lambda d: namedtuple('x', d.keys())
(*d.values())))
)

return config.share()

The parse_config function takes an observable of ReadResponse items as input. In practice, this observable contains only one item, with the content of the JSON configuration file. The ReadResponse object contains a data field that is itself an observable of bytes. This is why the flat_map operator is used here: it allows us to extract the content of the file in the same operator chain. The data observable emits a single item that contains the whole content of the file. From that point, the JSON data is available and can be parsed with the json module of the standard library. The lambda provided in the object_hook parameter converts the JSON object to a named tuple instead of a dictionary. Once again, this will make the syntax easier to read when using this object. An important point here is that the parse_config function is a pure function. Had the json.load function been used instead of json.loads, then parse_config would be a side-effect since it would read the content of the file. Moreover, by splitting the reading and parsing steps, the parsing step can be reused in other projects, eventually with JSON data coming from sources other than a file.

Finally, the resulting configuration observable is converted to a hot observable with the share operator. This configuration observable will be subscribed by several observers. If the config observable is not converted to a hot observable, then there will be as many subscriptions at the top of the chain (that is, the argv observable) as there are observers. This means that the arguments would be parsed several times and the configuration file would be read several times. Making the config observable hot after these steps allows us to read and parse the file only once for all observers.

This function can now be used, fed with the source of the file driver:

config = sources.file.response.let(parse_config)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.196.146