Fwd: Goals for next weeks

Wolodja Wentland debian at babilen5.org
Tue Jul 16 10:16:47 UTC 2013


On Tue, Jul 16, 2013 at 12:45 +1000, Eugenio Cano-Manuel Mendoza wrote:
> Sorry Wolodja you're getting this email twice. When I clicked on reply it
> didn't send to pkg-clojure.

No problem, but try to send plain text messages :)

>     * fix indentation
[…]

>>         Which editor and indenting scheme do you use? 

> I use vim with 4 spaces. I'm also using the vimclojure plugin but you got me
> here, when I wrote this I didn't know how to indent it properly.

That's okay and it was simply something that I stumbled over. Meikel does not
really maintain VimClojure anymore and Tim Pope kindly developed a new plugin.
AFAICT the best way to develop Clojure in vim these days is to use that plugin
(vim-fireplace) paired with the static files from VimClojure that are
maintained by 

    https://github.com/tpope/vim-fireplace
    https://github.com/guns/vim-clojure-static

>     * No exception handling

> True story. Sometimes I find myself trying to decrypt tracebacks. I planned to
> do this later but I guess it makes sense to do it now since the program is
> substantially larger.

My experience is that you will never reach this mythical "later" point in a
project when you have the time and oversight to introduce exception handling,
logging or finally "do it right"™.

I would strongly suggest to start thinking about the exceptions that might
reasonably arise and how you want to handle them. I found it much easier to
continue developing a tools once these foundations are fleshed out.

The way I would like you to think about this is that you have the "program"
level where you handle input/output and the results of calls into the
"library" level. You would actually handle exceptions at the program level,
but your library functions should behave gracefully.

You might want to take a look at some of my code at:

    https://github.com/babilen/wp-download/blob/master/scripts/wp-download

and the corresponding module:

    https://github.com/babilen/wp-download/blob/master/lib/wp_download/exceptions.py


as an example. This code has been written against py2.6 and I would certainly
do a couple of things quite differently (e.g. use argparse, string formatting,
..), but it exemplifies two things:

    * Exception handling at the program level
    * Sensibly defined exit codes and error messages

I would hope that Paul can give another example.

>     * logging
> 
>       This isn't really necessary right away, but the logging module *is* quite
>       nice and you should accustom yourself with it.

> Will do.

Don't spend too much time on this, but simple module level loggers are a great
thing to have. That is you have something like:

    LOG = logging.getLogger(__name__)

at the top of a module and can then call LOG.debug("goo") in that module. With
suitable log levels and handlers you can easily configure which information is
being shown/logged/... this makes it easy to understand what is going on.

All that being said: logging is non-trivial and I would rather spend time on
something else now than working on the perfect logging system. It is
definitely something that you should look into eventually, but not crucial at
the moment.

See http://docs.python.org/2/howto/logging.html for a how-to.

>     PropertiesParser / PomParser / ProjectParser

>      * class members/variables
> 
>     I don't quite understand why you initialise all those class variables
>     and, in particular, items. If you *really* have to initialise them
>     then just use instance variables (self.foo) and initialise them there.

> I did it to make the variables that the methods are expecting more explicit,
> but I don't think I understand what you're telling me. Could you show me an
> example?

What I am saying is that you don't want this data to be shared across
instances. This data should belong to each instance and it therefore doesn't
make sense to "declare" them as class variables.

    >>> class Foo(object):
    ...     a = "hello"
    ... 
    >>> foo_1 = Foo()
    >>> foo_2 = Foo()
    >>> foo_1.a
    'hello'
    >>> foo_2.a
    'hello'
    >>> Foo.a = "uh oh!"
    >>> foo_1.a
    'uh oh!'
    >>> foo_2.a
    'uh oh!'

> Wolodja thank you so much for your feedback. I'm very happy with the comments
> and I find very positive that we have a lot of room for improvements. I'll be
> working on the more obvious ones while we get more feedback from paultag and
> algernon on the critical ones.

I'll reply to the rest later as I have to think a bit about it. The general
point is that we should try to model the following overrides:

    command_line → env vars → per-user configuration → system configuration

Modelling the latter two (configuration files) is pretty easy as ConfigParser
can simply be given a list of configuration files and settings in "later"
ones override each other. The basic design here would be something like:

    config = ConfigParser.SafeConfigParser()
    config.read(['/etc/foo.conf', os.expanduser('~/.foo.conf)])

That way a user can easily override settings in /etc/foo.conf by setting it in
~/.foo.conf. ConfigParser simply ignores files it cannot read.

This takes care or "per-user configuration → system configuration" and gives
us *the entire configuration from configuration files* in "config".

Modelling "command_line → env vars" is also quite easy. What I would do for
this is to populate argparse's default values from environment variables. This
is not entirely trivial if you want to model hierarchies of "fallback"
environment variables (think DEBEMAIL first then EMAIL then ...), but the
general layout would be something like:

    def init_parser(default_entries):
        parser = argparse.ArgumentParser(prog='myprogram')
        parser.add_argument('--maint-mail', help='set maint mail address'
                            default=default_entries.get('maint_mail', 'SENSIBLE
                            FALLBACK)))

    def maintainer_mail(environment):

        mail = construct_from('/etc/mailname', username)

        if 'DEBEMAIL' in os.environment:
            mail = os.environment['DEBEMAIL']
        elif 'DEBEMAIL' in os.environment:
            mail = os.environment['EMAIL']

        return mail

    def populate_defaults(environment):

        return {
           'maint_mail' : maintainer_mail(environment)
           'foo': foo()
           ...
        }

    def do_stuff(args, command_line_config, project_data):

        template_data = merge_inputs(args, command_line_config, project_data)
        write_template1(template_data)

    def the_mighty_main():
        default_settings = populate_defaults(os.environ)
        parser = init_parser(default_settings)
        args = parser.parse_args()

        config = init_config_parser()
        config.read(['/etc/foo.conf', os.expanduser('~/.foo.conf)])

        project_data = parse_project_clojure()

        do_stuff(args, config, project_data)

Just as a general idea of the top of my head. The "merge_data" function could
simply convert all three inputs into dictionary and then .update() them, or
construct input data more explicitly. (refer to the dch manpage for the
specification)

The only complicated case would be if we have something that can be set as
environment variable, but not as command line. This is why I asked you to
compile the table of data sources so that we can see how far we get with an
approach like this.

I hope this helps, I am, naturally, very happy about any other thoughts
regarding this matter.
-- 
Wolodja <debian at babilen5.org>

4096R/CAF14EFC
081C B7CD FF04 2BA9 94EA  36B2 8B7F 7D30 CAF1 4EFC
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.alioth.debian.org/pipermail/pkg-clojure-maintainers/attachments/20130716/7ad27566/attachment.sig>


More information about the Pkg-clojure-maintainers mailing list