i think it only makes sense to have an "opinion" for command line interface in this project, and we probably need more `wutta` subcommands too but we'll see. main motivation for this currently is to allow poser apps to define their own CLI, in particular e.g. `poser install`
		
			
				
	
	
		
			159 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
 | 
						|
Ad Hoc Scripts
 | 
						|
==============
 | 
						|
 | 
						|
It can be useful to write :term:`ad hoc scripts<ad hoc script>` for
 | 
						|
certain things, as opposed to a proper :term:`subcommand`.  This is
 | 
						|
especially true when first getting acquainted with the framework.
 | 
						|
 | 
						|
A script is just a text file with Python code.  To run it you
 | 
						|
generally must invoke the Python interpreter somehow and explicitly
 | 
						|
tell it the path to your script.
 | 
						|
 | 
						|
Note that a script is (usually) not installed as part of a
 | 
						|
:term:`package`.  They can live anywhere.
 | 
						|
 | 
						|
Below we'll walk through creating a script.
 | 
						|
 | 
						|
 | 
						|
Hello World
 | 
						|
-----------
 | 
						|
 | 
						|
First to establish a baseline, here is a starting point script which
 | 
						|
we'll name ``hello.py``::
 | 
						|
 | 
						|
   print('hello world')
 | 
						|
 | 
						|
Run that like so:
 | 
						|
 | 
						|
.. code-block:: sh
 | 
						|
 | 
						|
   $ python hello.py
 | 
						|
   hello world
 | 
						|
 | 
						|
 | 
						|
Better Standards
 | 
						|
~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
Keeping it simple, but improving that script per recommended patterns::
 | 
						|
 | 
						|
   def hello():
 | 
						|
       print('hello world')
 | 
						|
 | 
						|
   if __name__ == '__main__':
 | 
						|
       hello()
 | 
						|
 | 
						|
Runs the same:
 | 
						|
 | 
						|
.. code-block:: sh
 | 
						|
 | 
						|
   $ python hello.py
 | 
						|
   hello world
 | 
						|
 | 
						|
 | 
						|
Configurability
 | 
						|
---------------
 | 
						|
 | 
						|
If you have a :term:`config file` e.g. named ``my.conf``:
 | 
						|
 | 
						|
.. code-block:: ini
 | 
						|
 | 
						|
   [hello]
 | 
						|
   name = George
 | 
						|
 | 
						|
Then you can make a :term:`config object` to access its values.  Note
 | 
						|
that this also gives you access to the :term:`app handler`::
 | 
						|
 | 
						|
   from wuttjamaican.conf import make_config
 | 
						|
 | 
						|
   def hello(config):
 | 
						|
       app = config.get_app()
 | 
						|
       print('hello', config.get('hello.name'))
 | 
						|
       print('from', app.get_title())
 | 
						|
 | 
						|
   if __name__ == '__main__':
 | 
						|
       config = make_config('my.conf')
 | 
						|
       hello(config)
 | 
						|
 | 
						|
Output should now be different:
 | 
						|
 | 
						|
.. code-block:: sh
 | 
						|
 | 
						|
   $ python hello.py
 | 
						|
   hello George
 | 
						|
   from WuttJamaican
 | 
						|
 | 
						|
You are likely to need more imports; it is generally wise to do those
 | 
						|
*within the function* as opposed to the top of the module.  This is to
 | 
						|
ensure the :func:`~wuttjamaican.conf.make_config()` call happens
 | 
						|
before all packages are imported::
 | 
						|
 | 
						|
   from wuttjamaican.conf import make_config
 | 
						|
 | 
						|
   def hello(config):
 | 
						|
 | 
						|
       # do extra imports here
 | 
						|
       from otherpkg import something
 | 
						|
 | 
						|
       app = config.get_app()
 | 
						|
       print('hello', config.get('hello.name'))
 | 
						|
       print('from', app.get_title())
 | 
						|
 | 
						|
       something(config)
 | 
						|
 | 
						|
   if __name__ == '__main__':
 | 
						|
       config = make_config('my.conf')
 | 
						|
       hello(config)
 | 
						|
 | 
						|
 | 
						|
Logging
 | 
						|
-------
 | 
						|
 | 
						|
Logging behavior is determined by the config file(s).  If they contain
 | 
						|
no directives pertaining to the logging config then some default
 | 
						|
behavior will be used.
 | 
						|
 | 
						|
In any case your script should not need to worry about that, but is
 | 
						|
free to make logging calls.  The configured logging behavior would
 | 
						|
determine whether such messages are output to the console and/or file
 | 
						|
etc.
 | 
						|
 | 
						|
There are 3 steps to logging:
 | 
						|
 | 
						|
* import the :mod:`python:logging` module
 | 
						|
* call :func:`~python:logging.getLogger()` to get a logger
 | 
						|
* call methods on the logger, e.g. :meth:`~python:logging.Logger.debug()`
 | 
						|
 | 
						|
Here is the script with logging incorporated::
 | 
						|
 | 
						|
   # nb. it is always safe to import from standard library at the
 | 
						|
   # top of module, that will not interfere with make_config()
 | 
						|
   import logging
 | 
						|
 | 
						|
   from wuttjamaican.conf import make_config
 | 
						|
 | 
						|
   log = logging.getLogger(__name__)
 | 
						|
   log.debug("still at top of module")
 | 
						|
 | 
						|
   def hello(config):
 | 
						|
 | 
						|
       # do extra imports here
 | 
						|
       from otherpkg import something
 | 
						|
 | 
						|
       log.debug("saying hello")
 | 
						|
       app = config.get_app()
 | 
						|
       print('hello', config.get('hello.name'))
 | 
						|
       print('from', app.get_title())
 | 
						|
 | 
						|
       log.debug("about to do something")
 | 
						|
       if something(config):
 | 
						|
           log.info("something seems to have worked")
 | 
						|
       else:
 | 
						|
           log.warn("oh no! something failed")
 | 
						|
 | 
						|
   if __name__ == '__main__':
 | 
						|
       log.debug("entered the __main__ block")
 | 
						|
       config = make_config('my.conf')
 | 
						|
       log.debug("made config object: %s", config)
 | 
						|
       hello(config)
 | 
						|
       log.debug("all done")
 |