Modules

Command

class Command(argv)

Parse command line call, for easy access to parameters.

Warning

This class does not work, on purpose. Correctly parsing command line depends on each command, and implementing it correctly would mean re-implementing the parsing process used by every binary that is to be wrapped with CLAchievements. This is not going to happen. What is done is:

  • Any argument not starting with - is a positional argument.

  • Any argument starting with a single - is a list of short options (-foo is equivalent to -f -o -o). Those options do not have any arguments.

  • Any argument starting with double -- is a long option. If it contains a =, it is intepreted as an option with its argument; otherwise, it does not have any arguments.

The available attributes are:

bin

Base name of the wrapped binary (more or less equivalent to os.path.basename(sys.argv[0])).

short

multidict.MultiDict of short command line arguments (that is, arguments starting with a single -). Keys are the arguments, and values are the options to the arguments. See the warning at the beginning of the documentation of this class.

long

multidict.MultiDict of long command line arguments (that is, arguments starting with a double -). Keys are the arguments, and values are the options to the arguments. See the warning at the beginning of the documentation of this class.

positional

List of positional arguments (that is, arguments not starting with -).

argv

Complete list of arguments (as one would expect from sys.argv).

The following doctest serves as an example.

>>> command = Command("/usr/bin/foo tagada -bar --baz --baz=plop tsoin tsoin".split())
>>> command.bin
'foo'
>>> command.short
<MultiDict('b': None, 'a': None, 'r': None)>
>>> command.long
<MultiDict('baz': None, 'baz': 'plop')>
>>> command.argv
['/usr/bin/foo', 'tagada', '-bar', '--baz', '--baz=plop', 'tsoin', 'tsoin']
>>> command.positional
['tagada', 'tsoin', 'tsoin']

Achievements

class Achievement(command, database)

Achievement: Something that is unlocked when user perform the right commands.

A how-to is available in the Write your own achievement section, which illustrates this class documentation.

This class is a context manager. The __enter__() method is called before the actual wrapped command call, and the __exit__() method is called after it. One of those method must call unlock() when the conditions to fulfill the achievement are met.

_description = None

Description of the achievement. If None, the first non-empty line of the class docstring is used instead.

bin = None

List of binaries loading this achievement. If None, this achievement is always loaded.

first()

This method is called once: when this achievement is loaded for the first time.

This method is meant to be subclassed.

icon = 'star.svg'

File name of the icon (relative to the data directory).

last()

This method is called once: when this achievement has just been unlocked.

This method is meant to be subclassed.

title = None

Title of the achievement. If None, the class is an abstract achievement, to be subclassed.

unlock()

Called when achievement is unlocked.

  • Mark this achievement as unlocked in the database.

  • Notify user.

This method is to be called by one of the __enter__() or __exit__() method when the conditions to unlock the achievement are fulfilled.

class SimplePersistentDataAchievement(command, database)

Achievement, with a simple way to store data into a database.

It is very simple to use, since accessing or writing to self.data will automatically read or write data from the database.

But the cost is that concurrent access to the database will lead to errors. For instance, on a test, running fifty concurrent calls to self.data += 1 only incremented self.data by about twenty values.

This is wrong but:

  • this is just a game, so there is no important consequence to this error;

  • this is a very simple class. If you want a more robust one, please provide a patch.

property data

Picklable persistent data, specific to this achievement.

Note

Database is not locked when reading or writing this data. That is, concurrent runs of self.data += 1 are not guaranteed to succeed.

Note

Be careful to call self.data = MY_NEW_DATA to store your updated data. This means that, if self.data is a dictionary, self.data.update({"foo": "bar"}) will not store anything.

default_data = None

Data stored as this achievement persistent data when this achievement is met for the first time.

Test utils

test_lock(func)

Decorator for test methods keeping the achievement locked.

To be applied to methods of Achievement.

Those methods must iterate over shell commands (as strings). Executing those commands must not unlock the achievement. Otherwise, the corresponding test will fail.

test_unlock(func)

Decorator for test methods unlocking the achievement.

To be applied to methods of Achievement.

Those methods must iterate over shell commands (as strings). Executing those commands must unlock the achievement. Otherwise, the corresponding test will fail.