pax_global_header00006660000000000000000000000064141614211070014507gustar00rootroot0000000000000052 comment=1bfb2578822e7112732b5f346832298971b11b9d cmd2-2.3.3/000077500000000000000000000000001416142110700123415ustar00rootroot00000000000000cmd2-2.3.3/CHANGELOG.md000066400000000000000000002365171416142110700141700ustar00rootroot00000000000000## 2.3.3 (November 29, 2021) * Enhancements * Added clearer exception handling to `BorderedTable` and `SimpleTable`. ## 2.3.2 (November 22, 2021) * Bug Fixes * Fixed issue where a `ns_provider` could be passed `None` instead of its correct `cmd2.Cmd` or `CommandSet` value. ## 2.3.1 (November 18, 2021) * Bug Fixes * Fixed issue introduced in 2.3.0 with `AlternatingTable`, `BorderedTable`, and `SimpleTable` that caused header alignment settings to be overridden by data alignment settings. * Enhancements * `CompletionItems` now saves the original object from which it creates a string. * Using `CompletionItems` as argparse choices is fully supported. `cmd2` patched `argparse` to compare input to the original value instead of the `CompletionItems` instance. * `ArgparseCompleter` now does the following if a list of `CompletionItems` was created with numerical types: * Sorts completion hints numerically * Right-aligns the left-most column in completion hint table ## 2.3.0 (November 11, 2021) * Bug Fixes * Fixed `AttributeError` in `rl_get_prompt()` when prompt is `None`. * Fixed bug where using choices on a Settable didn't verify that a valid choice had been entered. * Fixed bug introduced in cmd2 2.0.0 in which `select()` converts return values to strings. It should never have converted return values. * Enhancements * Added settings to Column class which prevent a table from overriding existing styles in header and/or data text. This allows for things like nesting an AlternatingTable in another AlternatingTable. * AlternatingTable no longer automatically applies background color to borders. This was done to improve appearance since the background color extended beyond the borders of the table. * Added ability to colorize all aspects of `AlternatingTable`, `BorderedTable`, and `SimpleTable`. * Added support for 8-bit/256-colors with the `cmd2.EightBitFg` and `cmd2.EightBitBg` classes. * Added support for 24-bit/RGB colors with the `cmd2.RgbFg` and `cmd2.RgbBg` classes. * Removed dependency on colorama. * Changed type of `ansi.allow_style` from a string to an `ansi.AllowStyle` Enum class. * Deprecations * Deprecated `cmd2.fg`. Use `cmd2.Fg` instead. * Deprecated `cmd2.bg`. Use `cmd2.Bg` instead. * Breaking Changes * To support the color upgrade, all cmd2 colors now inherit from either `ansi.FgColor` or `ansi.BgColor`. Therefore, `ansi.style()` no longer accepts colors as strings. ## 2.2.0 (September 14, 2021) * Bug Fixes * Fixed extra space appended to each alias by "alias list" command * Enhancements * New function `set_default_ap_completer_type()` allows developer to extend and modify the behavior of `ArgparseCompleter`. * Added `ArgumentParser.get_ap_completer_type()` and `ArgumentParser.set_ap_completer_type()`. These methods allow developers to enable custom tab completion behavior for a given parser by using a custom `ArgparseCompleter`-based class. * Added `ap_completer_type` keyword arg to `Cmd2ArgumentParser.__init__()` which saves a call to `set_ap_completer_type()`. This keyword will also work with `add_parser()` when creating subcommands if the base command's parser is a `Cmd2ArgumentParser`. * New function `register_argparse_argument_parameter()` allows developers to specify custom parameters to be passed to the argparse parser's `add_argument()` method. These parameters will become accessible in the resulting argparse Action object when modifying `ArgparseCompleter` behavior. * Using `SimpleTable` in the output for the following commands to improve appearance. * help * set (command and tab completion of Settables) * alias tab completion * macro tab completion * Tab completion of `CompletionItems` now includes divider row comprised of `Cmd.ruler` character. * Removed `--verbose` flag from set command since descriptions always show now. * All cmd2 built-in commands now populate `self.last_result`. * Argparse tab completer will complete remaining flag names if there are no more positionals to complete. * Updated `async_alert()` to account for `self.prompt` not matching Readline's current prompt. * Deletions (potentially breaking changes) * Deleted `set_choices_provider()` and `set_completer()` which were deprecated in 2.1.2 * Breaking Changes * Renamed `set_default_argument_parser()` to `set_default_argument_parser_type()` ## 2.1.2 (July 5, 2021) * Enhancements * Added the following accessor methods for cmd2-specific attributes to the `argparse.Action` class * `get_choices_callable()` * `set_choices_provider()` * `set_completer()` * `get_descriptive_header()` * `set_descriptive_header()` * `get_nargs_range()` * `set_nargs_range()` * `get_suppress_tab_hint()` * `set_suppress_tab_hint()` * Deprecations * Now that `set_choices_provider()` and `set_completer()` have been added as methods to the `argparse.Action` class, the standalone functions of the same name will be removed in version 2.2.0. To update to the new convention, do the following: * Change `set_choices_provider(action, provider)` to `action.set_choices_provider(provider)` * Change `set_completer(action, completer)` to `action.set_completer(completer)` ## 2.1.1 (June 17, 2021) * Bug Fixes * Fixed handling of argparse's default options group name which was changed in Python 3.10 * Enhancements * Restored `plugins` and `tests_isolated` directories to tarball published to PyPI for `cmd2` release ## 2.1.0 (June 14, 2021) * Enhancements * Converted persistent history files from pickle to compressed JSON ## 2.0.1 (June 7, 2021) * Bug Fixes * Exclude `plugins` and `tests_isolated` directories from tarball published to PyPI for `cmd2` release ## 2.0.0 (June 6, 2021) * Bug Fixes * Fixed issue where history indexes could get repeated * Fixed issue where TableCreator was tossing blank last line * Corrected help text for alias command * Breaking Changes * `cmd2` 2.0 supports Python 3.6+ (removed support for Python 3.5) * Argparse Completion / Settables * Replaced `choices_function` / `choices_method` with `choices_provider`. * Replaced `completer_function` / `completer_method` with `completer`. * ArgparseCompleter now always passes `cmd2.Cmd` or `CommandSet` instance as the first positional argument to choices_provider and completer functions. * Moved `basic_complete` from utils into `cmd2.Cmd` class. * Moved `CompletionError` to exceptions.py * ``Namespace.__statement__`` has been removed. Use `Namespace.cmd2_statement.get()` instead. * Removed `--silent` flag from `alias/macro create` since startup scripts can be run silently. * Removed `--with_silent` flag from `alias/macro list` since startup scripts can be run silently. * Removed `with_argparser_and_unknown_args` since it was deprecated in 1.3.0. * Renamed `silent_startup_script` to `silence_startup_script` for clarity. * Replaced `cmd2.Cmd.completion_header` with `cmd2.Cmd.formatted_completions`. See Enhancements for description of this new class member. * Settables now have new initialization parameters. It is now a required parameter to supply the reference to the object that holds the settable attribute. `cmd2.Cmd.settables` is no longer a public dict attribute - it is now a property that aggregates all Settables across all registered CommandSets. * Failed transcript testing now sets self.exit_code to 1 instead of -1. * Renamed `use_ipython` keyword parameter of `cmd2.Cmd.__init__()` to `include_ipy`. * `py` command is only enabled if `include_py` parameter is `True`. See Enhancements for a description of this parameter. * Removed ability to run Python commands from the command line with `py`. Now `py` takes no arguments and just opens an interactive Python shell. * Changed default behavior of `runcmds_plus_hooks()` to not stop when Ctrl-C is pressed and instead run the next command in its list. * Removed `cmd2.Cmd.quit_on_sigint` flag, which when `True`, quit the application when Ctrl-C was pressed at the prompt. * The history bug fix resulted in structure changes to the classes in `cmd2.history`. Therefore, persistent history files created with versions older than 2.0.0 are not compatible. * Enhancements * Added support for custom tab completion and up-arrow input history to `cmd2.Cmd2.read_input`. See [read_input.py](https://github.com/python-cmd2/cmd2/blob/master/examples/read_input.py) for an example. * Added `cmd2.exceptions.PassThroughException` to raise unhandled command exceptions instead of printing them. * Added support for ANSI styles and newlines in tab completion results using `cmd2.Cmd.formatted_completions`. `cmd2` provides this capability automatically if you return argparse completion matches as `CompletionItems`. * Settables enhancements: * Settables may be optionally scoped to a CommandSet. Settables added to CommandSets will appear when a CommandSet is registered and disappear when a CommandSet is unregistered. Optionally, scoped Settables may have a prepended prefix. * Settables now allow changes to be applied to any arbitrary object attribute. It no longer needs to match an attribute added to the cmd2 instance itself. * Raising ``SystemExit`` or calling ``sys.exit()`` in a command or hook function will set ``self.exit_code`` to the exit code used in those calls. It will also result in the command loop stopping. * ipy command now includes all of `self.py_locals` in the IPython environment * Added `include_py` keyword parameter to `cmd2.Cmd.__init__()`. If `False`, then the `py` command will not be available. Defaults to `False`. `run_pyscript` is not affected by this parameter. * Made the amount of space between columns in a SimpleTable configurable * On POSIX systems, shell commands and processes being piped to are now run in the user's preferred shell instead of /bin/sh. The preferred shell is obtained by reading the SHELL environment variable. If that doesn't exist or is empty, then /bin/sh is used. * Changed `cmd2.Cmd._run_editor()` to the public method `cmd2.Cmd.run_editor()` ## 1.5.0 (January 31, 2021) * Bug Fixes * Fixed bug where setting `always_show_hint=True` did not show a hint when completing `Settables` * Fixed bug in editor detection logic on Linux systems that do not have `which` * Fixed bug in table creator where column headers with tabs would result in an incorrect width calculation * Fixed `FileNotFoundError` which occurred when running `history --clear` and no history file existed. * Enhancements * Added `silent_startup_script` option to `cmd2.Cmd.__init__()`. If `True`, then the startup script's output will be suppressed. Anything written to stderr will still display. * cmd2 now uses pyreadline3 when running Python 3.8 or greater on Windows * Notes * This is the last release planned to support Python 3.5 ## 1.4.0 (November 11, 2020) * Bug Fixes * Fixed tab completion crash on Windows * Enhancements * Changed how multiline doc string help is formatted to match style of other help messages ## 1.3.11 (October 1, 2020) * Bug Fixes * Fixed issue where quoted redirectors and terminators in aliases and macros were not being restored when read from a startup script. * Fixed issue where instantiating more than one cmd2-based class which uses the `@as_subcommand_to` decorator resulted in duplicated help text in the base command the subcommands belong to. ## 1.3.10 (September 17, 2020) * Enhancements * Added user-settable option called `always_show_hint`. If True, then tab completion hints will always display even when tab completion suggestions print. Arguments whose help or hint text is suppressed will not display hints even when this setting is True. * argparse tab completion now groups flag names which run the same action. Optional flags are wrapped in brackets like it is done in argparse usage text. * default category decorators are now heritable by default and will propagate the category down the class hierarchy until overridden. There's a new optional flag to set heritable to false. * Added `--silent` flag to `alias/macro create`. If used, then no confirmation message will be printed when aliases and macros are created or overwritten. * Added `--with_silent` flag to `alias/macro list`. Use this option when saving to a startup script that should silently create aliases and macros. * Bug Fixes * Fixed issue where flag names weren't always sorted correctly in argparse tab completion ## 1.3.9 (September 03, 2020) * Breaking Changes * `CommandSet.on_unregister()` is now called as first step in unregistering a `CommandSet` and not the last. `CommandSet.on_unregistered()` is now the last step. * Enhancements * Added `CommandSet.on_registered()`. This is called by `cmd2.Cmd` after a `CommandSet` is registered and all its commands have been added to the CLI. * Added `CommandSet.on_unregistered()`. This is called by `cmd2.Cmd` after a `CommandSet` is unregistered and all its commands have been removed from the CLI. ## 1.3.8 (August 28, 2020) * Bug Fixes * Fixed issue where subcommand added with `@as_subcommand_to` decorator did not display help when called with `-h/--help`. * Enhancements * `add_help=False` no longer has to be passed to parsers used in `@as_subcommand_to` decorator. Only pass this if your subcommand should not have the `-h/--help` help option (as stated in argparse documentation). ## 1.3.7 (August 27, 2020) * Bug Fixes * Fixes an issue introduced in 1.3.0 with processing command strings containing terminator/separator character(s) that are manually passed to a command that uses argparse. ## 1.3.6 (August 27, 2020) * Breaking changes * The functions cmd2 adds to Namespaces (`get_statement()` and `get_handler()`) are now `Cmd2AttributeWrapper` objects named `cmd2_statement` and `cmd2_handler`. This makes it easy to filter out which attributes in an `argparse.Namespace` were added by `cmd2`. * Deprecations * ``Namespace.__statement__`` will be removed in `cmd2` 2.0.0. Use `Namespace.cmd2_statement.get()` going forward. ## 1.3.5 (August 25, 2020) * Bug Fixes * Fixed `RecursionError` when printing an `argparse.Namespace` caused by custom attribute cmd2 was adding * Enhancements * Added `get_statement()` function to `argparse.Namespace` which returns `__statement__` attribute ## 1.3.4 (August 20, 2020) * Bug Fixes * Fixed `AttributeError` when `CommandSet` that uses `as_subcommand_to` decorator is loaded during `cmd2.Cmd.__init__()`. * Enhancements * Improved exception messages when using mock without `spec=True`. See [testing](https://cmd2.readthedocs.io/en/latest/testing.html) documentation for more details on testing cmd2-based applications with mock. ## 1.3.3 (August 13, 2020) * Breaking changes * CommandSet command functions (do_, complete_, help_) will no longer have the cmd2 app passed in as the first parameter after `self` since this is already a class member. * Renamed `install_command_set()` and `uninstall_command_set()` to `register_command_set()` and `unregister_command_set()` for better name consistency. * Bug Fixes * Fixed help formatting bug in `Cmd2ArgumentParser` when `metavar` is a tuple * Fixed tab completion bug when using `CompletionItem` on an argument whose `metavar` is a tuple * Added explicit testing against python 3.5.2 for Ubuntu 16.04, and 3.5.3 for Debian 9 * Added fallback definition of typing.Deque (taken from 3.5.4) * Removed explicit type hints that fail due to a bug in 3.5.2 favoring comment-based hints instead * When passing a ns_provider to an argparse command, will now attempt to resolve the correct CommandSet instance for self. If not, it'll fall back and pass in the cmd2 app * Other * Added missing doc-string for new cmd2.Cmd __init__ parameters introduced by CommandSet enhancement ## 1.3.2 (August 10, 2020) * Bug Fixes * Fixed `prog` value of subcommands added with `as_subcommand_to()` decorator. * Fixed missing settings in subcommand parsers created with `as_subcommand_to()` decorator. These settings include things like description and epilog text. * Fixed issue with CommandSet auto-discovery only searching direct sub-classes * Enhancements * Added functions to fetch registered CommandSets by type and command name ## 1.3.1 (August 6, 2020) * Bug Fixes * Fixed issue determining whether an argparse completer function required a reference to a containing CommandSet. Also resolves issues determining the correct CommandSet instance when calling the argparse argument completer function. Manifested as a TypeError when using `cmd2.Cmd.path_complete` as a completer for an argparse-based command defined in a CommandSet ## 1.3.0 (August 4, 2020) * Enhancements * Added CommandSet - Enables defining a separate loadable module of commands to register/unregister with your cmd2 application. * Other * Marked with_argparser_and_unknown_args pending deprecation and consolidated implementation into with_argparser ## 1.2.1 (July 14, 2020) * Bug Fixes * Relax minimum version of `importlib-metadata` to >= 1.6.0 when using Python < 3.8 ## 1.2.0 (July 13, 2020) * Bug Fixes * Fixed `typing` module compatibility issue with Python 3.5 prior to 3.5.4 * Enhancements * Switched to getting version using `importlib.metadata` instead of using `pkg_resources` * Improves `cmd2` application launch time on systems that have a lot of Python packages on `sys.path` * Added dependency on `importlib_metadata` when running on versions of Python prior to 3.8 ## 1.1.0 (June 6, 2020) * Bug Fixes * Fixed issue where subcommand usage text could contain a subcommand alias instead of the actual name * Fixed bug in `ArgparseCompleter` where `fill_width` could become negative if `token_width` was large relative to the terminal width. * Enhancements * Made `ipy` consistent with `py` in the following ways * `ipy` returns whether any of the commands run in it returned True to stop command loop * `Cmd.in_pyscript()` returns True while in `ipy`. * Starting `ipy` when `Cmd.in_pyscript()` is already True is not allowed. * `with_argument_list`, `with_argparser`, and `with_argparser_and_unknown_args` wrappers now pass `kwargs` through to their wrapped command function. * Added `table_creator` module for creating richly formatted tables. This module is in beta and subject to change. * See [table_creation](https://cmd2.readthedocs.io/en/latest/features/table_creation.html) documentation for an overview. * See [table_creation.py](https://github.com/python-cmd2/cmd2/blob/master/examples/table_creation.py) for an example. * Added the following exceptions to the public API * `SkipPostcommandHooks` - Custom exception class for when a command has a failure bad enough to skip post command hooks, but not bad enough to print the exception to the user. * `Cmd2ArgparseError` - A `SkipPostcommandHooks` exception for when a command fails to parse its arguments. Normally argparse raises a `SystemExit` exception in these cases. To avoid stopping the command loop, catch the `SystemExit` and raise this instead. If you still need to run post command hooks after parsing fails, just return instead of raising an exception. * Added explicit handling of `SystemExit`. If a command raises this exception, the command loop will be gracefully stopped. ## 1.0.2 (April 06, 2020) * Bug Fixes * Ctrl-C now stops a running text script instead of just the current `run_script` command * Enhancements * `do_shell()` now saves the return code of the command it runs in `self.last_result` for use in pyscripts ## 1.0.1 (March 13, 2020) * Bug Fixes * Fixed issue where postcmd hooks were running after an `argparse` exception in a command. ## 1.0.0 (March 1, 2020) * Enhancements * The documentation at [cmd2.rftd.io](https://cmd2.readthedocs.io) received a major overhaul * Other * Moved [categorize](https://cmd2.readthedocs.io/en/latest/api/utils.html#miscellaneous) utility function from **decorators** module to **utils** module * Notes * Now that the 1.0 release is out, `cmd2` intends to follow [Semantic Versioning](https://semver.org) ## 0.10.1 (February 19, 2020) * Bug Fixes * Corrected issue where the actual new value was not always being printed in do_set. This occurred in cases where the typed value differed from what the setter had converted it to. * Fixed bug where ANSI style sequences were not correctly handled in `utils.truncate_line()`. * Fixed bug where pyscripts could edit `cmd2.Cmd.py_locals` dictionary. * Fixed bug where cmd2 set `sys.path[0]` for a pyscript to cmd2's working directory instead of the script file's directory. * Fixed bug where `sys.path` was not being restored after a pyscript ran. * Enhancements * Renamed set command's `-l/--long` flag to `-v/--verbose` for consistency with help and history commands. * Setting the following pyscript variables: * `__name__`: __main__ * `__file__`: script path (as typed, ~ will be expanded) * Only tab complete after redirection tokens if redirection is allowed * Made `CompletionError` exception available to non-argparse tab completion * Added `apply_style` to `CompletionError` initializer. It defaults to True, but can be set to False if you don't want the error text to have `ansi.style_error()` applied to it when printed. * Other * Removed undocumented `py run` command since it was replaced by `run_pyscript` a while ago * Renamed `AutoCompleter` to `ArgparseCompleter` for clarity * Custom `EmptyStatement` exception is no longer part of the documented public API * Notes * This is a beta release leading up to the 1.0.0 release * We intend no more breaking changes prior to 1.0.0 * Just bug fixes, documentation updates, and enhancements ## 0.10.0 (February 7, 2020) * Enhancements * Changed the default help text to make `help -v` more discoverable * **set** command now supports tab completion of values * Added `add_settable()` and `remove_settable()` convenience methods to update `self.settable` dictionary * Added convenience `ansi.fg` and `ansi.bg` enums of foreground and background colors * `ansi.style()` `fg` argument can now either be of type `str` or `ansi.fg` * `ansi.style()` `bg` argument can now either be of type `str` or `ansi.bg` * This supports IDE auto-completion of color names * The enums also support * `f-strings` and `format()` calls (e.g. `"{}hello{}".format(fg.blue, fg.reset)`) * string concatenation (e.g. `fg.blue + "hello" + fg.reset`) * Breaking changes * Renamed `locals_in_py` attribute of `cmd2.Cmd` to `self_in_py` * The following public attributes of `cmd2.Cmd` are no longer settable at runtime by default: * `continuation_prompt` * `self_in_py` * `prompt` * `self.settable` changed to `self.settables` * It is now a Dict[str, Settable] instead of Dict[str, str] * setting onchange callbacks have a new method signature and must be added to the Settable instance in order to be called * Removed `cast()` utility function * Removed `ansi.FG_COLORS` and `ansi.BG_COLORS` dictionaries * Replaced with `ansi.fg` and `ansi.bg` enums providing similar but improved functionality * Notes * This is an alpha release leading up to the 1.0.0 release * We intend no more breaking changes prior to 1.0.0 * Just bug fixes, documentation updates, and enhancements ## 0.9.25 (January 26, 2020) * Enhancements * Reduced what gets put in package downloadable from PyPI (removed irrelevant CI config files and such) ## 0.9.24 (January 23, 2020) * Enhancements * Flushing stderr when setting the window title and printing alerts for better responsiveness in cases where stderr is not unbuffered. * Added function to truncate a single line to fit within a given display width. `cmd2.utils.truncate_line` supports characters with display widths greater than 1 and ANSI style sequences. * Added line truncation support to `cmd2.utils` text alignment functions. * Added support for Python 3.9 alpha ## 0.9.23 (January 9, 2020) * Bug Fixes * Fixed bug where startup script containing a single quote in its file name was incorrectly quoted * Added missing implicit dependency on `setuptools` due to build with `setuptools_scm` * Enhancements * Added dim text style support via `style()` function and `ansi.INTENSITY_DIM` setting. * Breaking changes * Renamed the following `ansi` members for accuracy in what types of ANSI escape sequences are handled * `ansi.allow_ansi` -> `ansi.allow_style` * `ansi.ansi_safe_wcswidth()` -> `ansi.style_aware_wcswidth()` * `ansi.ansi_aware_write()` -> `ansi.style_aware_write()` * Renamed the following `ansi` members for clarification * `ansi.BRIGHT` -> `ansi.INTENSITY_BRIGHT` * `ansi.NORMAL` -> `ansi.INTENSITY_NORMAL` ## 0.9.22 (December 9, 2019) * Bug Fixes * Fixed bug where a redefined `ansi.style_error` was not being used in all `cmd2` files * Enhancements * Enabled line buffering when redirecting output to a file * Added `align_left()`, `align_center()`, and `align_right()` to utils.py. All 3 of these functions support ANSI escape sequences and characters with display widths greater than 1. They wrap `align_text()` which is also in utils.py. ## 0.9.21 (November 26, 2019) * Bug Fixes * Fixed bug where pipe processes were not being stopped by Ctrl-C * Added exception handling to account for non-standard Python environments in which readline is not loaded dynamically from a shared library file * Enhancements * Added `read_input()` function that is used to read from stdin. Unlike the Python built-in `input()`, it also has an argument to disable tab completion while input is being entered. * Added capability to override the argument parser class used by cmd2 built-in commands. See override_parser.py example for more details. * Added `end` argument to `pfeedback()` to be consistent with the other print functions like `poutput()`. * Added `apply_style` to `pwarning()`. * Breaking changes * For consistency between all the print functions: * Made `end` and `chop` keyword-only arguments of `ppaged()` * `end` is always added to message in `ppaged()` ## 0.9.20 (November 12, 2019) * Bug Fixes * Fixed bug where setting `use_ipython` to False removed ipy command from the entire `cmd2.Cmd` class instead of just the instance being created * Fix bug where cmd2 ran 'stty sane' command when stdin was not a terminal * Enhancements * Send all startup script paths to run_script. Previously we didn't do this if the file was empty, but that showed no record of the run_script command in history. * Made it easier for developers to override `edit` command by having `do_history` no longer call `do_edit`. This also removes the need to exclude `edit` command from history list. * It is no longer necessary to set the `prog` attribute of an argparser with subcommands. cmd2 now automatically sets the prog value of it and all its subparsers so that all usage statements contain the top level command name and not sys.argv[0]. * Breaking changes * Some constants were moved from cmd2.py to constants.py * cmd2 command decorators were moved to decorators.py. If you were importing them via cmd2's \_\_init\_\_.py, then there will be no issues. ## 0.9.19 (October 14, 2019) * Bug Fixes * Fixed `ValueError` exception which could occur when an old format persistent history file is loaded with new `cmd2` * Enhancements * Improved displaying multiline CompletionErrors by indenting all lines ## 0.9.18 (October 1, 2019) * Bug Fixes * Fixed bug introduced in 0.9.17 where help functions for hidden and disabled commands were not being filtered out as help topics * Enhancements * `AutoCompleter` now handles argparse's mutually exclusive groups. It will not tab complete flag names or positionals for already completed groups. It also will print an error if you try tab completing a flag's value if the flag belongs to a completed group. * `AutoCompleter` now uses the passed-in parser's help formatter to generate hint text. This gives help and hint text for an argument consistent formatting. ## 0.9.17 (September 23, 2019) * Bug Fixes * Fixed a bug when using WSL when all Windows paths have been removed from $PATH * Fixed a bug when running a cmd2 application on Linux without Gtk libraries installed * Enhancements * No longer treating empty text scripts as an error condition * Allow dynamically extending a `cmd2.Cmd` object instance with a `do_xxx` method at runtime * Choices/Completer functions can now be passed a dictionary that maps command-line tokens to their argparse argument. This is helpful when one argument determines what is tab completed for another argument. If these functions have an argument called `arg_tokens`, then AutoCompleter will automatically pass this dictionary to them. * Added CompletionError class that can be raised during argparse-based tab completion and printed to the user * Added the following convenience methods - `Cmd.in_script()` - return whether a text script is running - `Cmd.in_pyscript()` - return whether a pyscript is running ## 0.9.16 (August 7, 2019) * Bug Fixes * Fixed inconsistent parsing/tab completion behavior based on the value of `allow_redirection`. This flag is only meant to be a security setting that prevents redirection of stdout and should not alter parsing logic. * Enhancements * Raise `TypeError` if trying to set choices/completions on argparse action that accepts no arguments * Create directory for the persistent history file if it does not already exist * Added `set_choices_function()`, `set_choices_method()`, `set_completer_function()`, and `set_completer_method()` to support cases where this functionality needs to be added to an argparse action outside of the normal `parser.add_argument()` call. * Breaking Changes * Aliases and macros can no longer have the same name as a command ## 0.9.15 (July 24, 2019) * Bug Fixes * Fixed exception caused by tab completing after an invalid subcommand was entered * Fixed bug where `history -v` was sometimes showing raw and expanded commands when they weren't different * Fixed bug where multiline commands were having leading and ending spaces stripped. This would mess up quoted strings that crossed multiple lines. * Fixed a bug when appending to the clipboard where contents were in reverse order * Fixed issue where run_pyscript failed if the script's filename had 2 or more consecutive spaces * Fixed issue where completer function of disabled command would still run * Enhancements * Greatly simplified using argparse-based tab completion. The new interface is a complete overhaul that breaks the previous way of specifying completion and choices functions. See header of [argparse_custom.py](https://github.com/python-cmd2/cmd2/blob/master/cmd2/argparse_custom.py) for more information. * Enabled tab completion on multiline commands * **Renamed Commands Notice** * The following commands were renamed in the last release and have been removed in this release * `load` - replaced by `run_script` * `_relative_load` - replaced by `_relative_run_script` * `pyscript` - replaced by `run_pyscript` * We apologize for any inconvenience, but the new names are more self-descriptive * Lots of end users were confused particularly about what exactly `load` should be loading * Breaking Changes * Restored `cmd2.Cmd.statement_parser` to be a public attribute (no underscore) * Since it can be useful for creating [post-parsing hooks](https://cmd2.readthedocs.io/en/latest/features/hooks.html#postparsing-hooks) * Completely overhauled the interface for adding tab completion to argparse arguments. See enhancements for more details. * `ACArgumentParser` is now called `Cmd2ArgumentParser` * Moved `basic_complete` to utils.py * Made optional arguments on the following completer methods keyword-only: `delimiter_complete`, `flag_based_complete`, `index_based_complete`, `path_complete`, `shell_cmd_complete` * Renamed history option from `--output-file` to `--output_file` * Renamed `matches_sort_key` to `default_sort_key`. This value determines the default sort ordering of string results like alias, command, category, macro, settable, and shortcut names. Unsorted tab completion results also are sorted with this key. Its default value (ALPHABETICAL_SORT_KEY) performs a case-insensitive alphabetical sort, but it can be changed to a natural sort by setting the value to NATURAL_SORT_KEY. * `StatementParser` now expects shortcuts to be passed in as dictionary. This eliminates the step of converting the shortcuts dictionary into a tuple before creating `StatementParser`. * Renamed `Cmd.pyscript_name` to `Cmd.py_bridge_name` * Renamed `Cmd.pystate` to `Cmd.py_locals` * Renamed `PyscriptBridge` to `PyBridge` ## 0.9.14 (June 29, 2019) * Enhancements * Added support for and testing with Python 3.8, starting with 3.8 beta * Improved information displayed during transcript testing * Added `ansi` module with functions and constants to support ANSI escape sequences which are used for things like applying style to text * Added support for applying styles (color, bold, underline) to text via `style()` function in `ansi` module * Added default styles to ansi.py for printing `success`, `warning`. and `error` text. These are the styles used by cmd2 and can be overridden to match the color scheme of your application. * Added `ansi_aware_write()` function to `ansi` module. This function takes into account the value of `allow_ansi` to determine if ANSI escape sequences should be stripped when not writing to a tty. See documentation for more information on the `allow_ansi` setting. * Breaking Changes * Python 3.4 reached its [end of life](https://www.python.org/dev/peps/pep-0429/) on March 18, 2019 and is no longer supported by `cmd2` * If you need to use Python 3.4, you should pin your requirements to use `cmd2` 0.9.13 * Made lots of changes to minimize the public API of the `cmd2.Cmd` class * Attributes and methods we do not intend to be public now all begin with an underscore * We make no API stability guarantees about these internal functions * Split `perror` into 2 functions: * `perror` - print a message to sys.stderr * `pexcept` - print Exception message to sys.stderr. If debug is true, print exception traceback if one exists * Signature of `poutput` and `perror` significantly changed * Removed color parameters `color`, `err_color`, and `war_color` from `poutput` and `perror` * See the docstrings of these methods or the [cmd2 docs](https://cmd2.readthedocs.io/en/latest/features/generating_output.html) for more info on applying styles to output messages * `end` argument is now keyword-only and cannot be specified positionally * `traceback_war` no longer exists as an argument since it isn't needed now that `perror` and `pexcept` exist * Moved `cmd2.Cmd.colors` to ansi.py and renamed it to `allow_ansi`. This is now an application-wide setting. * Renamed the following constants and moved them to ansi.py * `COLORS_ALWAYS` --> `ANSI_ALWAYS` * `COLORS_NEVER` --> `ANSI_NEVER` * `COLORS_TERMINAL` --> `ANSI_TERMINAL` * **Renamed Commands Notice** * The following commands have been renamed. The old names will be supported until the next release. * `load` --> `run_script` * `_relative_load` --> `_relative_run_script` * `pyscript` --> `run_pyscript` ## 0.9.13 (June 14, 2019) * Bug Fixes * Fixed issue where the wrong terminator was being appended by `Statement.expanded_command_line()` * Fixed issue where aliases and macros could not contain terminator characters in their values * History now shows what was typed for macros and not the resolved value by default. This is consistent with the behavior of aliases. Use the `expanded` or `verbose` arguments to `history` to see the resolved value for the macro. * Fixed parsing issue in case where output redirection appears before a pipe. In that case, the pipe was given precedence even though it appeared later in the command. * Fixed issue where quotes around redirection file paths were being lost in `Statement.expanded_command_line()` * Fixed a bug in how line numbers were calculated for transcript testing * Fixed issue where `_cmdloop()` suppressed exceptions by returning from within its `finally` code * Fixed UnsupportedOperation on fileno error when a shell command was one of the commands run while generating a transcript * Fixed bug where history was displaying expanded multiline commands when -x was not specified * Enhancements * **Added capability to chain pipe commands and redirect their output (e.g. !ls -l | grep user | wc -l > out.txt)** * `pyscript` limits a command's stdout capture to the same period that redirection does. Therefore output from a command's postparsing and finalization hooks isn't saved in the StdSim object. * `StdSim.buffer.write()` now flushes when the wrapped stream uses line buffering and the bytes being written contain a newline or carriage return. This helps when `pyscript` is echoing the output of a shell command since the output will print at the same frequency as when the command is run in a terminal. * **ACArgumentParser** no longer prints complete help text when a parsing error occurs since long help messages scroll the actual error message off the screen. * Exceptions occurring in tab completion functions are now printed to stderr before returning control back to readline. This makes debugging a lot easier since readline suppresses these exceptions. * Added support for custom Namespaces in the argparse decorators. See description of `ns_provider` argument for more information. * Transcript testing now sets the `exit_code` returned from `cmdloop` based on Success/Failure * The history of entered commands previously was saved using the readline persistence mechanism, and only persisted if you had readline installed. Now history is persisted independent of readline; user input from previous invocations of `cmd2` based apps now shows in the `history` command. * Text scripts now run immediately instead of adding their commands to `cmdqueue`. This allows easy capture of the entire script's output. * Added member to `CommandResult` called `stop` which is the return value of `onecmd_plus_hooks` after it runs the given command line. * Breaking changes * Replaced `unquote_redirection_tokens()` with `unquote_specific_tokens()`. This was to support the fix that allows terminators in alias and macro values. * Changed `Statement.pipe_to` to a string instead of a list * `preserve_quotes` is now a keyword-only argument in the argparse decorators * Refactored so that `cmd2.Cmd.cmdloop()` returns the `exit_code` instead of a call to `sys.exit()` It is now application developer's responsibility to treat the return value from `cmdloop()` accordingly * Only valid commands are persistent in history between invocations of `cmd2` based apps. Previously all user input was persistent in history. If readline is installed, the history available with the up and down arrow keys (readline history) may not match that shown in the `history` command, because `history` only tracks valid input, while readline history captures all input. * History is now persisted in a binary format, not plain text format. Previous history files are destroyed on first launch of a `cmd2` based app of version 0.9.13 or higher. * HistoryItem class is no longer a subclass of `str`. If you are directly accessing the `.history` attribute of a `cmd2` based app, you will need to update your code to use `.history.get(1).statement.raw` instead. * Removed internally used `eos` command that was used to keep track of when a text script's commands ended * Removed `cmd2` member called `_STOP_AND_EXIT` since it was just a boolean value that should always be True * Removed `cmd2` member called `_should_quit` since `PyBridge` now handles this logic * Removed support for `cmd.cmdqueue` * `allow_cli_args` is now an argument to __init__ instead of a `cmd2` class member * **Python 3.4 EOL notice** * Python 3.4 reached its [end of life](https://www.python.org/dev/peps/pep-0429/) on March 18, 2019 * This is the last release of `cmd2` which will support Python 3.4 ## 0.9.12 (April 22, 2019) * Bug Fixes * Fixed a bug in how redirection and piping worked inside ``py`` or ``pyscript`` commands * Fixed bug in `async_alert` where it didn't account for prompts that contained newline characters * Fixed path completion case when CWD is just a slash. Relative path matches were incorrectly prepended with a slash. * Enhancements * Added ability to include command name placeholders in the message printed when trying to run a disabled command. * See docstring for ``disable_command()`` or ``disable_category()`` for more details. * Added instance attributes to customize error messages without having to override methods. Theses messages can also be colored. * `help_error` - the error that prints when no help information can be found * `default_error` - the error that prints when a non-existent command is run * The `with_argparser` decorators now add the Statement object created when parsing the command line to the `argparse.Namespace` object they pass to the `do_*` methods. It is stored in an attribute called `__statement__`. This can be useful if a command function needs to know the command line for things like logging. * Added a `-t` option to the `load` command for automatically generating a transcript based on a script file * When in a **pyscript**, the stdout and stderr streams of shell commands and processes being piped to are now captured and included in the ``CommandResult`` structure. * Potentially breaking changes * The following commands now write to stderr instead of stdout when printing an error. This will make catching errors easier in pyscript. * ``do_help()`` - when no help information can be found * ``default()`` - in all cases since this is called when an invalid command name is run * ``_report_disabled_command_usage()`` - in all cases since this is called when a disabled command is run * Removed *** from beginning of error messages printed by `do_help()` and `default()` * Significantly refactored ``cmd.Cmd`` class so that all class attributes got converted to instance attributes, also: * Added ``allow_redirection``, ``terminators``, ``multiline_commands``, and ``shortcuts`` as optional arguments to ``cmd2.Cmd.__init__()`` * A few instance attributes were moved inside ``StatementParser`` and properties were created for accessing them * ``self.pipe_proc`` is now called ``self.cur_pipe_proc_reader`` and is a ``ProcReader`` class. * Shell commands and commands being piped to while in a *pyscript* will function as if their output is going to a pipe and not a tty. This was necessary to be able to capture their output. * Removed `reserved_words` class attribute due to lack of use * Removed `keywords` instance attribute due to lack of use ## 0.9.11 (March 13, 2019) * Bug Fixes * Fixed bug in how **history** command deals with multiline commands when output to a script * Fixed a bug when the ``with_argument_list`` decorator is called with the optional ``preserve_quotes`` argument * Fix bug in ``perror()`` where it would try to print an exception Traceback even if none existed * Enhancements * Improvements to the **history** command * Simplified the display format and made it more similar to **bash** * Added **-x**, **--expanded** flag * output expanded commands instead of entered command (expands aliases, macros, and shortcuts) * Added **-v**, **--verbose** flag * display history and include expanded commands if they differ from the typed command * Added support for negative indices * Added ``matches_sort_key`` to override the default way tab completion matches are sorted * Added ``StdSim.pause_storage`` member which when True will cause ``StdSim`` to not save the output sent to it. See documentation for ``CommandResult`` in ``pyscript_bridge.py`` for reasons pausing the storage can be useful. * Added ability to disable/enable individual commands and entire categories of commands. When a command is disabled, it will not show up in the help menu or tab complete. If a user tries to run the command or call help on it, a command-specific message supplied by the developer will be printed. The following commands were added to support this feature. * ``enable_command()`` * ``enable_category()`` * ``disable_command()`` * ``disable_category()`` * Potentially breaking changes * Made ``cmd2_app`` a positional and required argument of ``AutoCompleter`` since certain functionality now requires that it can't be ``None``. * ``AutoCompleter`` no longer assumes ``CompletionItem`` results are sorted. Therefore you should follow the ``cmd2`` convention of setting ``self.matches_sorted`` to True before returning the results if you have already sorted the ``CompletionItem`` list. Otherwise it will be sorted using ``self.matches_sort_key``. * Removed support for bash completion since this feature had slow performance. Also it relied on ``AutoCompleter`` which has since developed a dependency on ``cmd2`` methods. * Removed ability to call commands in ``pyscript`` as if they were functions (e.g. ``app.help()``) in favor of only supporting one ``pyscript`` interface. This simplifies future maintenance. * No longer supporting C-style comments. Hash (#) is the only valid comment marker. * No longer supporting comments embedded in a command. Only command line input where the first non-whitespace character is a # will be treated as a comment. This means any # character appearing later in the command will be treated as a literal. The same applies to a # in the middle of a multiline command, even if it is the first character on a line. * \# this is a comment * this # is not a comment ## 0.9.10 (February 22, 2019) * Bug Fixes * Fixed unit test that hangs on Windows ## 0.9.9 (February 21, 2019) * Bug Fixes * Fixed bug where the ``set`` command was not tab completing from the current ``settable`` dictionary. * Enhancements * Changed edit command to use do_shell() instead of calling os.system() ## 0.9.8 (February 06, 2019) * Bug Fixes * Fixed issue with echoing strings in StdSim. Because they were being sent to a binary buffer, line buffering was being ignored. * Enhancements * Made quit() and exit() functions available to scripts run with pyscript. This allows those scripts to exit back to the console's prompt instead of exiting the whole application. ## 0.9.7 (January 08, 2019) * Bug Fixes * Fixed bug when user chooses a zero or negative index when calling ``Cmd.select()`` * Restored behavior where ``cmd_echo`` always starts as False in a py script. This was broken in 0.9.5. * Enhancements * **cmdloop** now only attempts to register a custom signal handler for SIGINT if running in the main thread * commands run as a result of ``default_to_shell`` being **True** now run via ``do_shell()`` and are saved to history. * Added more tab completion to pyscript command. * Deletions (potentially breaking changes) * Deleted ``Cmd.colorize()`` and ``Cmd._colorcodes`` which were deprecated in 0.9.5 * Replaced ``dir_exe_only`` and ``dir_only`` flags in ``path_complete`` with optional ``path_filter`` function that is used to filter paths out of completion results. * ``perror()`` no longer prepends "ERROR: " to the error message being printed ## 0.9.6 (October 13, 2018) * Bug Fixes * Fixed bug introduced in 0.9.5 caused by backing up and restoring `self.prompt` in `pseudo_raw_input`. As part of this fix, continuation prompts will not be redrawn with `async_update_prompt` or `async_alert`. * Enhancements * All platforms now depend on [wcwidth](https://pypi.python.org/pypi/wcwidth) to assist with asynchronous alerts. * Macros now accept extra arguments when called. These will be tacked onto the resolved command. * All cmd2 commands run via `py` now go through `onecmd_plus_hooks`. ## 0.9.5 (October 11, 2018) * Bug Fixes * Fixed bug where ``get_all_commands`` could return non-callable attributes * Fixed bug where **alias** command was dropping quotes around arguments * Fixed bug where running help on argparse commands didn't work if they didn't support -h * Fixed transcript testing bug where last command in transcript has no expected output * Fixed bugs with how AutoCompleter and ArgparseFunctor handle argparse arguments with nargs=argparse.REMAINDER. Tab completion now correctly matches how argparse will parse the values. Command strings generated by ArgparseFunctor should now be compliant with how argparse expects REMAINDER arguments to be ordered. * Fixed bugs with how AutoCompleter handles flag prefixes. It is no longer hard-coded to use '-' and will check against the prefix_chars in the argparse object. Also, single-character tokens that happen to be a prefix char are not treated as flags by argparse and AutoCompleter now matches that behavior. * Fixed bug where AutoCompleter was not distinguishing between a negative number and a flag * Fixed bug where AutoCompleter did not handle -- the same way argparse does (all args after -- are non-options) * Enhancements * Added ``exit_code`` attribute of ``cmd2.Cmd`` class * Enables applications to return a non-zero exit code when exiting from ``cmdloop`` * ``ACHelpFormatter`` now inherits from ``argparse.RawTextHelpFormatter`` to make it easier for formatting help/description text * Aliases are now sorted alphabetically * The **set** command now tab completes settable parameter names * Added ``async_alert``, ``async_update_prompt``, and ``set_window_title`` functions * These allow you to provide feedback to the user in an asychronous fashion, meaning alerts can display when the user is still entering text at the prompt. See [async_printing.py](https://github.com/python-cmd2/cmd2/blob/master/examples/async_printing.py) for an example. * Cross-platform colored output support * ``colorama`` gets initialized properly in ``Cmd.__init()`` * The ``Cmd.colors`` setting is no longer platform dependent and now has three values: * Terminal (default) - output methods do not strip any ANSI escape sequences when output is a terminal, but if the output is a pipe or a file the escape sequences are stripped * Always - output methods **never** strip ANSI escape sequences, regardless of the output destination * Never - output methods strip all ANSI escape sequences * Added ``macro`` command to create macros, which are similar to aliases, but can take arguments when called * All cmd2 command functions have been converted to use argparse. * Renamed argparse_example.py to decorator_example.py to help clarify its intent * Deprecations * Deprecated the built-in ``cmd2`` support for colors including ``Cmd.colorize()`` and ``Cmd._colorcodes`` * Deletions (potentially breaking changes) * The ``preparse``, ``postparsing_precmd``, and ``postparsing_postcmd`` methods *deprecated* in the previous release have been deleted * The new application lifecycle hook system allows for registration of callbacks to be called at various points in the lifecycle and is more powerful and flexible than the previous system * ``alias`` is now a command with subcommands to create, list, and delete aliases. Therefore its syntax has changed. All current alias commands in startup scripts or transcripts will break with this release. * `unalias` was deleted since ``alias delete`` replaced it ## 0.9.4 (August 21, 2018) * Bug Fixes * Fixed bug where ``preparse`` was not getting called * Fixed bug in parsing of multiline commands where matching quote is on another line * Enhancements * Improved implementation of lifecycle hooks to support a plugin framework, see ``docs/hooks.rst`` for details. * New dependency on ``attrs`` third party module * Added ``matches_sorted`` member to support custom sorting of tab completion matches * Added [tab_autocomp_dynamic.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocomp_dynamic.py) example * Demonstrates updating the argparse object during init instead of during class construction * Deprecations * Deprecated the following hook methods, see ``hooks.rst`` for full details: * ``cmd2.Cmd.preparse()`` - equivalent functionality available via ``cmd2.Cmd.register_postparsing_hook()`` * ``cmd2.Cmd.postparsing_precmd()`` - equivalent functionality available via ``cmd2.Cmd.register_postparsing_hook()`` * ``cmd2.Cmd.postparsing_postcmd()`` - equivalent functionality available via ``cmd2.Cmd.register_postcmd_hook()`` ## 0.8.9 (August 20, 2018) * Bug Fixes * Fixed extra slash that could print when tab completing users on Windows ## 0.9.3 (July 12, 2018) * Bug Fixes * Fixed bug when StatementParser ``__init__()`` was called with ``terminators`` equal to ``None`` * Fixed bug when ``Cmd.onecmd()`` was called with a raw ``str`` * Enhancements * Added ``--clear`` flag to ``history`` command that clears both the command and readline history. * Deletions * The ``CmdResult`` helper class which was *deprecated* in the previous release has now been deleted * It has been replaced by the improved ``CommandResult`` class ## 0.9.2 (June 28, 2018) * Bug Fixes * Fixed issue where piping and redirecting did not work correctly with paths that had spaces * Enhancements * Added ability to print a header above tab completion suggestions using `completion_header` member * Added ``pager`` and ``pager_chop`` attributes to the ``cmd2.Cmd`` class * ``pager`` defaults to **less -RXF** on POSIX and **more** on Windows * ``pager_chop`` defaults to **less -SRXF** on POSIX and **more** on Windows * Added ``chop`` argument to ``cmd2.Cmd.ppaged()`` method for displaying output using a pager * If ``chop`` is ``False``, then ``self.pager`` is used as the pager * Otherwise ``self.pager_chop`` is used as the pager * Greatly improved the [table_display.py](https://github.com/python-cmd2/cmd2/blob/master/examples/table_display.py) example * Now uses the new [tableformatter](https://github.com/python-tableformatter/tableformatter) module which looks better than ``tabulate`` * Deprecations * The ``CmdResult`` helper class is *deprecated* and replaced by the improved ``CommandResult`` class * ``CommandResult`` has the following attributes: **stdout**, **stderr**, and **data** * ``CmdResult`` had attributes of: **out**, **err**, **war** * ``CmdResult`` will be deleted in the next release ## 0.8.8 (June 28, 2018) * Bug Fixes * Prevent crashes that could occur attempting to open a file in non-existent directory or with very long filename * Enhancements * `display_matches` is no longer restricted to delimited strings ## 0.9.1 (May 28, 2018) * Bug Fixes * fix packaging error for 0.8.x versions (yes we had to deploy a new version of the 0.9.x series to fix a packaging error with the 0.8.x version) ## 0.9.0 (May 28, 2018) * Bug Fixes * If self.default_to_shell is true, then redirection and piping are now properly passed to the shell. Previously it was truncated. * Submenus now call all hooks, it used to just call precmd and postcmd. * Enhancements * Automatic completion of ``argparse`` arguments via ``cmd2.argparse_completer.AutoCompleter`` * See the [tab_autocompletion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py) example for a demonstration of how to use this feature * ``cmd2`` no longer depends on the ``six`` module * ``cmd2`` is now a multi-file Python package instead of a single-file module * New pyscript approach that provides a pythonic interface to commands in the cmd2 application. * Switch command parsing from pyparsing to custom code which utilizes shlex. * The object passed to do_* methods has changed. It no longer is the pyparsing object, it's a new Statement object, which is a subclass of ``str``. The statement object has many attributes which give you access to various components of the parsed input. If you were using anything but the string in your do_* methods, this change will require you to update your code. * ``commentGrammers`` is no longer supported or available. Comments are C-style or python style. * Input redirection no longer supported. Use the load command instead. * ``multilineCommand`` attribute is ``now multiline_command`` * ``identchars`` is now ignored. The standardlibrary cmd uses those characters to split the first "word" of the input, but cmd2 hasn't used those for a while, and the new parsing logic parses on whitespace, which has the added benefit of full unicode support, unlike cmd or prior versions of cmd2. * ``set_posix_shlex`` function and ``POSIX_SHLEX`` variable have been removed. Parsing behavior is now always the more forgiving ``posix=false``. * ``set_strip_quotes`` function and ``STRIP_QUOTES_FOR_NON_POSIX`` have been removed. Quotes are stripped from arguments when presented as a list (a la ``sys.argv``), and present when arguments are presented as a string (like the string passed to do_*). * Changes * ``strip_ansi()`` and ``strip_quotes()`` functions have moved to new utils module * Several constants moved to new constants module * Submenu support has been moved to a new [cmd2-submenu](https://github.com/python-cmd2/cmd2-submenu) plugin. If you use submenus, you will need to update your dependencies and modify your imports. * Deletions (potentially breaking changes) * Deleted all ``optparse`` code which had previously been deprecated in release 0.8.0 * The ``options`` decorator no longer exists * All ``cmd2`` code should be ported to use the new ``argparse``-based decorators * See the [Argument Processing](http://cmd2.readthedocs.io/en/latest/argument_processing.html) section of the documentation for more information on these decorators * Alternatively, see the [argparse_example.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_example.py) * Deleted ``cmd_with_subs_completer``, ``get_subcommands``, and ``get_subcommand_completer`` * Replaced by default AutoCompleter implementation for all commands using argparse * Deleted support for old method of calling application commands with ``cmd()`` and ``self`` * ``cmd2.redirector`` is no longer supported. Output redirection can only be done with '>' or '>>' * Deleted ``postparse()`` hook since it was redundant with ``postparsing_precmd`` * Python 2 no longer supported * ``cmd2`` now supports Python 3.4+ * Known Issues * Some developers have noted very slow performance when importing the ``cmd2`` module. The issue it intermittent, and investigation of the root cause is ongoing. ## 0.8.6 (May 27, 2018) * Bug Fixes * Commands using the @with_argparser_and_unknown_args were not correctly recognized when tab completing * Fixed issue where completion display function was overwritten when a submenu quits * Fixed ``AttributeError`` on Windows when running a ``select`` command cause by **pyreadline** not implementing ``remove_history_item`` * Enhancements * Added warning about **libedit** variant of **readline** not being supported on macOS * Added tab completion of alias names in value field of **alias** command * Enhanced the ``py`` console in the following ways * Added tab completion of Python identifiers instead of **cmd2** commands * Separated the ``py`` console history from the **cmd2** history ## 0.8.5 (April 15, 2018) * Bug Fixes * Fixed a bug with all argument decorators where the wrapped function wasn't returning a value and thus couldn't cause the cmd2 app to quit * Enhancements * Added support for verbose help with -v where it lists a brief summary of what each command does * Added support for categorizing commands into groups within the help menu * See the [Grouping Commands](http://cmd2.readthedocs.io/en/latest/argument_processing.html?highlight=verbose#grouping-commands) section of the docs for more info * See [help_categories.py](https://github.com/python-cmd2/cmd2/blob/master/examples/help_categories.py) for an example * Tab completion of paths now supports ~user user path expansion * Simplified implementation of various tab completion functions so they no longer require ``ctypes`` * Expanded documentation of ``display_matches`` list to clarify its purpose. See cmd2.py for this documentation. * Adding opening quote to tab completion if any of the completion suggestions have a space. * **Python 2 EOL notice** * This is the last release where new features will be added to ``cmd2`` for Python 2.7 * The 0.9.0 release of ``cmd2`` will support Python 3.4+ only * Additional 0.8.x releases may be created to supply bug fixes for Python 2.7 up until August 31, 2018 * After August 31, 2018 not even bug fixes will be provided for Python 2.7 ## 0.8.4 (April 10, 2018) * Bug Fixes * Fixed conditional dependency issue in setup.py that was in 0.8.3. ## 0.8.3 (April 09, 2018) * Bug Fixes * Fixed ``help`` command not calling functions for help topics * Fixed not being able to use quoted paths when redirecting with ``<`` and ``>`` * Enhancements * Tab completion has been overhauled and now supports completion of strings with quotes and spaces. * Tab completion will automatically add an opening quote if a string with a space is completed. * Added ``delimiter_complete`` function for tab completing delimited strings * Added more control over tab completion behavior including the following flags. The use of these flags is documented in cmd2.py * ``allow_appended_space`` * ``allow_closing_quote`` * Due to the tab completion changes, non-Windows platforms now depend on [wcwidth](https://pypi.python.org/pypi/wcwidth). * An alias name can now match a command name. * An alias can now resolve to another alias. * Attribute Changes (Breaks backward compatibility) * ``exclude_from_help`` is now called ``hidden_commands`` since these commands are hidden from things other than help, including tab completion * This list also no longer takes the function names of commands (``do_history``), but instead uses the command names themselves (``history``) * ``excludeFromHistory`` is now called ``exclude_from_history`` * ``cmd_with_subs_completer()`` no longer takes an argument called ``base``. Adding tab completion to subcommands has been simplified to declaring it in the subcommand parser's default settings. This easily allows arbitrary completers like path_complete to be used. See [subcommands.py](https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py) for an example of how to use tab completion in subcommands. In addition, the docstring for ``cmd_with_subs_completer()`` offers more details. ## 0.8.2 (March 21, 2018) * Bug Fixes * Fixed a bug in tab completion of command names within sub-menus * Fixed a bug when using persistent readline history in Python 2.7 * Fixed a bug where the ``AddSubmenu`` decorator didn't work with a default value for ``shared_attributes`` * Added a check to ``ppaged()`` to only use a pager when running in a real fully functional terminal * Enhancements * Added [quit_on_sigint](http://cmd2.readthedocs.io/en/latest/settingchanges.html#quit-on-sigint) attribute to enable canceling current line instead of quitting when Ctrl+C is typed * Added possibility of having readline history preservation in a SubMenu * Added [table_display.py](https://github.com/python-cmd2/cmd2/blob/master/examples/table_display.py) example to demonstrate how to display tabular data * Added command aliasing with ``alias`` and ``unalias`` commands * Added the ability to load an initialization script at startup * See [alias_startup.py](https://github.com/python-cmd2/cmd2/blob/master/examples/alias_startup.py) for an example * Added a default SIGINT handler which terminates any open pipe subprocesses and re-raises a KeyboardInterrupt * For macOS, will load the ``gnureadline`` module if available and ``readline`` if not ## 0.8.1 (March 9, 2018) * Bug Fixes * Fixed a bug if a non-existent **do_*** method was added to the ``exclude_from_help`` list * Fixed a bug in a unit test which would fail if your home directory was empty on a Linux system * Fixed outdated help text for the **edit** command * Fixed outdated [remove_unused.py](https://github.com/python-cmd2/cmd2/blob/master/examples/remove_unused.py) * Enhancements * Added support for sub-menus. * See [submenus.py](https://github.com/python-cmd2/cmd2/blob/master/examples/submenus.py) for an example of how to use it * Added option for persistent readline history * See [persistent_history.py](https://github.com/python-cmd2/cmd2/blob/master/examples/persistent_history.py) for an example * See the [Searchable command history](http://cmd2.readthedocs.io/en/latest/freefeatures.html#searchable-command-history) section of the documentation for more info * Improved PyPI packaging by including unit tests and examples in the tarball * Improved documentation to make it more obvious that **poutput()** should be used instead of **print()** * ``exclude_from_help`` and ``excludeFromHistory`` are now instance instead of class attributes * Added flag and index based tab completion helper functions * See [tab_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_completion.py) * Added support for displaying output which won't fit on the screen via a pager using ``ppaged()`` * See [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) * Attributes Removed (**can cause breaking changes**) * ``abbrev`` - Removed support for abbreviated commands * Good tab completion makes this unnecessary and its presence could cause harmful unintended actions * ``case_insensitive`` - Removed support for case-insensitive command parsing * Its presence wasn't very helpful and could cause harmful unintended actions ## 0.8.0 (February 1, 2018) * Bug Fixes * Fixed unit tests on Python 3.7 due to changes in how re.escape() behaves in Python 3.7 * Fixed a bug where unknown commands were getting saved in the history * Enhancements * Three new decorators for **do_*** commands to make argument parsing easier * **with_argument_list** decorator to change argument type from str to List[str] * **do_*** commands get a single argument which is a list of strings, as pre-parsed by shlex.split() * **with_arparser** decorator for strict argparse-based argument parsing of command arguments * **do_*** commands get a single argument which is the output of argparse.parse_args() * **with_argparser_and_unknown_args** decorator for argparse-based argument parsing, but allows unknown args * **do_*** commands get two arguments, the output of argparse.parse_known_args() * See the [Argument Processing](http://cmd2.readthedocs.io/en/latest/argument_processing.html) section of the documentation for more information on these decorators * Alternatively, see the [argparse_example.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_example.py) and [arg_print.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) examples * Added support for Argparse subcommands when using the **with_argument_parser** or **with_argparser_and_unknown_args** decorators * See [subcommands.py](https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py) for an example of how to use subcommands * Tab completion of subcommand names is automatically supported * The **__relative_load** command is now hidden from the help menu by default * This command is not intended to be called from the command line, only from within scripts * The **set** command now has an additional **-a/--all** option to also display read-only settings * The **history** command can now run, edit, and save prior commands, in addition to displaying prior commands. * The **history** command can now automatically generate a transcript file for regression testing * This makes creating regression tests for your ``cmd2`` application trivial * Commands Removed * The **cmdenvironment** has been removed and its functionality incorporated into the **-a/--all** argument to **set** * The **show** command has been removed. Its functionality has always existing within **set** and continues to do so * The **save** command has been removed. The capability to save commands is now part of the **history** command. * The **run** command has been removed. The capability to run prior commands is now part of the **history** command. * Other changes * The **edit** command no longer allows you to edit prior commands. The capability to edit prior commands is now part of the **history** command. The **edit** command still allows you to edit arbitrary files. * the **autorun_on_edit** setting has been removed. * For Python 3.4 and earlier, ``cmd2`` now has an additional dependency on the ``contextlib2`` module * Deprecations * The old **options** decorator for optparse-based argument parsing is now *deprecated* * The old decorator is still present for now, but will be removed in a future release * ``cmd2`` no longer includes **optparse.make_option**, so if your app needs it import directly from optparse ## 0.7.9 (January 4, 2018) * Bug Fixes * Fixed a couple broken examples * Enhancements * Improved documentation for modifying shortcuts (command aliases) * Made ``pyreadline`` a dependency on Windows to ensure tab completion works * Other changes * Abandoned official support for Python 3.3. It should still work, just don't have an easy way to test it anymore. ## 0.7.8 (November 8, 2017) * Bug Fixes * Fixed ``poutput()`` so it can print an integer zero and other **falsy** things * Fixed a bug which was causing autodoc to fail for building docs on Readthedocs * Fixed bug due to ``pyperclip`` dependency radically changing its project structure in latest version * Enhancements * Improved documentation for user-settable environment parameters * Improved documentation for overriding the default supported comment styles * Added ``runcmds_plus_hooks()`` method to run multiple commands w/o a cmdloop ## 0.7.7 (August 25, 2017) * Bug Fixes * Added workaround for bug which occurs in Python 2.7 on Linux when ``pygtk`` is installed * ``pfeedback()`` now honors feedback_to_output setting and won't redirect when it is ``False`` * For ``edit`` command, both **editor** and **filename** can now have spaces in the name/path * Fixed a bug which occurred when stdin was a pipe instead of a tty due to input redirection * Enhancements * ``feedback_to_output`` now defaults to ``False`` so info like command timing won't redirect * Transcript regular expressions now have predictable, tested, and documented behavior * This makes a breaking change to the format and expectations of transcript testing * The prior behavior removed whitespace before making the comparison, now whitespace must match exactly * Prior version did not allow regexes with whitespace, new version allows any regex * Improved display for ``load`` command and input redirection when **echo** is ``True`` ## 0.7.6 (August 11, 2017) * Bug Fixes * Case-sensitive command parsing was completely broken and has been fixed * ``+d`` now properly quits when case-sensitive command parsing is enabled * Fixed some pyperclip clipboard interaction bugs on Linux * Fixed some timing bugs when running unit tests in parallel by using monkeypatch * Enhancements * Enhanced tab completion of cmd2 command names to support case-insensitive completion * Added an example showing how to remove unused commands * Improved how transcript testing handles prompts with ANSI escape codes by stripping them * Greatly improved implementation for how command output gets piped to a shell command ## 0.7.5 (July 8, 2017) * Bug Fixes * `case_insensitive` is no longer a runtime-settable parameter, but it was still listed as such * Fixed a recursive loop bug when abbreviated commands are enabled and it could get stuck in the editor forever * Added additional command abbreviations to the "exclude from history" list * Fixed argparse_example.py and pirate.py examples and transcript_regex.txt transcript * Fixed a bug in a unit test which occurred under unusual circumstances * Enhancements * Organized all attributes used to configure the ParserManager into a single location * Set the default value of `abbrev` to `False` (which controls whether or not abbreviated commands are allowed) * With good tab completion of command names, using abbreviated commands isn't particularly useful * And it can create complications if you are't careful * Improved implementation of `load` to use command queue instead of nested inner loop ## 0.7.4 (July 3, 2017) * Bug fixes * Fixed a couple bugs in interacting with pastebuffer/clipboard on macOS and Linux * Fixed a couple bugs in edit and save commands if called when history is empty * Ability to pipe ``cmd2`` command output to a shell command is now more reliable, particularly on Windows * Fixed a bug in ``pyscript`` command on Windows related to ``\`` being interpreted as an escape * Enhancements * Ensure that path and shell command tab completion results are alphabetically sorted * Removed feature for load command to load scripts from URLS * It didn't work, there were no unit tests, and it felt out of place * Removed presence of a default file name and default file extension * These also strongly felt out of place * ``load`` and ``_relative_load`` now require a file path * ``edit`` and ``save`` now use a temporary file if a file path isn't provided * ``load`` command has better error checking and reporting * Clipboard copy and paste functionality is now handled by the **pyperclip** module * ``shell`` command now supports redirection and piping of output * Added a lot of unit tests * Other changes * Removed pause command * Added a dependency on the **pyperclip** module ## 0.7.3 (June 23, 2017) * Bug fixes * Fixed a bug in displaying a span of history items when only an end index is supplied * Fixed a bug which caused transcript test failures to display twice * Enhancements * Added the ability to exclude commands from the help menu (**eof** included by default) * Redundant **list** command removed and features merged into **history** command * Added **pyscript** command which supports tab completion and running Python scripts with arguments * Improved tab completion of file system paths, command names, and shell commands * Thanks to Kevin Van Brunt for all of the help with debugging and testing this * Changed default value of USE_ARG_LIST to True - this affects the beavhior of all **@options** commands * **WARNING**: This breaks backwards compatibility, to restore backwards compatibility, add this to the **__init__()** method in your custom class derived from cmd2.Cmd: * cmd2.set_use_arg_list(False) * This change improves argument parsing for all new applications * Refactored code to encapsulate most of the pyparsing logic into a ParserManager class ## 0.7.2 (May 22, 2017) * Added a MANIFEST.ini file to make sure a few extra files get included in the PyPI source distribution ## 0.7.1 (May 22, 2017) * Bug fixes * ``-`` wasn't being treated as a legal character * The allow_cli_args attribute wasn't properly disabling parsing of args at invocation when False * py command wasn't allowing scripts which used *cmd* function prior to entering an interactive Python session * Don't throw exception when piping output to a shell command * Transcript testing now properly calls ``preloop`` before and ``postloop`` after * Fixed readline bug related to ANSI color escape codes in the prompt * Added CONTRIBUTING.md and CODE_OF_CONDUCT.md files * Added unicode parsing unit tests and listed unicode support as a feature when using Python 3 * Added more examples and improved documentation * Example for how use cmd2 in a way where it doesn't own the main loop so it can integrate with external event loops * Example for how to use argparse for parsing command-line args at invocation * Example for how to use the **py** command to run Python scripts which use conditional control flow * Example of how to use regular expressions in a transcript test * Added CmdResult namedtumple for returning and storing results * Added local file system path completion for ``edit``, ``load``, ``save``, and ``shell`` commands * Add shell command completion for ``shell`` command or ``!`` shortcut * Abbreviated multiline commands are no longer allowed (they never worked correctly anyways) ## 0.7.0 (February 23, 2017) * Refactored to use six module for a unified codebase which supports both Python 2 and Python 3 * Stabilized on all platforms (Windows, Mac, Linux) and all supported Python versions (2.7, 3.3, 3.4, 3.5, 3.6, PyPy) * Added lots of unit tests and fixed a number of bugs * Improved documentation and moved it to cmd2.readthedocs.io ## 0.6.9 (October 3, 2016) * Support Python 3 input() * Fix subprocess.mswindows bug * Add Python3.6 support * Drop distutils from setup.py ## 0.6.8 (December 9, 2014) * better editor checking (by Ian Cordascu) ## 0.6.6.1 (August 14, 2013) * No changes to code trunk. Generated sdist from Python 2.7 to avoid 2to3 changes being applied to source. (Issue https://bitbucket.org/catherinedevlin/cmd2/issue/6/packaging-bug) ## 0.6.6 (August 6, 2013) * Added fix by bitbucket.org/desaintmartin to silence the editor check. bitbucket.org/catherinedevlin/cmd2/issue/1/silent-editor-check ## 0.6.5.1 (March 18, 2013) * Bugfix for setup.py version check for Python 2.6, contributed by Tomaz Muraus (https://bitbucket.org/kami) ## 0.6.5 (February 29, 2013) * Belatedly began a NEWS.txt * Changed pyparsing requirement for compatibility with Python version (2 vs 3) cmd2-2.3.3/LICENSE000066400000000000000000000021131416142110700133430ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2008-2021 Catherine Devlin and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cmd2-2.3.3/MANIFEST.in000066400000000000000000000004611416142110700141000ustar00rootroot00000000000000include LICENSE README.md CHANGELOG.md noxfile.py tasks.py Pipfile recursive-include examples * recursive-include tests * recursive-include docs * prune .github prune docs/_build prune docs/.nox exclude .github .gitignore azure-pipelines.yml global-exclude htmlcov/** .coverage* __pycache__/** .gitignore cmd2-2.3.3/PKG-INFO000066400000000000000000000601061416142110700134410ustar00rootroot00000000000000Metadata-Version: 2.1 Name: cmd2 Version: 2.3.3 Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python Home-page: https://github.com/python-cmd2/cmd2 Author: Catherine Devlin Author-email: catherine.devlin@gmail.com License: MIT Description: cmd2: a tool for building interactive command line apps ======================================================= [![Latest Version](https://img.shields.io/pypi/v/cmd2.svg?style=flat-square&label=latest%20stable%20version)](https://pypi.python.org/pypi/cmd2/) [![GitHub Actions](https://github.com/python-cmd2/cmd2/workflows/CI/badge.svg)](https://github.com/python-cmd2/cmd2/actions?query=workflow%3ACI) [![Azure Build status](https://python-cmd2.visualstudio.com/cmd2/_apis/build/status/python-cmd2.cmd2?branch=master)](https://python-cmd2.visualstudio.com/cmd2/_build/latest?definitionId=1&branch=master) [![codecov](https://codecov.io/gh/python-cmd2/cmd2/branch/master/graph/badge.svg)](https://codecov.io/gh/python-cmd2/cmd2) [![Documentation Status](https://readthedocs.org/projects/cmd2/badge/?version=latest)](http://cmd2.readthedocs.io/en/latest/?badge=latest) Chat cmd2 is a tool for building interactive command line applications in Python. Its goal is to make it quick and easy for developers to build feature-rich and user-friendly interactive command line applications. It provides a simple API which is an extension of Python's built-in [cmd](https://docs.python.org/3/library/cmd.html) module. cmd2 provides a wealth of features on top of cmd to make your life easier and eliminates much of the boilerplate code which would be necessary when using cmd. Click on image below to watch a short video demonstrating the capabilities of cmd2: [![Screenshot](cmd2.png)](https://youtu.be/DDU_JH6cFsA) Main Features ------------- - Searchable command history (`history` command and `+r`) - optionally persistent - Text file scripting of your application with `run_script` (`@`) and `_relative_run_script` (`@@`) - Python scripting of your application with ``run_pyscript`` - Run shell commands with ``!`` - Pipe command output to shell commands with `|` - Redirect command output to file with `>`, `>>` - Bare `>`, `>>` with no filename send output to paste buffer (clipboard) - Optional `py` command runs interactive Python console which can be used to debug your application - Optional `ipy` command runs interactive IPython console which can be used to debug your application - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Multi-line commands - Special-character command shortcuts (beyond cmd's `?` and `!`) - Command aliasing similar to bash `alias` command - Macros, which are similar to aliases, but they can contain argument placeholders - Ability to run commands at startup from an initialization script - Settable environment parameters - Parsing commands with arguments using `argparse`, including support for subcommands - Unicode character support - Good tab completion of commands, subcommands, file system paths, and shell commands - Automatic tab completion of `argparse` flags when using one of the `cmd2` `argparse` decorators - Support for Python 3.6+ on Windows, macOS, and Linux - Trivial to provide built-in help for all commands - Built-in regression testing framework for your applications (transcript-based testing) - Transcripts for use with built-in regression can be automatically generated from `history -t` or `run_script -t` - Alerts that seamlessly print while user enters text at prompt - Colored and stylized output using `ansi.style()` Version 2.0 Notes ----------------- - Python 3.5 support ended - The last release of `cmd2` to support Python 3.5 was the 1.5.0 release on January 31, 2021. Python 3.5 was [released](https://docs.python.org/3/whatsnew/3.5.html) on Sept. 13, 2015 and it reached [end-of-life](https://devguide.python.org/#status-of-python-branches) on September 5, 2020. - `cmd2` 2.0 simplifies portions of the API and introduces new features. Many of these changes are not compatible with previous versions of `cmd2`. For assistance with porting your current `cmd2` application to version 2.0, see the [CHANGELOG](https://github.com/python-cmd2/cmd2/blob/master/CHANGELOG.md) for a description of each breaking change and enhancement. Installation ------------ On all operating systems, the latest stable version of `cmd2` can be installed using pip: ```bash pip install -U cmd2 ``` cmd2 works with Python 3.6+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies. For information on other installation options, see [Installation Instructions](https://cmd2.readthedocs.io/en/latest/overview/installation.html) in the cmd2 documentation. Documentation ------------- The latest documentation for cmd2 can be read online here: https://cmd2.readthedocs.io/en/latest/ It is available in HTML, PDF, and ePub formats. Feature Overview ---------------- Instructions for implementing each feature follow. - Extension of the `cmd` module. So capabilities provided by `cmd` still exist - Your applicaiton inherits from `cmd2.Cmd`, let's say you call this class `MyApp` ```Python import cmd2 class MyApp(cmd2.Cmd): pass ``` - Define a command named **foo** by creating a method named **do_foo** ```Python class MyApp(cmd2.Cmd): def do_foo(self, args): """This docstring is the built-in help for the foo command.""" self.poutput(cmd2.style('foo bar baz', fg=cmd2.Fg.RED)) ``` - By default the docstring for your **do_foo** method is the help for the **foo** command - NOTE: This doesn't apply if you use one of the `argparse` decorators mentioned below - Can provide more custom help by creating a **help_foo** method (except when using `argparse` decorators) - Can provide custom tab completion for the **foo** command by creating a **complete_foo** method - Easy to upgrade an existing `cmd` app to `cmd2` - Run your `cmd2` app using the built-in REPL by executing the **cmdloop** method - Searchable command history - Readline history using `+r`, arrow keys, and other [Readline Shortcut keys](http://readline.kablamo.org/emacs.html) - `cmd2` `history` command provides flexible and powerful search - If you wish to exclude some of your custom commands from the history, append their names to the list at `Cmd.exclude_from_history`. - Do `help history` in any `cmd2` application for more information - Both of the above types of history can be optionally persistent between application runs - Via optional `persistent_history_file` argument to `cmd2.Cmd` initializer - Simple scripting using text files with one command + arguments per line - See the [Command Scripts](https://cmd2.readthedocs.io/en/latest/features/scripting.html#command-scripts) section of the `cmd2` docs for more info - See [script.txt](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/script.txt) for a trivial example script that can be used in any `cmd2` application with the `run_script` command (or `@` shortcut) - Powerful and flexible built-in Python scripting of your application using the `run_pyscript` command - Run arbitrary Python scripts within your `cmd2` application with the ability to also call custom `cmd2` commands - No separate API for your end users to learn - Syntax for calling `cmd2` commands in a `run_pyscript` is essentially identical to what they would enter on the command line - See the [Python Scripts](https://cmd2.readthedocs.io/en/latest/features/scripting.html#python-scripts) section of the `cmd2` docs for more info - Also see the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example in conjunction with the [conditional.py](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/conditional.py) script - Parsing commands with `argparse` - The built-in `cmd2.with_argparser` decorator will parse arguments using `argparse.ArgumentParser` - Optionally, `cmd2.with_argparser(.., with_unknown_args=True)` can be used to pass all unknown arguments as a list ```Python from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') argparser.add_argument('words', nargs='+', help='words to say') @with_argparser(argparser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) self.stdout.write('{}\n'.format(' '.join(words))) ``` See [Argument Processing](https://cmd2.readthedocs.io/en/latest/features/argument_processing.html) in the docs for more details NOTE: `cmd2` also provides the `Cmd2ArgumentParser` customization of `argparse.ArgumentParser` for prettier formatting of help and error messages. - `cmd2` applications function like a full-featured shell in many ways (and are cross-platform) - Run arbitrary shell commands by preceding them with `!` or `shell` - Redirect the output of any command to a file with `>` for overwrite or `>>` for append - If no file name provided after the `>`/`>>`, then output goes to the clipboard/pastebuffer - Pipe the output of any command to an arbitrary shell command with `|` - Create your own custom command aliases using the `alias` command - Create your own custom macros using the `macro` command (similar to aliases, but allow arguments) - Settable environment parameters that users can change during execution supported via `set` command - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Optionally specify a startup script that end users can use to customize their environment - Top-notch tab completion capabilities which are easy to use but very powerful - For a command **foo** implement a **complete_foo** method to provide custom tab completion for that command - But the helper methods within `cmd2` discussed below mean you would rarely have to implement this from scratch - Commands which use one of the `argparse` decorators have automatic tab completion of `argparse` flags - And also provide help hints for values associated with these flags - Experiment with the [argprint.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) example using the **oprint** and **pprint** commands to get a feel for how this works - `basic_complete` helper method for tab completion against a list - `path_complete` helper method provides flexible tab completion of file system paths - See the [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) example for a simple use case - See the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example for a more full-featured use case - `delimiter_complete` helper method for tab completion against a list but each match is split on a delimiter - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use this feature - `flag_based_complete` helper method for tab completion based on a particular flag preceding the token being completed - `index_based_complete` helper method for tab completion based on a fixed position in the input string - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use these features - `flag_based_complete()` and `index_based_complete()` are basic methods and should only be used if you are not familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use argparse-based completion - `cmd2` in combination with `argparse` also provide several advanced capabilities for automatic tab completion - See the [argparse_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_completion.py) example for more info - Multi-line commands Any command accepts multi-line input when its name is listed the `multiline_commands` optional argument to `cmd2.Cmd.__init`. The program will keep expecting input until a line ends with any of the characters listed in the `terminators` optional argument to `cmd2.Cmd.__init__()` . The default terminators are `;` and `\n` (empty newline). - Special-character shortcut commands (beyond cmd's "@" and "!") To create a single-character shortcut for a command, update `Cmd.shortcuts`. - Asynchronous alerts based on events happening in background threads - `cmd2` provides the following helper methods for providing information to users asynchronously even though the `cmd2` REPL is a line-oriented command interpreter: - `async_alert` - display an important message to the user while they are at the prompt in between commands - To the user it appears as if an alert message is printed above the prompt - `async_update_prompt` - update the prompt while the user is still typing at it - This is good for alerting the user to system changes dynamically in between commands - `set_window_title` - set the terminal window title - This changes the window title of the terminal that the user is running the `cmd2` app within Tutorials --------- * PyOhio 2019 presentation: * [video](https://www.youtube.com/watch?v=pebeWrTqIIw) * [slides](https://github.com/python-cmd2/talks/blob/master/PyOhio_2019/cmd2-PyOhio_2019.pdf) * [example code](https://github.com/python-cmd2/talks/tree/master/PyOhio_2019/examples) * [Cookiecutter](https://github.com/cookiecutter/cookiecutter) Templates from community * Basic cookiecutter template for cmd2 application : https://github.com/jayrod/cookiecutter-python-cmd2 * Advanced cookiecutter template with external plugin support : https://github.com/jayrod/cookiecutter-python-cmd2-ext-plug Example Application ------------------- Example cmd2 application (**examples/example.py**): ```python #!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2. """ import random import sys import cmd2 class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh'] MUMBLE_FIRST = ['so', 'like', 'well'] MUMBLE_LAST = ['right?'] def __init__(self): self.maxrepeats = 3 shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'&': 'speak'}) super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) # Make maxrepeats settable at runtime self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command')) speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input mumble_parser = cmd2.Cmd2ArgumentParser() mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat') mumble_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(mumble_parser) def do_mumble(self, args): """Mumbles what you tell me to.""" repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): output = [] if (random.random() < .33): output.append(random.choice(self.MUMBLE_FIRST)) for word in args.words: if (random.random() < .40): output.append(random.choice(self.MUMBLES)) output.append(word) if (random.random() < .25): output.append(random.choice(self.MUMBLE_LAST)) self.poutput(' '.join(output)) if __name__ == '__main__': app = CmdLineApp() sys.exit(app.cmdloop()) ``` The following is a sample session running example.py. Thanks to Cmd2's built-in transcript testing capability, it also serves as a test suite for example.py when saved as *transcript_regex.txt*. Running ```bash python example.py -t transcript_regex.txt ``` will run all the commands in the transcript against `example.py`, verifying that the output produced matches the transcript. example/transcript_regex.txt: ```text # Run this transcript with "python example.py -t transcript_regex.txt" # Anything between two forward slashes, /, is interpreted as a regular expression (regex). # The regex for editor will match whatever program you use. # regexes on prompts just make the trailing space obvious (Cmd) set allow_style: '/(Terminal|Always|Never)/' always_show_hint: False debug: False echo: False editor: /.*?/ feedback_to_output: False maxrepeats: 3 quiet: False timing: False ``` Regular expressions can be used anywhere within a transcript file simply by enclosing them within forward slashes, `/`. Found a bug? ------------ If you think you've found a bug, please first read through the open [Issues](https://github.com/python-cmd2/cmd2/issues). If you're confident it's a new bug, go ahead and create a new GitHub issue. Be sure to include as much information as possible so we can reproduce the bug. At a minimum, please state the following: * ``cmd2`` version * Python version * OS name and version * What you did to cause the bug to occur * Include any traceback or error message associated with the bug Open source projects using cmd2 ------------------------------- Here are a few examples of open-source projects which use `cmd2`: * [Jok3r](http://www.jok3r-framework.com) * Network & Web Pentest Automation Framework * [CephFS Shell](http://docs.ceph.com/docs/master/cephfs/cephfs-shell/) * [Ceph](https://ceph.com/) is a distributed object, block, and file storage platform * [JSShell](https://github.com/Den1al/JSShell) * An interactive multi-user web JavaScript shell * [psiTurk](https://psiturk.org) * An open platform for science on Amazon Mechanical Turk * [Poseidon](https://github.com/CyberReboot/poseidon) * Leverages software-defined networks (SDNs) to acquire and then feed network traffic to a number of machine learning techniques * [Unipacker](https://github.com/unipacker/unipacker) * Automatic and platform-independent unpacker for Windows binaries based on emulation * [FLASHMINGO](https://github.com/fireeye/flashmingo) * Automatic analysis of SWF files based on some heuristics. Extensible via plugins. * [tomcatmanager](https://github.com/tomcatmanager/tomcatmanager) * A command line tool and python library for managing a tomcat server * [Expliot](https://gitlab.com/expliot_framework/expliot) * Internet of Things (IoT) exploitation framework * [mptcpanalyzer](https://github.com/teto/mptcpanalyzer) * Tool to help analyze mptcp pcaps * [clanvas](https://github.com/marklalor/clanvas) * Command-line client for Canvas by Instructure Keywords: command prompt console cmd Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Operating System :: OS Independent Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.6 Description-Content-Type: text/markdown Provides-Extra: test Provides-Extra: dev Provides-Extra: validate cmd2-2.3.3/Pipfile000066400000000000000000000015351416142110700136600ustar00rootroot00000000000000[[source]] name = "pypi" url = "https://pypi.org/simple" verify_ssl = true [packages] attrs = ">=16.3.0" pyperclip = ">=1.6" setuptools = ">=34.4" wcwidth = ">=0.1.7" [dev-packages] black = "*" cmd2 = {editable = true,path = "."} cmd2_ext_test = {editable = true,path = "plugins/ext_test"} codecov = "*" doc8 = "*" flake8 = "*" gnureadline = {version = "*",sys_platform = "== 'darwin'"} invoke = "*" ipython = "*" isort = "*" mock = {version = "*",markers = "python_version < '3.6'"} mypy = "*" pyreadline = {version = "*",sys_platform = "== 'win32'",markers = "python_version < '3.8'"} pyreadline3 = {version = "*",sys_platform = "== 'win32'",markers = "python_version >= '3.8'"} pytest = "*" pytest-cov = "*" pytest-mock = "*" sphinx = "*" sphinx-autobuild = "*" sphinx-rtd-theme = "*" tableformatter="*" twine = ">=1.11" [pipenv] allow_prereleases = true cmd2-2.3.3/README.md000077500000000000000000000476321416142110700136370ustar00rootroot00000000000000cmd2: a tool for building interactive command line apps ======================================================= [![Latest Version](https://img.shields.io/pypi/v/cmd2.svg?style=flat-square&label=latest%20stable%20version)](https://pypi.python.org/pypi/cmd2/) [![GitHub Actions](https://github.com/python-cmd2/cmd2/workflows/CI/badge.svg)](https://github.com/python-cmd2/cmd2/actions?query=workflow%3ACI) [![Azure Build status](https://python-cmd2.visualstudio.com/cmd2/_apis/build/status/python-cmd2.cmd2?branch=master)](https://python-cmd2.visualstudio.com/cmd2/_build/latest?definitionId=1&branch=master) [![codecov](https://codecov.io/gh/python-cmd2/cmd2/branch/master/graph/badge.svg)](https://codecov.io/gh/python-cmd2/cmd2) [![Documentation Status](https://readthedocs.org/projects/cmd2/badge/?version=latest)](http://cmd2.readthedocs.io/en/latest/?badge=latest) Chat cmd2 is a tool for building interactive command line applications in Python. Its goal is to make it quick and easy for developers to build feature-rich and user-friendly interactive command line applications. It provides a simple API which is an extension of Python's built-in [cmd](https://docs.python.org/3/library/cmd.html) module. cmd2 provides a wealth of features on top of cmd to make your life easier and eliminates much of the boilerplate code which would be necessary when using cmd. Click on image below to watch a short video demonstrating the capabilities of cmd2: [![Screenshot](cmd2.png)](https://youtu.be/DDU_JH6cFsA) Main Features ------------- - Searchable command history (`history` command and `+r`) - optionally persistent - Text file scripting of your application with `run_script` (`@`) and `_relative_run_script` (`@@`) - Python scripting of your application with ``run_pyscript`` - Run shell commands with ``!`` - Pipe command output to shell commands with `|` - Redirect command output to file with `>`, `>>` - Bare `>`, `>>` with no filename send output to paste buffer (clipboard) - Optional `py` command runs interactive Python console which can be used to debug your application - Optional `ipy` command runs interactive IPython console which can be used to debug your application - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Multi-line commands - Special-character command shortcuts (beyond cmd's `?` and `!`) - Command aliasing similar to bash `alias` command - Macros, which are similar to aliases, but they can contain argument placeholders - Ability to run commands at startup from an initialization script - Settable environment parameters - Parsing commands with arguments using `argparse`, including support for subcommands - Unicode character support - Good tab completion of commands, subcommands, file system paths, and shell commands - Automatic tab completion of `argparse` flags when using one of the `cmd2` `argparse` decorators - Support for Python 3.6+ on Windows, macOS, and Linux - Trivial to provide built-in help for all commands - Built-in regression testing framework for your applications (transcript-based testing) - Transcripts for use with built-in regression can be automatically generated from `history -t` or `run_script -t` - Alerts that seamlessly print while user enters text at prompt - Colored and stylized output using `ansi.style()` Version 2.0 Notes ----------------- - Python 3.5 support ended - The last release of `cmd2` to support Python 3.5 was the 1.5.0 release on January 31, 2021. Python 3.5 was [released](https://docs.python.org/3/whatsnew/3.5.html) on Sept. 13, 2015 and it reached [end-of-life](https://devguide.python.org/#status-of-python-branches) on September 5, 2020. - `cmd2` 2.0 simplifies portions of the API and introduces new features. Many of these changes are not compatible with previous versions of `cmd2`. For assistance with porting your current `cmd2` application to version 2.0, see the [CHANGELOG](https://github.com/python-cmd2/cmd2/blob/master/CHANGELOG.md) for a description of each breaking change and enhancement. Installation ------------ On all operating systems, the latest stable version of `cmd2` can be installed using pip: ```bash pip install -U cmd2 ``` cmd2 works with Python 3.6+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies. For information on other installation options, see [Installation Instructions](https://cmd2.readthedocs.io/en/latest/overview/installation.html) in the cmd2 documentation. Documentation ------------- The latest documentation for cmd2 can be read online here: https://cmd2.readthedocs.io/en/latest/ It is available in HTML, PDF, and ePub formats. Feature Overview ---------------- Instructions for implementing each feature follow. - Extension of the `cmd` module. So capabilities provided by `cmd` still exist - Your applicaiton inherits from `cmd2.Cmd`, let's say you call this class `MyApp` ```Python import cmd2 class MyApp(cmd2.Cmd): pass ``` - Define a command named **foo** by creating a method named **do_foo** ```Python class MyApp(cmd2.Cmd): def do_foo(self, args): """This docstring is the built-in help for the foo command.""" self.poutput(cmd2.style('foo bar baz', fg=cmd2.Fg.RED)) ``` - By default the docstring for your **do_foo** method is the help for the **foo** command - NOTE: This doesn't apply if you use one of the `argparse` decorators mentioned below - Can provide more custom help by creating a **help_foo** method (except when using `argparse` decorators) - Can provide custom tab completion for the **foo** command by creating a **complete_foo** method - Easy to upgrade an existing `cmd` app to `cmd2` - Run your `cmd2` app using the built-in REPL by executing the **cmdloop** method - Searchable command history - Readline history using `+r`, arrow keys, and other [Readline Shortcut keys](http://readline.kablamo.org/emacs.html) - `cmd2` `history` command provides flexible and powerful search - If you wish to exclude some of your custom commands from the history, append their names to the list at `Cmd.exclude_from_history`. - Do `help history` in any `cmd2` application for more information - Both of the above types of history can be optionally persistent between application runs - Via optional `persistent_history_file` argument to `cmd2.Cmd` initializer - Simple scripting using text files with one command + arguments per line - See the [Command Scripts](https://cmd2.readthedocs.io/en/latest/features/scripting.html#command-scripts) section of the `cmd2` docs for more info - See [script.txt](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/script.txt) for a trivial example script that can be used in any `cmd2` application with the `run_script` command (or `@` shortcut) - Powerful and flexible built-in Python scripting of your application using the `run_pyscript` command - Run arbitrary Python scripts within your `cmd2` application with the ability to also call custom `cmd2` commands - No separate API for your end users to learn - Syntax for calling `cmd2` commands in a `run_pyscript` is essentially identical to what they would enter on the command line - See the [Python Scripts](https://cmd2.readthedocs.io/en/latest/features/scripting.html#python-scripts) section of the `cmd2` docs for more info - Also see the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example in conjunction with the [conditional.py](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/conditional.py) script - Parsing commands with `argparse` - The built-in `cmd2.with_argparser` decorator will parse arguments using `argparse.ArgumentParser` - Optionally, `cmd2.with_argparser(.., with_unknown_args=True)` can be used to pass all unknown arguments as a list ```Python from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') argparser.add_argument('words', nargs='+', help='words to say') @with_argparser(argparser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) self.stdout.write('{}\n'.format(' '.join(words))) ``` See [Argument Processing](https://cmd2.readthedocs.io/en/latest/features/argument_processing.html) in the docs for more details NOTE: `cmd2` also provides the `Cmd2ArgumentParser` customization of `argparse.ArgumentParser` for prettier formatting of help and error messages. - `cmd2` applications function like a full-featured shell in many ways (and are cross-platform) - Run arbitrary shell commands by preceding them with `!` or `shell` - Redirect the output of any command to a file with `>` for overwrite or `>>` for append - If no file name provided after the `>`/`>>`, then output goes to the clipboard/pastebuffer - Pipe the output of any command to an arbitrary shell command with `|` - Create your own custom command aliases using the `alias` command - Create your own custom macros using the `macro` command (similar to aliases, but allow arguments) - Settable environment parameters that users can change during execution supported via `set` command - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Optionally specify a startup script that end users can use to customize their environment - Top-notch tab completion capabilities which are easy to use but very powerful - For a command **foo** implement a **complete_foo** method to provide custom tab completion for that command - But the helper methods within `cmd2` discussed below mean you would rarely have to implement this from scratch - Commands which use one of the `argparse` decorators have automatic tab completion of `argparse` flags - And also provide help hints for values associated with these flags - Experiment with the [argprint.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) example using the **oprint** and **pprint** commands to get a feel for how this works - `basic_complete` helper method for tab completion against a list - `path_complete` helper method provides flexible tab completion of file system paths - See the [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) example for a simple use case - See the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example for a more full-featured use case - `delimiter_complete` helper method for tab completion against a list but each match is split on a delimiter - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use this feature - `flag_based_complete` helper method for tab completion based on a particular flag preceding the token being completed - `index_based_complete` helper method for tab completion based on a fixed position in the input string - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use these features - `flag_based_complete()` and `index_based_complete()` are basic methods and should only be used if you are not familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use argparse-based completion - `cmd2` in combination with `argparse` also provide several advanced capabilities for automatic tab completion - See the [argparse_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_completion.py) example for more info - Multi-line commands Any command accepts multi-line input when its name is listed the `multiline_commands` optional argument to `cmd2.Cmd.__init`. The program will keep expecting input until a line ends with any of the characters listed in the `terminators` optional argument to `cmd2.Cmd.__init__()` . The default terminators are `;` and `\n` (empty newline). - Special-character shortcut commands (beyond cmd's "@" and "!") To create a single-character shortcut for a command, update `Cmd.shortcuts`. - Asynchronous alerts based on events happening in background threads - `cmd2` provides the following helper methods for providing information to users asynchronously even though the `cmd2` REPL is a line-oriented command interpreter: - `async_alert` - display an important message to the user while they are at the prompt in between commands - To the user it appears as if an alert message is printed above the prompt - `async_update_prompt` - update the prompt while the user is still typing at it - This is good for alerting the user to system changes dynamically in between commands - `set_window_title` - set the terminal window title - This changes the window title of the terminal that the user is running the `cmd2` app within Tutorials --------- * PyOhio 2019 presentation: * [video](https://www.youtube.com/watch?v=pebeWrTqIIw) * [slides](https://github.com/python-cmd2/talks/blob/master/PyOhio_2019/cmd2-PyOhio_2019.pdf) * [example code](https://github.com/python-cmd2/talks/tree/master/PyOhio_2019/examples) * [Cookiecutter](https://github.com/cookiecutter/cookiecutter) Templates from community * Basic cookiecutter template for cmd2 application : https://github.com/jayrod/cookiecutter-python-cmd2 * Advanced cookiecutter template with external plugin support : https://github.com/jayrod/cookiecutter-python-cmd2-ext-plug Example Application ------------------- Example cmd2 application (**examples/example.py**): ```python #!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2. """ import random import sys import cmd2 class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh'] MUMBLE_FIRST = ['so', 'like', 'well'] MUMBLE_LAST = ['right?'] def __init__(self): self.maxrepeats = 3 shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'&': 'speak'}) super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) # Make maxrepeats settable at runtime self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command')) speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input mumble_parser = cmd2.Cmd2ArgumentParser() mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat') mumble_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(mumble_parser) def do_mumble(self, args): """Mumbles what you tell me to.""" repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): output = [] if (random.random() < .33): output.append(random.choice(self.MUMBLE_FIRST)) for word in args.words: if (random.random() < .40): output.append(random.choice(self.MUMBLES)) output.append(word) if (random.random() < .25): output.append(random.choice(self.MUMBLE_LAST)) self.poutput(' '.join(output)) if __name__ == '__main__': app = CmdLineApp() sys.exit(app.cmdloop()) ``` The following is a sample session running example.py. Thanks to Cmd2's built-in transcript testing capability, it also serves as a test suite for example.py when saved as *transcript_regex.txt*. Running ```bash python example.py -t transcript_regex.txt ``` will run all the commands in the transcript against `example.py`, verifying that the output produced matches the transcript. example/transcript_regex.txt: ```text # Run this transcript with "python example.py -t transcript_regex.txt" # Anything between two forward slashes, /, is interpreted as a regular expression (regex). # The regex for editor will match whatever program you use. # regexes on prompts just make the trailing space obvious (Cmd) set allow_style: '/(Terminal|Always|Never)/' always_show_hint: False debug: False echo: False editor: /.*?/ feedback_to_output: False maxrepeats: 3 quiet: False timing: False ``` Regular expressions can be used anywhere within a transcript file simply by enclosing them within forward slashes, `/`. Found a bug? ------------ If you think you've found a bug, please first read through the open [Issues](https://github.com/python-cmd2/cmd2/issues). If you're confident it's a new bug, go ahead and create a new GitHub issue. Be sure to include as much information as possible so we can reproduce the bug. At a minimum, please state the following: * ``cmd2`` version * Python version * OS name and version * What you did to cause the bug to occur * Include any traceback or error message associated with the bug Open source projects using cmd2 ------------------------------- Here are a few examples of open-source projects which use `cmd2`: * [Jok3r](http://www.jok3r-framework.com) * Network & Web Pentest Automation Framework * [CephFS Shell](http://docs.ceph.com/docs/master/cephfs/cephfs-shell/) * [Ceph](https://ceph.com/) is a distributed object, block, and file storage platform * [JSShell](https://github.com/Den1al/JSShell) * An interactive multi-user web JavaScript shell * [psiTurk](https://psiturk.org) * An open platform for science on Amazon Mechanical Turk * [Poseidon](https://github.com/CyberReboot/poseidon) * Leverages software-defined networks (SDNs) to acquire and then feed network traffic to a number of machine learning techniques * [Unipacker](https://github.com/unipacker/unipacker) * Automatic and platform-independent unpacker for Windows binaries based on emulation * [FLASHMINGO](https://github.com/fireeye/flashmingo) * Automatic analysis of SWF files based on some heuristics. Extensible via plugins. * [tomcatmanager](https://github.com/tomcatmanager/tomcatmanager) * A command line tool and python library for managing a tomcat server * [Expliot](https://gitlab.com/expliot_framework/expliot) * Internet of Things (IoT) exploitation framework * [mptcpanalyzer](https://github.com/teto/mptcpanalyzer) * Tool to help analyze mptcp pcaps * [clanvas](https://github.com/marklalor/clanvas) * Command-line client for Canvas by Instructure cmd2-2.3.3/cmd2.egg-info/000077500000000000000000000000001416142110700146605ustar00rootroot00000000000000cmd2-2.3.3/cmd2.egg-info/PKG-INFO000066400000000000000000000601061416142110700157600ustar00rootroot00000000000000Metadata-Version: 2.1 Name: cmd2 Version: 2.3.3 Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python Home-page: https://github.com/python-cmd2/cmd2 Author: Catherine Devlin Author-email: catherine.devlin@gmail.com License: MIT Description: cmd2: a tool for building interactive command line apps ======================================================= [![Latest Version](https://img.shields.io/pypi/v/cmd2.svg?style=flat-square&label=latest%20stable%20version)](https://pypi.python.org/pypi/cmd2/) [![GitHub Actions](https://github.com/python-cmd2/cmd2/workflows/CI/badge.svg)](https://github.com/python-cmd2/cmd2/actions?query=workflow%3ACI) [![Azure Build status](https://python-cmd2.visualstudio.com/cmd2/_apis/build/status/python-cmd2.cmd2?branch=master)](https://python-cmd2.visualstudio.com/cmd2/_build/latest?definitionId=1&branch=master) [![codecov](https://codecov.io/gh/python-cmd2/cmd2/branch/master/graph/badge.svg)](https://codecov.io/gh/python-cmd2/cmd2) [![Documentation Status](https://readthedocs.org/projects/cmd2/badge/?version=latest)](http://cmd2.readthedocs.io/en/latest/?badge=latest) Chat cmd2 is a tool for building interactive command line applications in Python. Its goal is to make it quick and easy for developers to build feature-rich and user-friendly interactive command line applications. It provides a simple API which is an extension of Python's built-in [cmd](https://docs.python.org/3/library/cmd.html) module. cmd2 provides a wealth of features on top of cmd to make your life easier and eliminates much of the boilerplate code which would be necessary when using cmd. Click on image below to watch a short video demonstrating the capabilities of cmd2: [![Screenshot](cmd2.png)](https://youtu.be/DDU_JH6cFsA) Main Features ------------- - Searchable command history (`history` command and `+r`) - optionally persistent - Text file scripting of your application with `run_script` (`@`) and `_relative_run_script` (`@@`) - Python scripting of your application with ``run_pyscript`` - Run shell commands with ``!`` - Pipe command output to shell commands with `|` - Redirect command output to file with `>`, `>>` - Bare `>`, `>>` with no filename send output to paste buffer (clipboard) - Optional `py` command runs interactive Python console which can be used to debug your application - Optional `ipy` command runs interactive IPython console which can be used to debug your application - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Multi-line commands - Special-character command shortcuts (beyond cmd's `?` and `!`) - Command aliasing similar to bash `alias` command - Macros, which are similar to aliases, but they can contain argument placeholders - Ability to run commands at startup from an initialization script - Settable environment parameters - Parsing commands with arguments using `argparse`, including support for subcommands - Unicode character support - Good tab completion of commands, subcommands, file system paths, and shell commands - Automatic tab completion of `argparse` flags when using one of the `cmd2` `argparse` decorators - Support for Python 3.6+ on Windows, macOS, and Linux - Trivial to provide built-in help for all commands - Built-in regression testing framework for your applications (transcript-based testing) - Transcripts for use with built-in regression can be automatically generated from `history -t` or `run_script -t` - Alerts that seamlessly print while user enters text at prompt - Colored and stylized output using `ansi.style()` Version 2.0 Notes ----------------- - Python 3.5 support ended - The last release of `cmd2` to support Python 3.5 was the 1.5.0 release on January 31, 2021. Python 3.5 was [released](https://docs.python.org/3/whatsnew/3.5.html) on Sept. 13, 2015 and it reached [end-of-life](https://devguide.python.org/#status-of-python-branches) on September 5, 2020. - `cmd2` 2.0 simplifies portions of the API and introduces new features. Many of these changes are not compatible with previous versions of `cmd2`. For assistance with porting your current `cmd2` application to version 2.0, see the [CHANGELOG](https://github.com/python-cmd2/cmd2/blob/master/CHANGELOG.md) for a description of each breaking change and enhancement. Installation ------------ On all operating systems, the latest stable version of `cmd2` can be installed using pip: ```bash pip install -U cmd2 ``` cmd2 works with Python 3.6+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies. For information on other installation options, see [Installation Instructions](https://cmd2.readthedocs.io/en/latest/overview/installation.html) in the cmd2 documentation. Documentation ------------- The latest documentation for cmd2 can be read online here: https://cmd2.readthedocs.io/en/latest/ It is available in HTML, PDF, and ePub formats. Feature Overview ---------------- Instructions for implementing each feature follow. - Extension of the `cmd` module. So capabilities provided by `cmd` still exist - Your applicaiton inherits from `cmd2.Cmd`, let's say you call this class `MyApp` ```Python import cmd2 class MyApp(cmd2.Cmd): pass ``` - Define a command named **foo** by creating a method named **do_foo** ```Python class MyApp(cmd2.Cmd): def do_foo(self, args): """This docstring is the built-in help for the foo command.""" self.poutput(cmd2.style('foo bar baz', fg=cmd2.Fg.RED)) ``` - By default the docstring for your **do_foo** method is the help for the **foo** command - NOTE: This doesn't apply if you use one of the `argparse` decorators mentioned below - Can provide more custom help by creating a **help_foo** method (except when using `argparse` decorators) - Can provide custom tab completion for the **foo** command by creating a **complete_foo** method - Easy to upgrade an existing `cmd` app to `cmd2` - Run your `cmd2` app using the built-in REPL by executing the **cmdloop** method - Searchable command history - Readline history using `+r`, arrow keys, and other [Readline Shortcut keys](http://readline.kablamo.org/emacs.html) - `cmd2` `history` command provides flexible and powerful search - If you wish to exclude some of your custom commands from the history, append their names to the list at `Cmd.exclude_from_history`. - Do `help history` in any `cmd2` application for more information - Both of the above types of history can be optionally persistent between application runs - Via optional `persistent_history_file` argument to `cmd2.Cmd` initializer - Simple scripting using text files with one command + arguments per line - See the [Command Scripts](https://cmd2.readthedocs.io/en/latest/features/scripting.html#command-scripts) section of the `cmd2` docs for more info - See [script.txt](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/script.txt) for a trivial example script that can be used in any `cmd2` application with the `run_script` command (or `@` shortcut) - Powerful and flexible built-in Python scripting of your application using the `run_pyscript` command - Run arbitrary Python scripts within your `cmd2` application with the ability to also call custom `cmd2` commands - No separate API for your end users to learn - Syntax for calling `cmd2` commands in a `run_pyscript` is essentially identical to what they would enter on the command line - See the [Python Scripts](https://cmd2.readthedocs.io/en/latest/features/scripting.html#python-scripts) section of the `cmd2` docs for more info - Also see the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example in conjunction with the [conditional.py](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/conditional.py) script - Parsing commands with `argparse` - The built-in `cmd2.with_argparser` decorator will parse arguments using `argparse.ArgumentParser` - Optionally, `cmd2.with_argparser(.., with_unknown_args=True)` can be used to pass all unknown arguments as a list ```Python from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') argparser.add_argument('words', nargs='+', help='words to say') @with_argparser(argparser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) self.stdout.write('{}\n'.format(' '.join(words))) ``` See [Argument Processing](https://cmd2.readthedocs.io/en/latest/features/argument_processing.html) in the docs for more details NOTE: `cmd2` also provides the `Cmd2ArgumentParser` customization of `argparse.ArgumentParser` for prettier formatting of help and error messages. - `cmd2` applications function like a full-featured shell in many ways (and are cross-platform) - Run arbitrary shell commands by preceding them with `!` or `shell` - Redirect the output of any command to a file with `>` for overwrite or `>>` for append - If no file name provided after the `>`/`>>`, then output goes to the clipboard/pastebuffer - Pipe the output of any command to an arbitrary shell command with `|` - Create your own custom command aliases using the `alias` command - Create your own custom macros using the `macro` command (similar to aliases, but allow arguments) - Settable environment parameters that users can change during execution supported via `set` command - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Optionally specify a startup script that end users can use to customize their environment - Top-notch tab completion capabilities which are easy to use but very powerful - For a command **foo** implement a **complete_foo** method to provide custom tab completion for that command - But the helper methods within `cmd2` discussed below mean you would rarely have to implement this from scratch - Commands which use one of the `argparse` decorators have automatic tab completion of `argparse` flags - And also provide help hints for values associated with these flags - Experiment with the [argprint.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) example using the **oprint** and **pprint** commands to get a feel for how this works - `basic_complete` helper method for tab completion against a list - `path_complete` helper method provides flexible tab completion of file system paths - See the [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) example for a simple use case - See the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example for a more full-featured use case - `delimiter_complete` helper method for tab completion against a list but each match is split on a delimiter - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use this feature - `flag_based_complete` helper method for tab completion based on a particular flag preceding the token being completed - `index_based_complete` helper method for tab completion based on a fixed position in the input string - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use these features - `flag_based_complete()` and `index_based_complete()` are basic methods and should only be used if you are not familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use argparse-based completion - `cmd2` in combination with `argparse` also provide several advanced capabilities for automatic tab completion - See the [argparse_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_completion.py) example for more info - Multi-line commands Any command accepts multi-line input when its name is listed the `multiline_commands` optional argument to `cmd2.Cmd.__init`. The program will keep expecting input until a line ends with any of the characters listed in the `terminators` optional argument to `cmd2.Cmd.__init__()` . The default terminators are `;` and `\n` (empty newline). - Special-character shortcut commands (beyond cmd's "@" and "!") To create a single-character shortcut for a command, update `Cmd.shortcuts`. - Asynchronous alerts based on events happening in background threads - `cmd2` provides the following helper methods for providing information to users asynchronously even though the `cmd2` REPL is a line-oriented command interpreter: - `async_alert` - display an important message to the user while they are at the prompt in between commands - To the user it appears as if an alert message is printed above the prompt - `async_update_prompt` - update the prompt while the user is still typing at it - This is good for alerting the user to system changes dynamically in between commands - `set_window_title` - set the terminal window title - This changes the window title of the terminal that the user is running the `cmd2` app within Tutorials --------- * PyOhio 2019 presentation: * [video](https://www.youtube.com/watch?v=pebeWrTqIIw) * [slides](https://github.com/python-cmd2/talks/blob/master/PyOhio_2019/cmd2-PyOhio_2019.pdf) * [example code](https://github.com/python-cmd2/talks/tree/master/PyOhio_2019/examples) * [Cookiecutter](https://github.com/cookiecutter/cookiecutter) Templates from community * Basic cookiecutter template for cmd2 application : https://github.com/jayrod/cookiecutter-python-cmd2 * Advanced cookiecutter template with external plugin support : https://github.com/jayrod/cookiecutter-python-cmd2-ext-plug Example Application ------------------- Example cmd2 application (**examples/example.py**): ```python #!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2. """ import random import sys import cmd2 class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh'] MUMBLE_FIRST = ['so', 'like', 'well'] MUMBLE_LAST = ['right?'] def __init__(self): self.maxrepeats = 3 shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'&': 'speak'}) super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) # Make maxrepeats settable at runtime self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command')) speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input mumble_parser = cmd2.Cmd2ArgumentParser() mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat') mumble_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(mumble_parser) def do_mumble(self, args): """Mumbles what you tell me to.""" repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): output = [] if (random.random() < .33): output.append(random.choice(self.MUMBLE_FIRST)) for word in args.words: if (random.random() < .40): output.append(random.choice(self.MUMBLES)) output.append(word) if (random.random() < .25): output.append(random.choice(self.MUMBLE_LAST)) self.poutput(' '.join(output)) if __name__ == '__main__': app = CmdLineApp() sys.exit(app.cmdloop()) ``` The following is a sample session running example.py. Thanks to Cmd2's built-in transcript testing capability, it also serves as a test suite for example.py when saved as *transcript_regex.txt*. Running ```bash python example.py -t transcript_regex.txt ``` will run all the commands in the transcript against `example.py`, verifying that the output produced matches the transcript. example/transcript_regex.txt: ```text # Run this transcript with "python example.py -t transcript_regex.txt" # Anything between two forward slashes, /, is interpreted as a regular expression (regex). # The regex for editor will match whatever program you use. # regexes on prompts just make the trailing space obvious (Cmd) set allow_style: '/(Terminal|Always|Never)/' always_show_hint: False debug: False echo: False editor: /.*?/ feedback_to_output: False maxrepeats: 3 quiet: False timing: False ``` Regular expressions can be used anywhere within a transcript file simply by enclosing them within forward slashes, `/`. Found a bug? ------------ If you think you've found a bug, please first read through the open [Issues](https://github.com/python-cmd2/cmd2/issues). If you're confident it's a new bug, go ahead and create a new GitHub issue. Be sure to include as much information as possible so we can reproduce the bug. At a minimum, please state the following: * ``cmd2`` version * Python version * OS name and version * What you did to cause the bug to occur * Include any traceback or error message associated with the bug Open source projects using cmd2 ------------------------------- Here are a few examples of open-source projects which use `cmd2`: * [Jok3r](http://www.jok3r-framework.com) * Network & Web Pentest Automation Framework * [CephFS Shell](http://docs.ceph.com/docs/master/cephfs/cephfs-shell/) * [Ceph](https://ceph.com/) is a distributed object, block, and file storage platform * [JSShell](https://github.com/Den1al/JSShell) * An interactive multi-user web JavaScript shell * [psiTurk](https://psiturk.org) * An open platform for science on Amazon Mechanical Turk * [Poseidon](https://github.com/CyberReboot/poseidon) * Leverages software-defined networks (SDNs) to acquire and then feed network traffic to a number of machine learning techniques * [Unipacker](https://github.com/unipacker/unipacker) * Automatic and platform-independent unpacker for Windows binaries based on emulation * [FLASHMINGO](https://github.com/fireeye/flashmingo) * Automatic analysis of SWF files based on some heuristics. Extensible via plugins. * [tomcatmanager](https://github.com/tomcatmanager/tomcatmanager) * A command line tool and python library for managing a tomcat server * [Expliot](https://gitlab.com/expliot_framework/expliot) * Internet of Things (IoT) exploitation framework * [mptcpanalyzer](https://github.com/teto/mptcpanalyzer) * Tool to help analyze mptcp pcaps * [clanvas](https://github.com/marklalor/clanvas) * Command-line client for Canvas by Instructure Keywords: command prompt console cmd Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Operating System :: OS Independent Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.6 Description-Content-Type: text/markdown Provides-Extra: test Provides-Extra: dev Provides-Extra: validate cmd2-2.3.3/cmd2.egg-info/SOURCES.txt000066400000000000000000000151211416142110700165440ustar00rootroot00000000000000CHANGELOG.md LICENSE MANIFEST.in Pipfile README.md cmd2.png noxfile.py pyproject.toml setup.cfg setup.py tasks.py cmd2/__init__.py cmd2/ansi.py cmd2/argparse_completer.py cmd2/argparse_custom.py cmd2/clipboard.py cmd2/cmd2.py cmd2/command_definition.py cmd2/constants.py cmd2/decorators.py cmd2/exceptions.py cmd2/history.py cmd2/parsing.py cmd2/plugin.py cmd2/py.typed cmd2/py_bridge.py cmd2/rl_utils.py cmd2/table_creator.py cmd2/transcript.py cmd2/utils.py cmd2.egg-info/PKG-INFO cmd2.egg-info/SOURCES.txt cmd2.egg-info/dependency_links.txt cmd2.egg-info/requires.txt cmd2.egg-info/top_level.txt docs/Makefile docs/conf.py docs/doc_conventions.rst docs/index.rst docs/make.bat docs/testing.rst docs/api/ansi.rst docs/api/argparse_completer.rst docs/api/argparse_custom.rst docs/api/cmd.rst docs/api/command_definition.rst docs/api/constants.rst docs/api/decorators.rst docs/api/exceptions.rst docs/api/history.rst docs/api/index.rst docs/api/parsing.rst docs/api/plugin.rst docs/api/plugin_external_test.rst docs/api/py_bridge.rst docs/api/table_creator.rst docs/api/utils.rst docs/examples/alternate_event_loops.rst docs/examples/first_app.rst docs/examples/index.rst docs/features/argument_processing.rst docs/features/builtin_commands.rst docs/features/clipboard.rst docs/features/commands.rst docs/features/completion.rst docs/features/disable_commands.rst docs/features/embedded_python_shells.rst docs/features/generating_output.rst docs/features/help.rst docs/features/history.rst docs/features/hooks.rst docs/features/index.rst docs/features/initialization.rst docs/features/misc.rst docs/features/modular_commands.rst docs/features/multiline_commands.rst docs/features/os.rst docs/features/packaging.rst docs/features/plugins.rst docs/features/prompt.rst docs/features/redirection.rst docs/features/scripting.rst docs/features/settings.rst docs/features/shortcuts_aliases_macros.rst docs/features/startup_commands.rst docs/features/table_creation.rst docs/features/transcripts.rst docs/migrating/incompatibilities.rst docs/migrating/index.rst docs/migrating/minimum.rst docs/migrating/next_steps.rst docs/migrating/summary.rst docs/migrating/why.rst docs/overview/alternatives.rst docs/overview/index.rst docs/overview/installation.rst docs/overview/integrating.rst docs/overview/resources.rst docs/overview/summary.rst docs/plugins/external_test.rst docs/plugins/index.rst examples/.cmd2rc examples/alias_startup.py examples/arg_decorators.py examples/arg_print.py examples/argparse_completion.py examples/async_printing.py examples/basic.py examples/basic_completion.py examples/cmd2_history.dat examples/cmd_as_argument.py examples/colors.py examples/custom_parser.py examples/decorator_example.py examples/default_categories.py examples/dynamic_commands.py examples/environment.py examples/event_loops.py examples/example.py examples/exit_code.py examples/first_app.py examples/hello_cmd2.py examples/help_categories.py examples/hooks.py examples/initialization.py examples/migrating.py examples/modular_commands_basic.py examples/modular_commands_dynamic.py examples/modular_commands_main.py examples/modular_subcommands.py examples/override_parser.py examples/paged_output.py examples/persistent_history.py examples/pirate.py examples/python_scripting.py examples/read_input.py examples/remove_builtin_commands.py examples/remove_settable.py examples/subcommands.py examples/table_creation.py examples/unicode_commands.py examples/modular_commands/__init__.py examples/modular_commands/commandset_basic.py examples/modular_commands/commandset_complex.py examples/modular_commands/commandset_custominit.py examples/scripts/arg_printer.py examples/scripts/conditional.py examples/scripts/nested.txt examples/scripts/quit.txt examples/scripts/save_help_text.py examples/scripts/script.py examples/scripts/script.txt examples/transcripts/exampleSession.txt examples/transcripts/pirate.transcript examples/transcripts/quit.txt examples/transcripts/transcript_regex.txt plugins/README.txt plugins/tasks.py plugins/ext_test/CHANGELOG.md plugins/ext_test/README.md plugins/ext_test/build-pyenvs.sh plugins/ext_test/noxfile.py plugins/ext_test/setup.py plugins/ext_test/tasks.py plugins/ext_test/cmd2_ext_test/__init__.py plugins/ext_test/cmd2_ext_test/cmd2_ext_test.py plugins/ext_test/cmd2_ext_test/py.typed plugins/ext_test/cmd2_ext_test/pylintrc plugins/ext_test/examples/example.py plugins/ext_test/tests/__init__.py plugins/ext_test/tests/pylintrc plugins/ext_test/tests/test_ext_test.py plugins/template/CHANGELOG.md plugins/template/LICENSE plugins/template/README.md plugins/template/build-pyenvs.sh plugins/template/noxfile.py plugins/template/setup.py plugins/template/tasks.py plugins/template/cmd2_myplugin/__init__.py plugins/template/cmd2_myplugin/myplugin.py plugins/template/cmd2_myplugin/pylintrc plugins/template/examples/example.py plugins/template/tests/__init__.py plugins/template/tests/pylintrc plugins/template/tests/test_myplugin.py tests/.cmd2rc tests/__init__.py tests/conftest.py tests/relative_multiple.txt tests/script.py tests/script.txt tests/test_ansi.py tests/test_argparse.py tests/test_argparse_completer.py tests/test_argparse_custom.py tests/test_cmd2.py tests/test_completion.py tests/test_history.py tests/test_parsing.py tests/test_plugin.py tests/test_run_pyscript.py tests/test_table_creator.py tests/test_transcript.py tests/test_utils.py tests/test_utils_defining_class.py tests/pyscript/echo.py tests/pyscript/environment.py tests/pyscript/help.py tests/pyscript/py_locals.py tests/pyscript/pyscript_dir.py tests/pyscript/raises_exception.py tests/pyscript/recursive.py tests/pyscript/self_in_py.py tests/pyscript/stdout_capture.py tests/pyscript/stop.py tests/scripts/binary.bin tests/scripts/empty.txt tests/scripts/help.txt tests/scripts/nested.txt tests/scripts/one_down.txt tests/scripts/postcmds.txt tests/scripts/precmds.txt tests/scripts/utf8.txt tests/transcripts/bol_eol.txt tests/transcripts/characterclass.txt tests/transcripts/dotstar.txt tests/transcripts/extension_notation.txt tests/transcripts/failure.txt tests/transcripts/from_cmdloop.txt tests/transcripts/multiline_no_regex.txt tests/transcripts/multiline_regex.txt tests/transcripts/no_output.txt tests/transcripts/no_output_last.txt tests/transcripts/regex_set.txt tests/transcripts/singleslash.txt tests/transcripts/slashes_escaped.txt tests/transcripts/slashslash.txt tests/transcripts/spaces.txt tests/transcripts/word_boundaries.txt tests_isolated/__init__.py tests_isolated/test_commandset/__init__.py tests_isolated/test_commandset/conftest.py tests_isolated/test_commandset/test_argparse_subcommands.py tests_isolated/test_commandset/test_categories.py tests_isolated/test_commandset/test_commandset.pycmd2-2.3.3/cmd2.egg-info/dependency_links.txt000066400000000000000000000000011416142110700207260ustar00rootroot00000000000000 cmd2-2.3.3/cmd2.egg-info/requires.txt000066400000000000000000000010271416142110700172600ustar00rootroot00000000000000attrs>=16.3.0 pyperclip>=1.6 wcwidth>=0.1.7 [:python_version < "3.8"] importlib_metadata>=1.6.0 typing_extensions [:sys_platform=='win32' and python_version<'3.8'] pyreadline [:sys_platform=='win32' and python_version>='3.8'] pyreadline3 [dev] codecov doc8 flake8 invoke mypy==0.902 nox pytest>=4.6 pytest-cov pytest-mock sphinx sphinx-rtd-theme sphinx-autobuild twine>=1.11 [test] codecov coverage pytest>=4.6 pytest-cov pytest-mock [test:sys_platform == "darwin"] gnureadline [validate] flake8 mypy==0.902 types-pkg-resources cmd2-2.3.3/cmd2.egg-info/top_level.txt000066400000000000000000000000051416142110700174050ustar00rootroot00000000000000cmd2 cmd2-2.3.3/cmd2.png000066400000000000000000011420141416142110700136770ustar00rootroot00000000000000‰PNG  IHDRŽªúñ7 IiCCPICC ProfileH‰•WXSÉž[RIhH ½‰R¤K ¡E*ØI ¡ÄDì.Ë*¸vº*¢èZY+êÚX»kyXPQÖÅ‚ •7)°®û½÷¾w¾oîýsæœÿ”̽wžTš‹ê')ÅG„°&¥¦±H]0 0y|¹”   Ýÿ.o¯Dy¿â¢äúçü=PΉƒ8C ççA|¼„/•@ôzë™R%ž± &±T‰³Ô¸D‰3Ô¸Re“Ïx7d'Ë@»êY…ü,È£}bW‰@,@‡ q _Ä@ ñ¨¼¼J í€CÆWT/¶X$æÆhpU(1Ró›ÏSåoq³PÂNâÊ'EÕ"††©kÇ:„’$M½X—´ $^ãûJš§±Ç©ÂÜ¥Þ bSya‚Æ,€ RÍÇH âÕyâÙ¼ñqê|ð" 8 °€Ž 0dq{oS/ü¥ž < Y@\4š!ÕŒ^@1ø"!û…¨f… ê?kÕW©š-Tyä€Çç( +T^’áhÉàÔˆÿsÍ…C9÷Oj¢5Å/KgÈ’F %FÃ‰Ž¸ ˆûãÑð ‡;îƒûeû—=á1¡“ð€pÐE¸5]¼HöM=,0tÁášš3¾®·ƒ¬žxù!7ÎÄM€ >FbãA0¶'Ôr4™+«ÿ–ûo5|ÕuÅ•‚RFP‚)ßzj;i{³({úu‡Ô¹f ÷•3<óm|ÎWÀ{Ô·–Øbìv;‰ÇŽ`M€…Çš±6쨯¢GªU4-^•Oäÿ#OSÙI¹k½kë'õ\°Hù~œÒY2q–¨€Å†o~!‹+áÅrwuƒomåwDýšzÍT}æ…¿tù'ð-ƒÊ¬¿tQÆÀXX;ðþ „ñ $‚T0 vY׳ ÌsÀBP ÊÁ °TM`+Ø ö€ý  'Á¯à"è×Àm¸zºÁsÐÞ‚AHa ƈb‹8#„!ÑH<’Ф#YˆQ sïrdR…lAꟑÃÈIä<Ò‰ÜBî#=È+ä#Š¡4Ô5CíÐ1¨ÊF£ÐDt*š…æ£Åh º ­DkÑÝh#z½ˆ^C»Ðçh?0-Œ‰Yb.˜ÆÁb±4,“aó°2¬«Å°ø?_Áº°^ìNÄ8 w+8OÂùx>>_ŠWá;ñFü4~¿÷á_t‚)Á™àGà&²3 ¥„ ÂvÂ!Âø4uÞ‰D&Ñžè ŸÆTb6q6q)qq/ñ±“øØO"‘ŒIΤR,‰G* •’Ö“v“Ž“.“ºIïÉZd ²;9œœF–‘+È»ÈÇÈ—ÉOÈ]Š-ÅKPfQ–S¶QZ(—(Ý”ªÕž@M¤fSR+© Ô3Ô;Ô×ZZZVZ¾ZµÄZ ´*µöiÓº¯õ¦Os¢qhSh Ú2ÚÚ Ú-Úk:nG¦§Ñ èËèuôSô{ô÷Ú íÑÚ\mö|íjíFíËÚ/t(:¶:li:Å::t.éôêRtít9º<ÝyºÕº‡uoèöë1ôÜôbõòô–êíÒ;¯÷TŸ¤o§¦/Ð/ÑߪJÿ!cX38 >ã;Æ6ÆF·ÑÀÞ€kmPn°Ç Ý ÏPßp¬a²a‘aµáQÃ.&Æ´cr™¹ÌåÌýÌëÌ#ÌF°GG,Ñ0âòˆwF#‚„FeF{®}4f‡ç¯4n2¾k‚›8™L4™i²ÑäŒIïHƒ‘þ#ù#ËFîù»)jêdo:Ût«i›i¿™¹Y„™Ôl½Ù)³^s¦y°y¶ùócæ= ‹@ ±Å‹ãÏX†,6+—UÉ:Íê³4µŒ´TXn±l·°²·J²Zdµ×ê®5ÕÚÇ:Ózu«uŸ…Í›96õ6¿ÛRl}lE¶ëlÏÚ¾³³·K±ûÁ®É‘=׾ؾÞþŽÝ!È!ß¡Öáª#ÑÑÇ1Çqƒc‡êäé$rªvºäŒ:{9‹78wŽ"Œò%U;ê† Í…íRèRïr4stôèE£›F¿c3&mÌÊ1gÇ|qõtÍuÝæzÛMßm¼Û"··WîNî|÷j÷«tpùÍ/Ç:ŽÝ8ö¦'Ãs‚çž­žŸ½¼½d^ ^=Þ6ÞéÞ5Þ7| |â|–úœó%ø†øÎ÷=âûÁÏ˯Ào¿ßŸþ.þ9þ»üŸŽ³'·mÜë^À–€®@V`zàæÀ® Ë ^PmЃ`ë`Aðöà'lGv6{7ûEˆkˆ,äPÈ;Žg.çD(ZÚ¦–Vv/Ü*<+¼>¼/Â3bvĉHBdTäÊÈ\3.Ÿ[Çíï=~îøÓQ´¨„¨ª¨ÑNÑ²è– è„ñVO¸c#‰iбܨձwãìãòã~™Hœ7±zâãx·ø9ñg Óv%¼M I\žx;É!I‘Ôš¬“<%¹.ù]Jhʪ”®Ic&Ít1Õ$UœÚœFJKNÛžÖ?9lòÚÉÝS<§”N¹>Õ~jÑÔóÓL¦åN;:]g:oútBzJú®ôO¼X^-¯?ƒ›Q“ÑÇçð×ñŸ ‚k=Âá*á“Ì€ÌU™O³²Vgõˆ‚D¢^1G\%~™™½)û]NlÎŽœÁܔܽyä¼ô¼Ã}IŽäô óE3:¥ÎÒRiW¾_þÚü>Y”l»‘O•7À {›ÂAñ½â~a`auáû™É3éIŠÚf9ÍZ2ëIqxñO³ñÙüÙ­s,ç,œs.{î–yȼŒy­ó­ç—Ìï^±`çB꜅¿-r]´jÑ›ïR¾k)1+YPòðûˆïëKµKe¥7~ðÿaÓb|±xqû%ë—|)”](w-¯(ÿ´”¿ôÂn?Vþ8¸,sYûr¯åWWHV\_´rç*½UÅ«®ž°ºq kMÙš7k§¯=_1¶bÓ:ê:ź®ÊèÊæõ6ëW¬ÿT%ªºVR½·Æ´fIÍ» ‚ —7olØd¶©|ÓÇÍâÍ7·Dli¬µ«­ØJÜZ¸õñ¶ämgòù©n»ÉöòíŸwHvtíŒßyºÎ»®n—é®åõh½¢¾g÷”Ý{B÷47¸4lÙËÜ[¾ìSì{ösúÏ×÷Gío=às á íÁšCŒCeHã¬Æ¾&QSWsjsçáñ‡[[ü[ý2ú—G,T5<ºüõXɱÁãÅÇûOHOôžÌ:ù°uzëíS“N]==ñtû™¨3ç~ ÿõÔYöÙãçÎ9ïwþðŸ M½.6¶y¶úÍó·Cí^í—¼/5wøv´tŽëç©QŸMUü'¬> ªÄ €Á$- îQ6Âa 1 Þ•[õÄ`€zx È3=ÜÕ\4xâ!¼|m©€Ï²ÁÁ ƒƒŸ·Ádop"_}¾T ž 6+QÛ ]ð­ü¾~zè9º— pHYs%%IR$ðžiTXtXML:com.adobe.xmp 1166 938 A;iDOTÕ(ÕÕ P7å@IDATxì½ ¸fUyç»j¤Š ™dQPŒæÆ~MâtÓ}on®;ƒÝ&¹×hÛš¡3t'·cÔn“Ö{;f21ÆØmÚLÄdTE ÅX@M½~û«ßwÖÙõ}ç|g¢êTýßçÙg­õŽký÷Úkïýž½÷WJ( ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A  ‚@A ,cV,ã¾ïi]_Y´½nìËmµ.¿Vw"ô t°Ûº£¬EGúBŽ rŸc 2^¿LüàŸù78>GÊô¿9þxdýÉú›óOο¹þ˜º¾beÌõ×àÜ™ëÏ\çþcp­”û¯ó½ÿôš{à%ƒÀ†€ „ÃZU+Þ`Á£¡‡LR¯m[W·õ£­%:nÚQê”øÄtК©m[W7øO] ;ï,{Î3±ËüËñß7YG†kJÿ8iÛÖÕmqô¸³Ìñ—óŸsÀyC™õ7ëo»ndý®©+'mÛºº-Ž®»–{”-éW~ð #¦b%NmÛººÁ?×ßGw–ðÝœ7)ƒÀn…€“•Nµ'ÚN^¾Üv«K¹:”ú«Õ®N™ø 0 ±'H¼‚ÿô¹$>âÕbÕ·;äêPŠ':úÈü‰•؈Wæ_æ_{,9?œ/ÌçÊŽ©Ôµ[ê­Ž>rü‰ØÔZ%6â•ã/Ç_{,9?rüM;+rh·˜eýÉúÛΡœ<åµ:¼r1‡§õ>Ô^8¡ÏYoO ð W0Hû8㬦ìÄ;øÌ¿ÁqžQ‚MŽÿâ2î8ÇÏñ7@@|<Þ²þ p¬?Y8F\g²þæü“óo®?X˜® žGiËsžTVÇkùð í<ßPB¶±Éú3…)xˆ+¥˜Š#myêUֲß>‡‚ÀŒô”üÔÛE„¶ õ–ôCi[HØê¯ìyõõÑQ¿õ¡m[“Òzß_âOá)vb%6ðáIÁ€™˜dþ;3¦—Î#Jë9þµsÇãIôÄ*Çßà8ñ¢.^òrüÊÎä<¢´žão€S;wœO"(V9þrüõæˆó¥CζtQZïûsŽÉÇ^]eò(¡Ä`NPÖÿý¿Î#Jëγ;ç“öêfþeýëÏçHʽvb´ ˆ ¼v!.Úòh»ÀÈs!§-õm”%þ!±kñ@ü§æZ;¯Ä Œ2ÿÆÏðrüMÍ#ñ l7êm[¹s-óDvžK9þrü;O fÌÎs†c j·ÓñŸ¬?\²þv‡ÌNçò¬¿Y³þN]ßµëÄàˆÉùg)¯ÿÅx¯*½pÙ›í"ÃOÍCü¼?I žrÔÙ˜|Ú kíj³“Ã(ûömÜÖWëg¦øÊð/ú²ºn[ê!7>2/¸àÙ6®eM‡1Ôwœ´•a“øÁŸ9‘ù—ã/ë+bÖßœrþÍõÇ`-uýÄ5—×]–hºþeŸë¯é×ß¹þÈõW®?÷ÜëOî1½ÿdl×Lù¬‰Ü·CÈ=ÿPöíiëò²F®¿£ì[›Qññµ×ì äNw‡;nøL<ù`AÝ““È *¿²:r‚QJ}}bÀ×qàAýøèÁ36ž í'2 }}¶<úË ¾ØÐÞ:þ(!Jý¢“øL‚ÿ`^eþåøËú3¸@wÌúË™c@9ÿäü›ëÁuS{Läú+ן¹þ\Kçþ#÷_K}ÿÉ5ªD¬ö~Ýù‡Žç*¯áú÷¿ø@æ†Mߟ¶èŽ»þA¶GS ž:PÆÈf’@´¹ à3A(Õu’¡6“?Ôûdæ]·öFC{ì÷ãÓÆ?2bÐnû«½ñ)Õ¥$þæ6´¡ÄŸÚâ.àüAb@̳̿Y¦ÖÔ¬¿9ÿäü;8?p¾ôü™ë¹þšZ+sý™ëïÜäþkw¿ÿlÏçýû?ÎoÈYË<ÇQ‡Fÿ’©¿ýû$øÜcIpöÄ:ØLÚîLêðØ 3”ò,”ð¼‘Ð}ˆ6rH™úÞˆÁg£MILêýøü‡B~?¾2ú =´!êPâOO„€ [ðÏücäø›ZgÚõÏ5^ÖŸ©µ•õTl²þæü“óo®?rýUÅJœOsýÙA‘ëï ¹ÿ¨8pÀáuTî?v¿û/ì+ïñG]ÿ*ÚmýGëF÷¿×ÙØî䢿G ¦‚ÊNrGYÂggº#Ù©r1pRІŽ6´õ]«C?­-rmkÙÆwQÁòÖmã¶6ôƒ¬fÛGûVÙ}lña\ËÖ×RÆßÕãOüìÿÌÿÿ®{–Yÿ¦ÎeYÿ§.ü˜‹yþÍù'矜rþñ¼c™óOÎ?Þ¯íÍçßzº&Õ¨ïŽç_ÏáÞ‡Ûö¦ßPÛwêè«ëqo©­mì—51Ð=ÜÉŒÅ×–ðÙiðä·;‘ËmâxÊà³¶6è@¶Óú¢.ßø”&…Z[|!CÿɈo c&~ðw.87˜‹K5ÿaÌÌ¿Ì?ç‚s#ó/Ç_ÖŸ¥¹þðó˜Ëú›õ×¹àÜÈú›õ7ëoÖߥ¸ÿuqÍwþQyÈzä=3|È6²Q¾ä#ƒ(¹ÿÖ¯6”Ö5Óú‡¤ÿ¶„¯/}À[¶Ä –39,Ž…Ïd›’ ÒF<E윈$• xèÂ7|ÐFG¿µ:|¨¯Žz£âë ¼ýbÄg\‰?ØGÁ¿N†J™Só, SëMÖŸ¬¿9ÿL`1ßóο¹þÈõ×Ôù–sm®?¦ðÈõ3"×_¹þœ:ß.äúS½ÿžÏùë¶ç|I·´]ËäsÏø¶)Ù m­eö—-GbgÒwv®;ÅÉ›;©V‡mùÈðã$S¹“ºÈ8Ú)3.¥™Kdlú¬Õa[¾~Œ=„|T|ãh‡¶ò-á!3N­vdÛf;~ì!äÔøŽ_ÿ‹_?»jü‰ŸýŸù?µþåøgõËú—õ?翜ÿsý“ë¿Á¹ ׿ƒób®ÿ§’íý§¸XNzÿµ§Ü´÷o^Os ÑÞgz]ŘåïŠñ³æŸþšìi÷Ÿ?Ÿý>ËŠq¹‘}v²™l;äNµn›²_wBµ'L|·„äB1S|&É$[mŒoLÚÖ‰ÏæY«CêÇGo)âÓ—Ä`;¿V‚ÿÌåÌ¿YÆŸæ»þgýÍù'çßœg»þÌù7çßœsþåZœ bMð>eO¹þp<–Œs1î?ðƒO¶öþ{Òë/ôf:þðËþ€Œãþic4–Ñ_¾ÜHàÝÑîJÈ2h ÚÔ±k.xú ŽœMžú´±£dëÇGÆä1¾º²:‚ÏæÓBêÚM›…ÆÇq¡Äþ™ƒc!ÇßìëHeý¬®s]ÿÁ0ë/(äü㋬?Yf»þtžpÎvîdý™Ûõ7fý…©9D=ëOÖŸå¼þ°B³Ý£3îø÷õ8ÖWŽ|¡kY«#¯½&FîÚLjï¯ìã¸øà¿,Èìl.nì 1íNCîVß(¿ª '‚ut!uÝÑð°ã‰!eÆ—ÏD3iÔ?ª_úÁ²ŸÔ•õã·þÛøkªÍLññ­OÇß¶‰¹øà’øã÷ðÏük·þ|Èñ—õ'ëï`p,x¬PJœ·ÆÿrþÉù7×Ó¯ó\W<¦ä·mŽ1õ¨+ËñÊú3}~µ÷Y³þ.Æúë±¶Tëþç³þÑŸ¹ÿ&Þ\WÓ\ã·k°øìV%ÜÝÉý¤¿€ÊæÎp1£l·B_;=v²¾õYY¹ãÅ]'‘¥I+}Îÿ3Å'¨ý¤Ùê³Å·/Žëök¦øýñcŸø À"øÏ<ÿ3ÿ¦ØÌ(Ç߇¬?ƒ¹õwòó/3'矜&½þÊù'çÏ3ƒ³Nο¹þÈõx\ìI׌É5¿­;ç·õQã÷¾ß]ôZj¯?H©ŽÚa3*>ºýø´ñI©½eeu2JI{Û»Ué‰y·êԎβe»cû ÚF‡:›vîw2;y««ïÖ{6õ+k$!G—„”u|¶“Œ~ÈÓe¿n¿gŠMK´‰ÅSHÖ5éø±c[Hü™ÆOìq¨³­'þÔ~7ÿÀ­%ڳͿàŸù—ãopœdýÉúÛžsÚzÎ?9ÿ°>Ìçú+×?ƒ¹ãu e®?§ßä‚IÎ?9ÿ´çœ¶¾'Ÿ\S—rþs|Íuýs×}KýØßêzxÏJ]BÎÆZG©][ƒý OÚÛÿÈwKb0»+µ}£ÎF–Q~»ÜaîNà;IB¦-|êø`ƒÌ`öã S—Ì£úÆÇ— îhúMß>»qñáOŸ>Ì7>cJüéó¬BÒaü3ÿrü;þeýÉú›óg¹ŸÿsþÍõG®¿rýÕÞç°Ž0'rý™ëϽýúss=f»ÿwýé=?Ç“uÏ·ð¼ÿ·Îñ&:þàᇭ½ÿ›>©C£â£Ó?þÕïŒv‡?dwè‹}L€ââÓC?á)klÇÐÚiC‰/:ê-}mÜYÆÀ™“ŒRž|tÙ$ø´µ¡ÝÆGfüZíü£ƒßQñÛñ/Füvü‹ŸÒlãOüÅßÿÁßÙ7µFŒ;þ2ÿ2ÿ˜¬SåB×ߔݟ¬ÿ3Ÿÿ³þdýÉú“õ7çŸÁ9#çß½÷úÃ}O¹ûßöú‹ëþ™î¿Ûóï$ñ¹'÷^bÜý÷`&þâs\|’^m|,Ї´±.&Ä4¾ºèìrjAßå© ?-øô‰I±¸Mà`¸™?³²øq|Ö)±mùî}«ŸºO/±ã©Ão3Œø¢oèúÁj|Amêè kùm|ä´éãLñûã_mœIâ3ñƒæ_Ž¿¬?ã×ÿ¬¿l<Çäü³óù_lê)µ;ïçü;Àa¦ëŸ\äú+ן¹þÌõg®?G]:/ærýÅyØs1å¨óçhø3ž¨rïÿÇÅG>[þëïË#mãÛGd± ®±vr»M‡jGÐ>ÑGÀlwmI>z€Mé¸úmìܰëëUVŸ¤Œ›6Y¤ßÊ&yì7únúÑúãâ#ƒð£mm@ØŠžüqñMÙeu4S|íƒ~l[&þ`¿¹݇âü3ÿrüM­±¬RÖŸñë¿ë‡ë,˜eýÍùÇù`™óoοÌ\ æA®¿¦î;<‡äú#×ÐîrýÅÜ\Èõ_»Þ[ÿëL÷_­ظvp¼`‡½çÙZÝéú ^‹+u}xì©ÓêÁƒ»?´mãwŠ»òß•}06€,D¿ì›;¾  â(}&rJôñƒ?;R>õQñµÕF¿úr·ý5>2|BÆ¡®/ãã>¶Ú´ñµGgTüIÆ|ÚWÚPëo¡ññ7nü‰ü3ÿrüeýYºõ?ëoÎ?9ÿ޾þËõG®?rý‘ë\,þõ‡OÞ,ÅõÇ,k7û jïW½ÿöþW=ïÙi»µû}Ôý·¶ê¿øÂv¦ø“ŽŸ¾ãTüÊîb ‡Ð1fÇØUèô®&núAúÀØGKäÔûvNAFG ©cç¢Í)³®}'Ü!W†®RßÊðï„–§oJü¶ñÑÔé×HÈ•QŽ‹?jüú¶œK|lÐO|PŸÂ!øŽ«þüÏü›ùøï¯?ƒY5þøÏñ—õ'ëïÔºëy(ëoÖ_Î=9ÿL]+släü›óïL÷¹þ˜ÛýW®¿vë¯ö:™úlçæ9Äþc£méõmI™mím·ºðúñõ9jýE¿ŸóVß§1û}Á^]Kýw—‘Ùeؘ~Œ!Á¢ÞòhKØ+Ómý:V|Á§M·OØÁGÏÉ‚Ž>õ š4¾¾ôCÛ fçhÇãÓÔ†¾hG¿[jã÷M µã§Î†l.ñ«zâïÀ-ø3¦Èù&Çvæß‘8n²þdýÍù'çß\äú+ן“_ÿsÍõÿ`ÝÌõ÷àzÊ¿^o÷ÛËéúÛ¾2êÞ—{ÿ ¿?ÿµ¡D&Ù¦dóú[_^ðdP›pÕžù5*¾výùG{®ñ±±o”}Òzö¿?þ¾Í“Ò¶3OJ°APØAìPú#X*¸èX¯Õár§«Œ:›¾ð½¶nNü°‘°AoTüÊîúƒÔßa¶±¥ßèQÛøðˆË‡³Œº #JÚãÆ_E]|ûm?ØBãâ#›-þ¨ñãÛqáƒñŽŠ-”ø£÷?Øÿ™çæßüÿaY²þfýuýÁ±‘óOÎ?3]æü›óï|ï?rýÁ ›ë¹^°ñþw®ë÷Ö”ãæýiïÿÛõ󽿲†6Þÿ¢gìØ ÏµýøÈÑgkã{ï}tëà1îþ¿Õ¥Ðz’ÿ|WàñzT?ú¼ÙAí*«#Ú軣¬ë9À¸êéG~Uö¥µ¥î/¦±“‡>ÔúQ†O} C|líekg»²;ÒVÙÆ>1Õ'–>pØ¿ñ´£Ý’¶ú›küQãgK}ÛNü)‚ÿ`.;G2ÿf^ÿ<ŽÅ+Ç_Ö?×V•¬ÿÓÏÿ9ÿM]+äü;¸.›:ûN­®§9ÿäü3ÓýGοÓï?rý1µ†ìÍç_×ÔŸþà—ͽÿæx“\£)!ÎíýùDZëyÎý¶_µÙö^;©3nýï_GàÀµ@ðŒ£¬oGê—ćǸõ?’6¶—¼¤»‚ÜqÄûÔmSÊ£0“"´ N(÷*úpv|kO¿;“6ºøÂ/¤Üþôã»CûñÕ§œ$>zlöTü¶ÿôÃñÛçÖ¾Š;jãÃp¬ýñ÷ãÓ†(±¡\h|íÇíb8þį`T þ™9þ²þdýÍùÇó'×ó9ÿkŸóï`.õϯ¹þÈõW®?§nÎûLJëGÖŸ¬¿£Î?Ìêkjÿþw.ç|ÌvýkÌvýÆNŸ6­ÿö^ºS#oÇ?.>Ç˨ñÃCµñiÛøP+dž üÚþ뫲Ÿ²sON´©(œønì0ê,Jìù¿9„míhã8-¡£¾rtàŸu¨å·ñá³Aè"ÃOŸº¾kuHƧTÞc|äÖÛø83~ëÇøŒì,ö}2¾ýv,´©cƒd¿¨Ï%>ºŽ[ü¸ïÚø£ÆŸøÁ?ó£&Ç_ÖŸù­ÿYsþÉùwêú'×¹þÊõçàš"×߃{îwrÿ1¸Æô:‹r.÷_SÌ'qœëý§vm|®]¼ç­Õa}Üýg»î¸û_ôa|ãÐdøìut]ChKúC—˜­Q6žŸÑ7Nk‹ÚÞ ò¥™zØ-9øÉ$è`‰ÛÖi Ž;H}ÚèÊw²:™h;–¶tRá§ÝÁê£Ûî@ô í³4S|Ç€~FÅÇ|ãÛ¦ÄÎØ­?ûÑú…çx°mû«ž}/ê­~?¾º”nƵԯ}kýaÓ×Óç$ñ«ù´ñ·þú~€uðÌUæN;_œ§™ƒc*Çßìë_ÖŸ¬¿¬¬nžw,]WrþÉù‡9‘óïàXaíÌù7׿ýu2×_Ëÿú‹c»¿_—òüçZÒ®'ćXo‰­¬¿þÚö<÷¹íõomvÔÞÿ¢µçÚŽ›’ 9¾è±ŒW«]Ÿl¾6Ä@.‹6þ –ç-‘ÛŸ>þðŸ¨'%ØŽ Æd%méNœ~[ Ñ¡íS×XÈ™ ;2¾~,§?ôÑ•ôIÛþß®Në„.4.¾6”ŒG}lúd¿úñÕÓrǯ¿¹Ä7Ž~-å/$>¾ðc_ÛÒñǸ–ò€ŸóO|Är¦ýnðÏüs®´eŽ¿Áúë:ãqe)?ëOÖŽ›¬¿ƒyàñáZ’óÏøë/°Êù7ç_•¶Ìù7ç_ÖN¯3\W-åïŠëb/U|æ=ÇÁLó\|±¡l¥þý¯¾Ñs”ÚÀW§jyàµçÿÖ—ç?xêZ×qÛRÐEòÛý?JoÑytòÉ"0Å`†º‰;ÈÐí¿:Ö}óWÓÚ1!·­ß6¾rûBÛo!Q§~?l³ÅG®_ÇWYÓVȉOúÆøÛøØŸ2>znŽ¿íg§\ÿôãkCÙö“úLñÝ7‹ßþ%~ðÏüüçø¬hY¦Öm×ò…¬ÿY§Î©Y²þpLyýDZÁæu+PÖŸ¬?^7gý¬9ÿ î?]'X#$ϯ´ÁÉ9CÉóüßê¡ÛÏ? kläýøíþǧíÖoÿ·ñÛóz´Mz¡7jý!þçø+kiÈA-÷)¯ hb¶ÀÁ·}yغ> žucioýS¢oëµÚÕ±k·6¾q-Tø°n_kÿ>ÇÅGÇøÔÕmûn|Çì˜8ÆC²M=øú¤„g_i[{x£|´1ÛººÚÛ®n†û?ñ§ïÿàÏ옚ÎAyír·s®­«›ù×A:íØõøÎñ—ã¯=ÿdý+Y×Yç$Ï)à‘óÏ`n´çܶ.V9ÿÖñðX—œsþÝÏ¿mâf>Ç?눤=ÇJ{ ©Ó®9ÖGÅÇOkCÝãûeÚø‡àCØhg|e­/ï¿[ø¢mÿmWÖo¢=ãÛ/ck¯¯6®}3>¾£ Þ’‘[²Õ±18u&~ð¶)µtýTÑÐ/uuÑw‡Â§Ž¬•Û6áB_Úɇ\ö×6ñ!ûEÛ ž> ÖNúưN_¨£ƒ¬-k³Ë*ÒOuSgƒäSwÌm]9vPÿ~|}¨ßÇ\|øÚÇzâOÍ p þS™ræ{æßÔ1ìÚà:À|Éñ78fÚu„:Ôò]sà[oåðsüMs9þ¦°àü•ãoê¸ÉùŸÕ"ëoÎ?9ÿLrÿ‘ëÿ©µ“uco½þðºl®ãWß²=ÿp^n¯ã¨Û¶DŸµ ]?­žþ¼æA‡yíÓ=È=ÿëäÎ|¢‡\R™}Gf_Z9üöúzêß}ã©Ó¶‘Ë'ö’A–’0Ä ˆE[kuÈgðOí´ý|xnèã}ê«øì`J]vò–o¿*»#tZñFÅGYŸÔ'‰Žý#ÔÆ¢ÝN"Úm|Ûð?ú3ß8bîøåP?><ÇO½?>r÷‹v‰?Ø÷Ájáe^dþ Ž©̆¬?Yç Î#RÎ?ƒsxäü;µ^zýÕ^ÿäú#×_¹þÜ÷äú{p=Áº™ûÝãþƒõ¹—³]ÿ³¶:ÿÃë¯ÿ´ûë?÷[û¿¯oxøƒ¸þ¢ð uðC¾úm|y”Ø›˜)~Uëú… ¾¡~|xíøi«/[âж^GVÖÒ–’ðo ÁiÊÝiöÃ$ÈÙ”Ú¢‹œçNjc!Ç7úòLßú…'!ƒÔÁN_Æ×'ñy“Éɧ®x}ÑãѯrJ쌉Žzýñãžã¯Õ!ÖÔõÓÆ‡ß>¤%26÷“¶•ÕÅjñ—G é'ñxˆ¯¶¸¢¡\Ü‚æ_Ž¿¬?í:áÚÁzÁºšõ7翜ÿ§_ql@žGsý1ÀÃë ×v]AC¹¸åú#×¹þÈõG»N¸v°^,Æõ‡kÌLë×8£î?±é_ÿÀ“ôíúÏ8 ÇÐ÷ëú‡>uJ翾äWQ'o“D­Œ:6”’u|êqÑH¹2Ë>þŽÇþµ>žµoy‹V·‹æ°qÔvœ:8lʈO›Á \_Ž®¾õ¿ÃdßIgЃԇOü¶OãÆ¯.¶ô~:~ýUVçÛxŽÅv_Ûqñ[_&ßÚ~Nß±$þÔü×à?8æ2ÿFÿÎŽSse.ë_ÖŸ©s›ç?çUÖŸ©9՞ל3â”ã/ëOÖß©c%ëïä×ß®%¹þÍõïr>ÿzÜž'çzÿÇõÆL÷Ÿ#èxÌxþ%N»þªg|JüŠ/uuô…N+o×0ï?´áG¸Ð…à±µãWŸØÒž-¾6èA£â˳ϴÝÚøê9~|/ 1¸¥"Aw€´½ µ$6rv¾;Á ¦Šv ;E»*îPêê£g||éÏøÊõC ôGÙ+«ânRPÂcgâG¹}kã9t¬S>ã÷ PG9meµ:œ”ðˆŸ:ú´[{û£oJˆñŸ¾:­½²*ž1>6NÜÖ>ñÇÏÿà?úøoçOæßà¸Ìñ—õ‡ã‚5zÔúŸõ7矜çÚöü‘ë\0¼¶¥„rý›ëÿ\/Ýõ7Ç™ÇÜ$Çç.õÛõÛ}Ä1ëõ¼öþ×cZ{t½ŸÇÆøÚ+‡ï9[ëêéOÿ­ññA,làÑîÛÛ6^U®?Øxc<å´•ÁƒÚu‹¶ñ•Ëxð•p¼”„Äài .q‘µ|dð´CÆÆr»3ksH}Ÿøð‚_{Á§„Úø´ohߟ”®Dü>á߉€~Œ§Jú¥ãÓf#6¼vüµ9$õ)%êÆÇ?<ñÃ'<¨¼6~ÿ*úèRJÔjñþ™9þkKÖŸ¬¿9ÿ Ζ9ÿN]ÿ°F²åú#×_¹þœºþÏõ÷Ì÷?¹ÿXž÷_®õÞÿö×ÎÌývþ³¯[²¤Ž¾÷Ÿè{ÿe2¹~(ß>Õj§ƒL^¿‹’ è@òˆÏÓIÄfC®½ee cà[ôúç?ô½ÿG 2–¶î€O|ìú2u\Ú‰;j0pH0í¼|Ú ¢Ÿ6ƒU×veu<|žàRbG‰ ‰vGÕæpG!×¾ñàC´‘_Ÿ´©CÊßî0äso°…ô?Ûø‘;~ìðãøé›ãw\Nã¡ÑÚÄo}ÒvüÚ·ñk9I|ì¡Äþ™ƒc¿9þ²þdýÝùüŸóÏ`ÈùwêúÌë¯;,sý1ûõ_®¿¦Ž§\ °ào®?rý±˜×Î,|rîjÏ_ÈFÝÿù2Ötl=ÿ×êðþÜõŸûsu)YÛ¼ÿ­ÕŽ<Æ‘·÷ó´‰áÏùO©®}GmbàÇ >öçŸ'êÖöy’øØ¶þÛø­/bßñSöã3&xl6P«7à,Â_ƒ,‚«¡ L§Ûár0ÈC@qò´ºô]wœ;Ô$ˆqªJ³ÕGÇñio6dm|ÚÆ¯Õ!ØØ\·ãëvrÝάÛé;ÚûÕè°ÑJûk|øè9ñju8 ´oǼï¾áÏñ£³­üÃñ·}ϵñ퓾ñ'6³ÅÇWâOáü3ÿrüMY¦ÎYsþÉù—+†\äúkê3ן£¯ÿ™#¹þ`÷(Ö)sÿ1u½µ»Ý09¾Ùã®ܧ–Þº_“öêTVwlPªºÄD¶ñÑ!>üGëvGÝ®­Û5uûvÝnªÛƺaÏÆýï¨øú¢Tr\|úBlô!t½Ç–‡{d$Åì?<êñ©£¡OÝømä‹Btb±‰;xK"^;(äöÁªk‰='èÐnýÔfÇ7QcLø¾Œ…Úú¥¿r6ãâ={Æ ?tÌ1ÇžZËÇX¿~ÝÊ•««ª$ŽBA  ‚@A  ‚À²@`Å£Û¶m½÷á‡Útçw®½í¶[¿]ËOÔ®_W·+w Áœ9 r&mÈÀ•¨!£Ž ¢Zò¨·I-êæ!ÌuØFW¥y ãÛF:„->¼Øä`Ú’ ž1¥OÓ0@õjµ«ƒ¦tPªG}¨£}êm|ûè7Œ´%)ô‚Õ«W?ÿÌ3Ÿý#ÇŸðÔmÛ·m?qóæÍeë–­eËÖ­eûvÜ…‚@A  ‚@A å€ÀŠ+ÊêU«ÊªÕ«Êš5kÊŠ•+núîwn^yÍ5WlË–-_¨cøRÝ®›y@ý¤ùs &,«h˜$"瀭2êæ?ÌGX¢CÝüG­v¶ôÅ~X·?Ú¶%vÆ£¾`ÂùbQë‹Nš¤$Úð£mS‡Ð%yùx˜mý"u6ìh¼~kµ«c‡ŸlêÔjGúÅκzßsä‘G¾ôÏxÖË<èÀS~èáòàC–Çç5ÆPA  ‚@A  °œØgŸµåÀõ–ÖPzðÁ®»îÚ¿½ë®»þ¦Žéâã"?@¾A"áDÈ ò ÔÛœ6´á#g3ÿÏü‡¾*«óK©b¡kþ™ù}Áƒô¯mc´ðÇ‹I ÒᛎJ¶$2õ)Õu°ð¨»£Ô§$ ¤¬åc¸ÈØ ~i|dê·ñϪO}ÿ/|á«Ö­Ûïä{ï½' #  ‚@A  ‚@ØÃ tØa‡—Ç{ô[—|þóŸ¨O}¦ñŠº™' ‡‘›`ƒO ŸÒœ¹ˆ–ÌqP"kíZ]êøÂÉ$H}KdIJÝö›ñjuØnyðçE8_lj3tR©³ >õvpÖL@ªZ§G© uåú£ã¡˜¼†¦oã#£®®õ*ïÕg=÷¹?øáGÇ|3+‚@A  ‚@A ìÉuÔQåž{îúî•W\ñž:ÎOÕíÁº‘+€L‘C ÷ÀfÁ::¾é„ÌäŽòÊêlô©=|u•©k‰b“ÿ ®OxÚ´þ¨Ë¯Õ… •Å"}Ñ9ëø¶³í€”SªÜmÀhu¨ëÇ{“Gȱ¡ цÐqÒ†ßú²þ¬#ŽØð+Ï9ë9O½ãŽ;OD1‚@A  ‚@A ìùuÔ†›®¾êª›îºëÎ_«£½ºnm^‚\ƒ êí67AîÁœ2ˆ|ƒyí°áé"dðÚ83åEô?7|Hð ý Z üÛXˆ+ü8`ë”-8‚©ñ¨Ã‡Ôµ„(&|Ð%!ˆèÀhåCÙú¥ÞúlÛêÉ{ÕgžùËûì³îø|Ϩ¢ A  ‚@A  °— Àkk?þØw¿zÍ5¿V‡ü麑`#AþÁ¼9„6ÁCÛ¤%yˆ¶9 sÊÚ=âP¢G,bÈ'>r}Á‡ÐÕ/¥ö}=tçE8](™ÌaPŸÖåÓd’=‚Ï ”Õj7`Kô!tÑÄÖ^Ÿê¢ß‚&°Æ ôWÔðc|ô~õùÏ?ÿe<úè)µ A  ‚@A  ö"ößo¿o~éK_øuÈ¿V7ó>DÞÁ:¹6ȼ‚ù “7”mÎÄDù íÛÜ :ÆÕÞ¤vÈ(ÛøúiùæEŒ_MæGmçæça*Iƒý¨NÂc°”ÖuȺup–êYÊ·ï¶±' .<@²xòkµ#}äÜsŸwÚc?qœ‚”A  ‚@A  ‚ÀÞÀº}ÖÞrÙe—~³ŽöŸÕÍüA¿4±ßDN[’§ Ï@.‚ͼuùØÂW®=|sµÚõ¡_¢C®ƒR{íl·q°Ÿ7ÑÉÅ ;LG~é0$HÆs ØÉxÚ†-Ä€[>õž ".Di‰ºþ‘aÁ§n|tèÓ%g>û¬c·oß¾_­‡‚@A  ‚@A ½+V~ ê "-ÄIëÃNÓaøv– FËGÎÐÚ0hxØÀÓuªÕáŸ[ëKðaçŽiùèCƺüŒ3Ÿsü€µøy_rß}÷-ëÖí[Ö¬Y]V®dØu Û¶–Í›·ÔŸÿÛT6mÚTß©|bñƒOà1ý›¤¨ ‚@A  ‚ÀÀW¯¹êæ:ÀóêF‚|‚¹ònäÌ5 q£OÒ$Ÿ;ü`Q—g쨛'ANÛøµÚ‘vƧ-výü ²y“™·ƒjHçDû&†p`ðäcK]Ph›ÈAŸ§‰ nâ]H‚¾¶òÐC® ß%²põà_ZGÇ¢¼˜´víÚrÐA•ý÷ß¿&‹ìÆèÛ¶m/<òHyàÊO<9 ¤ôoô¾7 ‚@A  ö>v$Žž_GN®Áœ‡¹ ræ-¨s“O[RÏ9y6ó%ÚÐ&A‰Üœ‰'êʵ¯¬ŽG‰œøöÏdºÆDF?ó&ÏÛA5db©_ÚvÔRBØ©'O>ƒBæ&_=Jeø¡-XÆë·ñéÕ•ÇG‹š8Z¿~}9äCÊêÕtsrÚ²ek¹ÿþûËC=4¹Ñ<4Ó¿y€“ ‚@X¶ðOœƒ>¸Ô×ÒË?þã?.ÛqØñú8}7Û)ƒ@A …#PG·W/Ï­¹“.æÌWÈ'7ÑêÑ&Ÿ²M`"'aò ž¾àÓf3^­“LÆ Ô†8mÛØ©C}΄ñ|ÉÄ övŽ:U&‰ÈÎS¶ Œ6–è@è›qó›FðÔÞ: ¢g?à¡ç“Kµ:$}R²#¿RGG¥ ¬|ðAåÐC]—ûlÜøÀ‚|Œ3NÿÆ!~A`ï@`ݺuåøã¯OI?^nºé¦=~àO}êSËI'Ôó¢‹.Zvã]³fM9ᄺ'™÷Ûo¿²jÕªnßñ´ò-·ÜR¸n˜”H uÔQ“ªw ªë¯¿~bý( ‚@X®ÔÄÑ­µïÏ«Ió mÂÇ¡ùÄ%¹sÔÉKXšÿ@nΤV;2oÑê“£@—Rrýƒ>¤œRº&ª¨»ÕêüHÇó±¦# Âu“?òC¹h㪃M¿Ž><ÈÒ8ÆÇ2€b‡JðØôa_±ƒ:Ô¯¨‰£É¯ ªÁ8âIž#Ž8|œxNü»ï¾gÑŸ}³\Gü¸Æ9çœÓ%¶mÛÖ%ˆx¥dÎXŽ9æ˜îé#à 7”Ûn»­Ý½#ë$¢HÍFø'ÁHÜÏ}îs³©G‚@A`Ù#Ð$ŽÈ-30ÁC¢Df2†6 Ê69£]ë‡\„|u±3G _È,Õ¯¬ŽŒg]û:„uãÛ3élΆÕ[:çàÚŽR‡ÌœÐYïLyúp@Èõ­.mêèªg\ü"‡d|t”ñálÀ†¡oÂȾñÄцNº€?GqDY¿þ€xØÙô¡‡.wß}÷΂ypÒ¿y€“ ‚À€O›òÄ ¯8A|G§txe-‰£Ý{Ÿ~úéÝke$Š®¹æš’b$wÎ:ë¬îµC~¡õK_úÒ¢ hŸ}ö)Ïþó;¿syšiQ‚ÇIA ]„@Mqó}VÝÈ!_0¯`^¾…×Bž8"F’„¤ËÖ­^£Š2GÒ§¸À¤Î\ [0yì±ÇÊæÍü€Êh:ÿüó»þÝsÏ=å«_ýêH¥g>ó™…W¡ø‡èp©8¦s†±ñ*#ø„‚@A ìéÔÄÑ=uŒÏ®›¹ .8Ñ{²§Mž6¹ô¼€€×&ÌGP¢ƒ.6úÒGeu>±å!}h¯¾mJ¾>;Fýc.F¥vêL\hbƒŠm§ða'쥾)‡„$¨}|BÈI䨯¾,u|¡ÙxÔ‰…½}Pðö&ŠÐA®ž8ZЇ‰ãƒÓµ?#i1>”þ-͇ÆGî°0ƒ@»$?üðòo|£Ü{ï½]ïžóœç,zâˆRè€xeŒ¤ÈÓžö´î×ÌHÐ@|Œ›ˆ|ç;ßéÚüáÏ|Õìæ›oû±n^³c,OßïéOzáÆZ2‘síµ×.PÿU5>4M²…ä¿Pöàƒ>ͧG¯zê©Ý+b~#Š×¸Hª0ž»îºk'3Æ}Át|0ÁŽ““ìâãä8Ýxã#Ÿ0æU4ˆ§Äèß(¢Oê-F∤Øyç—§F^A ìÑìHñ«j&z(MƘÐþëcN¼dN}ê\  c£¢®=uÉ\ú­/øÄ’‡>õÁ…­¡GÎ{m¨CÄÑǘäOÛ¹IôÕÑΠt”ºÐÉþ´q¾ƒ§N[êl ¾@kSYxøpê¾~jµ«ãC_ÚaKœ'Žøu’ýöÛ·ºZ|zôÑMÝ7 â9ýãg¡ ‚Àމߴ!ñÐ>½ºT‰#“$|o‡d OóŒ"X$vH¼@$*HâÌôOIñ«¥<¡sÉ%—tßk2‘4*þ}Ý»Mñ+r'žxâ(“î©£K/½´Kæ´ 6lè’V<)4ŽF½ÒErKLHŠñoF}?|Üs!|‘t»ÅzUdIº‰§¾ožªb¬ô‡èà)µPA ½š8º¯Ž•ÄdRˆœyryDnÈœ†ut‘é ¾º&€l«ÛúmóðÕÅúĄφùëµ:­N{bjƒMlTµ(;dGñ…:&hjµ«SBÚ9PxúnmáCúTG ÂkÁ¡¹}SW¾¾ÐåG JqAºX¿¦F[â×Õø/åB(ý[~ Á>¶A Ý'#qôÍo~³ÜzëôÄ=‰ž¡$™D¢„o ‘xácÌ$,8ç}ûÛßžOÀð$ tùå—O{mk®ß8"itÕUWMóOãyÏ{^—„"!Ä+_’¨¦Íw†|ýM9 ÆD‚‰ä¨ö{ImâÞ¨§™Hþø *äã^•ã©"’T~I˜‘Ì"^s}ZÉþ·%Qçé*nô%ß6jÑI= ötšÄ¹ 7ò mî„r`A‡ "øä(å×jW·DW™É&Úv’1ô‰ú-鋘ÊàAÚQ×?õ‰H')7Jžà&e,ñ©_ ÜÁÖj r|µãéú| ¡ï§²:BŽ>;Œ_MSrý²oö9ul¿²Ðo%1ss…qþ´»ã7ÿ‘Å2 °{"°Ô‰£™^9ã  |÷‡oùA3õÉ×ÔFùkâèÊ+¯,7nÜiÇp.:餓:>¯Âùjß‹^ô¢îÛD<%tÅWìdƒŸ·?å”S:Y;¦6qÄ“H<‘Ô§ƒ>¸ûe4øíëu}½C9¤Ã¨Ï'aÄSXã¾Ô××&1Eò,OC(ü ‚@ØÓØñªÚ9uœäȸ‘?à—È!ø„2r>¬BÛƒvú©¢Î'¶Ø´zØ“ÿ0!U«]>=c O|ôŒ‡B‡8ú¥t£ß­¼6''œÎ‡ÚΘɲ#”tNß´¥¶£„ }ÚÖ_ÖÕ±MÒÈøÔñ_HŸ¶í>ß:ðI-艣¼ v+xΛvwüæ=° vSfJÒÌ·Ëm’dÔSCúåu(ž.‚x匤Äk[<ݵ¯«µ¯©ñdMÿ)ܹ&ŽÆ}@š'mxââ©!_cóE¼böÝï~·“÷ÿðÄÔ ^ð‚ŽÝ>iÕbÒò[ûÖv¦×ÍÐ#A ‰^ÿ#ÑÃ+t`3®m¼qu§ø%5>Ú A  °7!PG¼ï~vÝÈ1@”äx’Èz›8ªìa9¹Krä(á™›Pn»ŠÆ&ƒÐm}X§O­OÚÄ6¾ùõªhîÔvp.ÖØÙ:dýŽÒFÎ`¬×j7J“?È<ú¾àGÀIÔÙì%ú¶¶©·v´Ùìß8:°¶çMùøôÜ>æÙzwǯßß´ƒ@Ë¥Nõ¿õÓâÅ·‰^üâw,¾qÄ·Ž ^»"ICÙ&ž|M䯶ñjVKsM]tÑE­ù°Î·‚žõ¬gumGtPyîsŸ:¸îºëFþr$o%¯ç‘$‚ÚÄѸ¤Ð$:³øÆ ’qàÃQóyòÈ_¶ËÓF#@+ öjâˆG’I‘2‡`âGÌ9O`S×¶y ÛØÁóáüb¡C%m99 ˆxÆÔ7%ºðõQ«][k‡/ã×êdd°É´Zj;EÝÙi’Aí€@´w ”ðÑ¥îF¾m|@ò©Û'ûác[ô;uõ/ß¾ëûªš8ZÃùR~î~a?w¿»ã7ßy» ‚ÀîŠÀî˜8+¿'Ô¾’ækjã¾M´7'ŽÀŒ§ŽÎ>›kÜ2ïZ“0#q–§:ó' öRjâˆ[^UƒH´´I!ò&|Ì{ð$ùr ðÌ1´|ò%zä'ôS«ÏÄr|@Ô[¾yäðõC ÁC¦%D\øø›Ù‘¹iCiB†ŽIÊÍN¶ƒ‡- J [õµW{6ÀP™I"xíÖêÁ‡,”øøÔŽc„â|‰ÿòaË•+ 5_OÓí¶mÛÞý:‹ßY˜.¼•þñ:h( D`©GíCÆ´÷ªòö;>¼®ÆëRþšÚ¸'v–2qÔ> ´ÐWÕÆõ¿Ñ×á ˆ§‰f"¿ÃÄ·›ø†Ó\ˆ§–Î9gp<î;LsñÝ ‚@ˉ#UÍ“/Iò$ƒäQ²q¢6/BÛ<¥2lÑiåÈÐQfRÛZí|Ÿ¥1kµ#ü´Ú¸´‘“8ZÐGÕG÷Ÿºõë ºhôÐCw? ¼ùObú·HÆGA`ù#°Ô‰£ö‰¡>ZíDZ¯¹æšî§í[óÏ?¿û…2’O|gˆ_SãÉø`µ¿VÖê·‰£Q¿z†nûáë¹¼ª†­I™…~»ŸÂ74.qtØa‡•3Ï<³Ói?¸Ý1š?$—.¼ðÂúÏ«•e¦>6& Vž6šKA  °"PGÔa?»næ9Șç0A9‰$JøðÈMwhm|òsê ¯OüètÑcCÇ\F­vmd/Ò?‘H‚@3%ŽH@yä‘Ý·„x=lRj Ø´¾Ö:üt=åæÍ›»oõ“Aü²‰’F$ HjðËa$^Fßçá×À >Í“A}ZHâÈ×çð9*1µfÍšnLûì³O÷Kl|É1µ˜Ì5q„_ž¶"1Ä/Ï‘dõäÑqÇWN>ùänÈí“^“ìÇö5·úÆé_‘´ƒ@{'3%Žüu-ùÚ×¾Vî¼óΉ@j“$ȹ뮻ʖ-[êS¯ë» hÜ«_üZØyç§‹®¼êª«Ê¸$Óæ‰ˆgç;ßét©ó ´Äý¥?ÄÁþï½÷Þ.9ÄÓN$møÅ3¨ÿQð“¹&ŽðçÓ@ÔIñËi^Xâé-G$—HVñz 7h’ýè÷£ ¯þ‡Ç;Gù‚@A`/A &ޏ÷Gä$¸`³Ni| sè™L2q„.røßïþŒ@IDAT¾àIðÌcPBÊ:$¼ñÍ£ÀïÇ­¬NÞòáMDvbe“, ¦O$jLéÓÑyl°GƦ¬V;>m|´¶­>G.0µÚ Ú’Äñ±!~ŒI[{D¦žãÂÿ5q´o-…ãCÏ\nܸ°NLú7™ðƒ@{3%Ž|U 4Ú_› 6IÂÓ+<µÄÏÆ"Îs<½cb§¯ÃÇžy"™ñ…/|¡¯2­m¤e^{íµÃ×½’8Âç† Êi§Ö%Úm‰¶–ZLæ“8?° ™&‘ âÉ£[ÚŒ÷ž{îQ­Ì¶Û_ŒËÓFCØR A ½š8â¿/|ãˆ\DÜ9„öã¹ðÈGPB蛇 Ý&‹ÈU´¹|ÚÖÞ|m|ɷ䢪MTÕfçßm.}Úö¿V»~é¿/C>’Pœ”8 €‚hëÃA GÇLâ Ãf‚=ímmjµó‰ _úüЦN\ÛÔõ¥%º2Ú”‰&;6møUµEK€ÿªòÏÕ«2ÜÙiË–­ÝIýoâìóÓHÿæ‡[¬‚@{ >ÉòÈ#”K/½tÚ°Ž9æ˜ò´§=­{Jˆ×£~øáiòq~’„×ÌðÃ9‡§u ’@·ß~{¹é¦›Æ¹éøÇ{l9å”Sºú¸×ÏZĿ難Ž:ªð$Ô&Ž|‹ËÅ_ÜÉûø ¿.}ñ‹_ì>ÌÝê¼9õÔS»„–ãÁOøðÒ¨'³Ú×ÍÆ=½Õâ6J‡‰/ö‹qÛ~‘,âé-ðni¶ýxÆg”Ã?¼KÞåi£¹Ôƒ@A`oE`GâÈWÕÈ+Kh@&tÈ'ø´pÁgƒOi΂løÓ>d‰‰$ì!.ž°Sžþð…ž øl´[cWv'Ã[tf%:5)X}kO9í¶CµÙ2õé ‰…¨C´©kcÛÄ1äQ9m ¾›¶Ž¡ÕmûÍDZ÷×Áb•\òß<¾½0Û¯­ñëi\¼óQËÅú¦ÑlãHÿfC(ò ‚ÀÞ‹ßÇ!)Â6)µ öé^£"éÂëj“¾ uâ‰'–N8¡ Mb‹sä$D,“+Ä[*â—áÀˆ¤Ñ¸§¦;6ãGb“L"QÄ6ÓuÃ|öãb÷;þ‚@A ,jâèñÚWGtÛ‹ -&]¸8¢m‚><òÔ!“@꣎;u-Éa #g¡?y”|óƆO2ï¡yâêCÝÎ`¶?ͦ‡]6GI›N´ ØÖ·eUú Ž(øc€ðX­vucê»Õ£ÙÚö >6ú£Tû¶¶Ö±ç‰£Ájc±iŸ}ÖvyëÖí[ÿ ºº^hº²mÛÖúaÐ-õ?š›º ÏÇoŸz[ì^Œ÷—þÇ&’ ‚@˜q‰£É= 4ñÃ7…HŽð]#¾o A  ‚ÀR#PGÕg×$ dIÜO ™ˆQ¢´Þ¶Í› ¡D>¼ˆ2x&›jµ‹KƒȨãÛ˜ú „O Ù7ëÆ¡M|ìõoFÒéŒJ;„8w0ꈲ%:Œ¾rÚ>õãÙyê;êĶúøRíþ«gømŸ*²íx)‘Û¶¯—ÖÄÑÔǪB( ‚@˜ MñM#žÒå+^oƒfúú¹õ.ÚA  ‚ÀÞ‚O Ïå©iq©‰#žæàG$ˆ sí+fä"à›ÔAº¹ änäÚºvæ9ô‹2õÛºú¶?”ø%§ƒ¶2øÆGBw"2q2‘rUrt‚:60%|xÈú¾í¨|ôIÞ OI§µÓmùðLGÐäë}dè@ø Íµ|âj‡ŒWÕ–ì‰#„‚@A ìé,4qÄOÏãCºãŽ; ¯¼…‚@A  0)$¤¹&jâˆòÓ®æ7ÈÄñi ¶D‘‹`kÛðñŸ\„vðÔ3Fß¼F­ó$Èä·þ°cƒ'ÑÖ?%ñ‰E©®:•5žp6)‘xiRÇÞ ÊZ=:CrF².[«k_౩[«C Ú:öÄ6ùCÛþP²áƒ+O}×j§o{ˆ61I ~Gn( ‚@˜3|ƒçÄOììøPô\èÁŸ¼ç›=Øó‹ns½à›s§c‚@A ìQ´‰#6éõDMñÐYu#Ï@^„ DÞ€møä(Ùà›ÑÆÜmH]ëæ&ð‡.ñØ mìºú·?ðÔ‡gŽC›Ög«[U'';9¹ÅÔ` J‡èŒÄàl«ƒÌÂ3®ƒ M][|¢O©®:ú¶D‡är6mÕ¯¬Žo‚ »vÓ9¯ªå‰#  ‚@A  ‚@X†ŒJ9ŒI’G5qÄDZϩùò–$‡È'¤gG9ºæ5Q‡Cú"/JôôY«Ólàcƒ%›¹•ZʨÃMÚØB”ö£× gú£ƒ™t”áXç£#v; üêÛÎYªWU:›¾.|H}:ívð¼¶F|“Føµèц¨ËLJ|ýÑ–weM¯²CA  ‚@A  ‚ÀrB`¦Äã˜-yTG$uHQ²‘Wà»GäÈC@ð%r$u ùm¹D}ÔÖæWÐ%¾Ì{ð”ùZ–4"¿A¼¶4¦ý@Æ!S·cÌôG£™tZú`vJàÚ´6È!ô©cOrÆÁLJ=D 7ô‰ž:ØëϺ2ôI _êt²”~®Hâ¨Ã"‚@A  ‚@A ,;fK1  GØ¢+aoNB¶èaG’6þ苾µk} CÛ| eë[=HƒÖ˜¿*Oc›ŒÙwn›ÙIêvÒNã9›ºµ:LôP·O=ˆ^+oÛøôKšú¯¬.!d›äuìZÖ¿RG~€»ª„‚@A  ‚@A å‚À$‰#Æ2Sò¨>qD‚å캑Ëh7ò ÈØÈo 3©C Ïv­vzèƒÐO[G>Ôúls'Ädƒ¬£k~£ogÔ±iýµõ*šèð¤Dr… ÚŒÒ dƒ0#Ø £dƒ½[­v¼Ö„¾zÄW‡Òx&Œ*«#’?ÆÄ–:„ mûL[}ºª&ŽŒ[›¡ ‚@A  ‚@A`9 0iÒȱŒKÕÄ9ƒ3ëfþò$…È!Pg3·AI;êlml óòl+G„¬}ÊÉ~÷®Ðe3‡ALí)±Q§V;=câ;û‰|,i4V¡'0ébgëÃN\]ø 0à©gYYðt9Ô‚¡/x}0é£_dð y”~‰z_nÿð—WÕ*¡ ‚@A  ‚@Ë EL‘´y^ÝL™Ü1Cþ‚|‚2t!xØKð±Ó†¼„6èš ¡ÔGËÃ>v-©ƒ ?èAä<ð’o‰l'&Mj`:BP6Š6AD»õoÔÙZúdøqPÔ±øöÛDÚcç yòˆ:<ˆÒXÆ…ÇF[’Ç«j-_yÊ ‚@A  ‚@A`7F`‰GäHÎk OAn¼ƒ "eÈ­Sb ÉkFúD9¹ cPG‡RûZíxè˜Áž¶e[‡Çéƒ6yÛÈf$̨´Cèè›$_¦t³M§´ÑžAöõh;JÛµÚE))7ÉD¢d\JukµóII 1 t®8ûì³)CA  ‚@A  ‚À^ˆÀå—_ΨŸ[77&{ÌoÀ#¯aY«]=óè"§d3Ï€¼Õ#¡ÿÖÖ|†öU­#üÀÃÿæ3jµãSÂ×›2tà«Oäú¶_ð¨·[mN{Šéòš8RY( ‚@A  ‚@Ø‹¨‰#òüªO‘o ÍFžÁœ%Irmò‡º¹ ôµ£$oÒÏ 5h«‹ ]|‘3ÁÆØæ4Ðѧ%<ô°ŒaLlõ­¬SœéÏ\’$t" vØNÓQÁ©Õa"‡Î cG±ÓW­vut øm]žñôCcÁ£Þ‚c u,Õ¥­Ž}AveÝBA  ‚@A  ‚À.Fà”SN)÷ßÙ¼Ù7–¶C+W®,ûõÖ[ ÄG&„ÈP'ï@΂:<Úæ:à“±„m·ZêôëúéÆêõO›(’‡|u±'ßAÛø´£^eÍL(NJꈠm0|Ø)ê’IÚØ³™`¢®ŸR§Š:"KX${¨SBتoBð!íµ£Ä=ýÑö ¦$Ž*¡ ‚@A  ‚@»Ý qä#LQ'‡ÐOÐІ”£Ãfâ :l&oÔ¡ÄÎ\‹¾á««ÿÊêü"3ïO{JH™ñàµ2ì'&Mb@¢…@0 Crž·^YÃì59d’ž¶”¨Mè±àëÛveu~í :’:úðÃÙ&“ðeÂȾ%q$z)ƒ@A  ‚@A ìBvƒÄy .”ä(MQG‡­•‘wà1)yí+oä(´GNn¢A¾±mSšQmsÔÛ mtSLJý…?+Ñ™IIçsðØR· "8úvs–mÇÑúþÐÁÉ|€qjµ«ÛÆo[oå‚Ïä‘ýЮû ¡ ‚@A  ‚@A`×!°‹GgÕ‘“Ÿ aÇ$ mHz&ƒÐ¡½¶T»vÓŸºä(Ð5¿aÜÊêˆ6º~¨Ã“ð‡-2Jõ-+«#ä³’fU¬ mcg'(¤uåtšd DGMؠϦOíÛäq)!tõE_ø$±„±¨ã‹¶öò*«³Qn_(“8PA  ‚@A ]ŒÀ.N]‡oò†Òäuó>UDž‚¼äÓE´Õ¥Ž›~jµÓ5×AR®.rìÙ¨ã“üd] {r&ð´¡„è7uJ¨µpFüUy„h'– ˜V`P:M]PhÓYlÚê3äÚ rdD}ùUÔùC_ßè¶I#ø¼–F_ð¥žeeuúD|7ü\Q·PA  ‚@A  °‹Ø G&_(ÙÈ3@ÔIQ’kx¢nmr ”lä=Èg˜+©Õޝdè «u|êCe›g1iENƒº6µ:$ú‚Ü~Ø”øœ•Pœ”ÚNØqxÑ—Á‘Ñ9: Ï+¯¬n`¦%„.ölè»ÕjW'>I!õá«+?êµuõÑ¡&˜òÄÈ„‚@A  ‚@A ìbvqâèœ:|òmÒ‡ümr&]àQ7T«ÃDuõåS¢Ož24h b"#¶~É]À£M׃¾¤Žmôà¹{Jr$­mmÎLMJê˜:%!¸u‚Ó Ú”ÆÆŽªO ¡«ÛòÔ…¯Ž¾MøÀ§®.mt؈OÛM[€F_[ʯÔ-‚@A  ‚@A ìbvƒÄ‘ùs$|¨“? NiÆ|mr –$‚ÈCÐV¹¾kµ#mðI]Úè¶>ÈsàOÝZí¨mK[Jÿ‘“(«‹sêl²ŸVŽ1 ]ÚÊi;`êfרCȰÓ¶øDOxú±nŒ*êtmã‡6›mc\vö?ù?ÊåŸýÓ*Z­X±¢œ~úéÝvÆg”û¼ï}ï[˜ÓE±®P­¨9³µÜ^•Û™k‹K§žzjyó›ß\®¹æšòõ¯½\uÕUå‘GYÜ {°·}÷-eÆÁô¾ùæme»GÙ2æå0¾“NZYN:aMyüñíå.!¡¿3­[WÊúõ+Êþû­(÷oÜ^|pû’쫵k×–~ðƒeÕ*—Áûçæ›o.ï|ç;G Ã]TN:é¤Â~¹é¦›êy|Ñ|ŸwÞyågöggõ÷þ÷¿¿\rÉ%³êE! ‚@Ë]œ8:·âgr(¹yfã­ÝÈQÀ'ADžÁ6uôlSçFÏ'Š[G‡6~´C¹7èÀƒÐ‘Œÿ¶©ë „/c §>+µÁfSF×­ívt ”&€è6t†ºsàøG‰vèÂkcé>„|ý!#>¯­á‡§Šäá=äòku ½¯ü»Ï>PÞûƳË=·ÞˆlÎDÂèœsÎ)oyË[Ê 'œ0´¿á†Êë^÷ºaûÉ­¬(+ý¡²ö)o,+VºSè-÷¶l¾õ·+2›¦Ëj‚iÕ†7NçÍÐÚöÀçÊöM_//yÉKÊoüÆo 5·lÙR~ÿ÷¿|ô£-›6õb µR÷½÷˜rîÙ–mÛ¶—ïûo”GU²g”Ëa|ïü×G”üÃ;ÀÏñׇ ¡žº²|ïK×—×¾úˆrðA,#SÄþú·üûwÝ]ê”_4ÚÿýËßüÍßÌêïÎ;ï,¯|å+gՋ ÷ùϾsòŽw¼£\|ñÅ sØX¿â¯(¿ð ¿ÐpFWßõ®w•O|â£…á ‚@A`B`'ŽžW¡$ï@n+|sµ:LøX"ÏÀ›‘£o²‰6|sä"[Â÷ξ±°k >zèS§¤OèÁ'×a?ju(Ó?ýÃŽ¸ö±V»~ãgVêwhVƒª  %餃…§¼TeO#dÚ2 [dƒCN õà£G‰îÞ[}´zú´Ää6l_y×ßo-·]ÿ•ò;ozaÙºì'§•+W–_üÅ_,ßÿýß?Íè›ßüfùÌg>Ó%N¦ ž¬ÆŠÕeß3/ž5Ú¦k N±§ôVTö}Ög¦Ú³Ôž¸ýƒeë]Ô%ÌÞô¦7•ç=ïyeeì 7–7¼á åŽ;îµ{”u¦¬8qeÙ~[F»8¯uîÙ«ËûÞ{J‡Ë{ßwKù‹ÿòðîÑ"õb¹ŒoTâhýúRþú3§ÏŠÄM7o*¯ãwÊ£TšÕ¾¯Ð&޾óï”üã}•®}ï½÷–‹.ºh¤lwg¾ù©ÿ²<ã€ÓË\Q>xëìÖÝ]ÊÄÑSžò”rÁtã_¹vU9ìÌÃËêýV—{¯¹»øñ凸‡;YG»õIç‚@A ED`'ŽÎÞ1rl$È)p¥/’|d†6[_†-¹ù”ä!ХĞ =}À—Lé> ëêc!Ç~Í¡ÔêÐŽú¬¤ÓY«Aút€$Œ¥ÝêÙYxDm»ª uЇϭë—xøÒŸÉt!ûC‰ôÐѦV‡:ÚZ^Fâúû¾§üåz[WŸäI£_ÿõ_//}éK‡êŸþô§Ëïþîï–‡zhÈÛ%Ggü]yâöÿ¯l½¯&‚¶ÕdÄöºkVPÖ÷βú vÝÚöX}íâú›êâÊý«Ýg§Ú³Ô¿å·Ê¶û>=Ôž>zûÛß^_é©wÝ•~øáòú׿¾ÜvÛmC½]YYqüŠrÌ/+Ö®,÷þÙÝåÑÿ¼ë5kê,üô'Nîžd¹ó®'Êkþé·ÊÖÁtÜ•-Zìå4¾™G÷Þ¿¹|èOî(—]þh¹çžmåÐCV–]°yÓOSV®d™)åçÞrc¹ô+sK<ºM}èC*øÀÆ©.[þŸù‡åÔýO-W>xeù×½y·ÇR&ŽøÑ[^þ™WÕ·Š9í•rß~­\ÿK_>e”Ä‘H¥ A  ötvƒÄyòÜ™‘àM\%7^øÓfÓF;uàë>(‘cYG×D|}™ï€g¾Ã~èÓŠþÍ¥à¾)!ËAkÌ_ '%¾í<;‡Œ£¯ÚÈí ¾”#CGè¼úµÚ ¦Ma‘ðqÇ ‡Z_mà·:ÄvÃ1‡‰#š¿ÿÖ(߸ô¯+{vâ5´ŸþéŸ*¾ímo¾Î0dî–•eÝ3>YoP¯ålºúÂÚKvÅd´bßÓ˺S¯SÞôÿ½”Ç¿»“á~ûíW>üá—£Ž:ª“Ýu×]嵯}mÙ¼Ùãk'“'…±êÜUåèw?Œµñ¿ÝWúí†í'»ò†×T~òGwaò_ÜP¾z­kǓݓ¥‰·œÆ7*qTßB-O=~e¹ù»£¿;õ½/Ù§üÛ_=©ïc¿«¼ç?Þ»(@&q´(0.š“¥N=ý Ï,~àû¦õ÷濼±\ó³—'q4 •4‚@A ½]œ8âGÜ“+0ïÀ.i“K€GdÏ9u©KIžÂ’úèBăGþBýÀ3>ˆÍ† :lmd”ø‚ëÛ²ÌôÇÎͤÓÊìŒ<‚Úay”u;¯.%„ÌAvŒú™vl$v°2ûNÛ~YšdBF>ö&Ž(•Á§è4‰£útÌýw–÷¼á¬òÐ}wVÑxÚ°aCùä'kò…;ËJ$IxÒh¹ÐêcÞVÖ>xbÓ5/©ˆOšÐYQ_eû½ʶÇo+㟎òqÇWþüÏÿ¼>‘ì¥ûæüÝU´êüÕåèß8nZø;~ã¶²ù¯yêðɧ#_QŸ6zzøï/ÙXÞþŽÛgíÓm¡Ξ«¹ê;ˆùŒÛùÆ3î|}ŒJµ>GÕŸ~Úªò‡¿wj'úðGî(¿ûûG©Í™—ÄÑœ![Rƒ¥L=ó§Î,üÇ—îÔÿËþÍ%åŽÝšÄÑNÈ„‚@A ìéìâÄ‘ß8"g@^ÂD%<7íM4|ÚðÛ„ m?æGЃ'¡ËF~‚º6òäã}âq³Í†ÌÄry”}¿•5;µœM›ÎØÙV$bÚŽ"·CtŽÎš´i;‹ž ˜Ì1||;Hí‘˯ÕNÎǰFÆÄuH™ãпm’S_öUµÎ¢þ¹á²Ï–¾õåõ°£é=ïyOyÁ ^Ð ¯»îºò“?ù“õÃÆ y ­:¤¬<°êoßR¶mÜñDÓÚãÊÚ£¦¬:èüúcgw–'îüãúÚ×_V;âjS9Û¸¸þ™á‹É«++ן×ßvMìè£ãìügÝ3?Ý}4{û–å±ë^±³ÂΪ£~ª¬ÝðctÓµÕnëÆ1šöË^ö²ò«¿ú«C>ËwYžlZuAMý»ã¦…ݾe{¹õß)å±iì'­á£™g?ðÃ×—õWºú´Ï>¥üÐ+ö//û¾Cˉ'ì[ÍkUyø‘-åÚë)_ûú£åÚk)_ür»^•úÚÛŠòc?zpçêÏ>º±â½½œñ¬Uåu?vdyîYë˾ëV•»î~¢>Ýôpù¥_¹sd" ýò²ƒËyçXŽ=f]¹ç¾ÍåòË,×|õ‘rã·람šáðèbO2>Ç;I¼£ŽZQ^ûªƒËcõWÏ>øË…/\[~îgíúÿ›ÿÏwËW®ØR6¹¢¼ãíO©ý>¨|üSw•ßùO÷Ö_À2Ê $1uîÙkÊç¯/'Ÿ¼_Ùwß•õ7— ^0À ­öãØÓ­§·Þü¦CÊþoƒ§ê󉱹$ŽÖ¯Z_~pÃ+Êc[+Ÿ¸ó“匞YÞyò;ë °½üâõ¿T¾½é¦r@Mô¾ý¤·–ÿåðï+¿wËËßö¡²µ÷ëŠ+êû¬õÏ*çtnyúO/kë‡òï|âîrû㷗Ƕ=VnÛt[ùÜý? €U+V• yQy冪6§—WX6nÞX.{àÒrՃה뺮\ÿè ÓllÌ÷Uµ}Wî[^{ÔkÊ+6¼¼¿îørï÷–¿½÷ïʇoû³r׿»t?,÷Y¹OyÍQ¯îÚŸ¼ãSeSýA€ãÖ[ÞpÌëÊ…‡½¸ì¿jÿrËc·–Ë6^ZÞuÓ»‡vme©GÏzÓ³Ëùï}Iª«oß¼µüé©P.&q´:a ‚@{:»8qÄM5yr%7jäÚ; nþMÕjW7'A››4ìØêÈ0wR«]®[øÞ‡<~Y«C9¹ žx0×LøÁNíÖ‡ý¢TV«3Š“’ÐgPÛ!ê2ŠŒÁµ:µ9NtÐ'–e+3I}£Û×Ç_ØR‡°¡ŽöÔ±µoÝDZk{}æýÿª\üç£o":è òWõWþ£õç¯^ýêW—˜ùu§•^Xö9qð«c›®yiYsÌ[ÊêÃvNØ ¾9ôVßµÛ«/û>óS]œ-/*›oþ¥i}lûœú‡eå¾§Ô½ðhÙtíËZQ¯¾¢¬:òuõ×ÖþyÇü–w×dÕ'{:£›+ö}V}EíÿØ}÷7˶û'ûˆ6ùË_ÞÙ½ï}ï+ùÈGF˜×½í,ácõ…kÊS~íØ$·þÄÍeû7g0ÜÉbñí£÷ý·–ÿùÎßĪ?¬U>ñ±SÊ0UGÓÍ·ôçïì³Ö”wÿÖÉ]˜ßü­›Ë;þÕS‡!·lÝ^^ó#×—èärÀþSxý‡ß¹¥|ôcSß°ZWqòG'vɰ¡ñˆJ—*å´SW–?úàiˆø/}Ù7꫘£4çΛKâèéûZþðÙØùù¯¿µ¼ûôßÜ\Ÿ$üþË^^þËYQ^3•#¡ô·÷ýÝPoÝÊuåãÏýX9tÍοÀ¨ÒÜX~ìš×Ù,kêwÔ>uö'g´¹þ‘ëËë¯ùñΆÄÔ÷úâºø²ü–òoOûõ®$ÁõK×ÿrWoÿ|íᯕ۟˜þQýCV\þòœOV£èmßx{ùüýŸŸ&:jíQå“g¼ã½îê×—úÂòÇýói:6^ðÅ ¬N+çš8Z³~MÙçàuå±{6•-›8ŸïLgüÌYåïzñ΂Êùìkþk¹ù37>œí/©åG#¡ 3 ‚@ØØÅ‰£s*¤ÛëÆE+7‹\ÌÑnëµ9L)ãNôÑ%ï@ɆmuàAäP e\äzñ8êN·Õ§N¼6Ž}&öÆ®ÕaÎFx3Ž'%ƒ©ï€írý!ƒÔq }]ätÒÆ8Üñ)§l?úìчÇ du}i‹ýžöªж­›ë¯¬]PnýÆåFó—Ÿ¿æãÏЇ'|E­M•­õF¶>m}äÚ²mÓ·†¯Á{üæ_¯O%}–jY{ò.«öFWßôÕúËmÛvN0”UÖ×Çþ{§óøM¿\¶=øw]½ûSÿ;_Öl(+êS +ÖTFoìž4BFì'n|S­¹:‹ÑVÖ'3Î<)µõá«Ëßú—£õFp>úèá¯Bñëj$Úfzšk„‹Yufüo+ëkRdûÛÊ­?rs)wV[ý=5iô+Çî$¸í-ß-Û®ð¸ÛI¼¤ ’%ÿõã'—C^Sî¾ç‰òªÿuô±ßú–Æ žøùoŸ¹§\ý£õ»QkËYõÉ¡sž»¾ÜzÛãåu?^ÇÞP›8jØÝSCU:þœ³×—§ŸZ³R•ú ’wÿÖÑåüçÔÉH†\tñýå’/E}âŽO Ÿ®!‘ÃÓ?'ïr'{dë#å³w¶|ûÑ›ÊS÷}jùá£~¨>}´¶ôG?~ì† žJú³Û>RHmØgC9ïàóÊ‹½°|«®moºv°VûïŸqc’?ï¿ùåOþñO‡ª<9ô©ç~b˜û‹Û?Vþæžÿ¿~`û´òÖ“~~¨÷’/o÷„”Œ6q$’>ããE‡\Pž{ÐÙh¡‰£µïS^ñ™W—ÃÏÞÐùãÉ¡ûá²ñ÷umÿœùsg•çÿûÛœV^ü]nø“¯u¼$ަA“FA  °— °‹GÏ«0›Øé'Ì!p#‰un¨‘YÇŽ6|}©K©.ù oJᓧЇ:­óèâÛ­V‡¶Ú™?±oè`71áhRB—t’’¶p¶‘Ñ1t%x´á·DbØBè±AÆB¦-úú†®ý².|ÛÖåӶΫnØLU~¹÷¶Ë{ßxNyüÑéÉžš9÷\¾™Uº_ »þúë»úL¦%Žv(>vãÏ”í\9hísBÙ÷éS7I›®~ဿæ)å²wpRU×?»ô¦ ˆÒ”¢¢Ø ¶ˆA{7Æ^ƒ&F-jŠÑؽüUl±Æ»±D,1öŠF@Dé eaå¿wøÍÞ};3;3[Ñ{ø¼¹çžvÏ;óf˜÷Û{ïk3àÏ/ý‚-™pÑ ïʦÙÇYË5ñ‚ä~E¥ö°VkSipeŸþÔUToa ¨Æ–X«õ°ÒV½œ}…•ü¹k »IæqâHÐÞ{ïížL5£Ú(…:_ßÕÚn’?ðûîªÉ¶ä™pæ C‡8Ðèüê Ñ”ó&ÚÒ7ø,7…FŸxÊ—öÑÇ™syâÑ~né—©ÙÁGŒ¶ nƒæ$†$WHfŽØ¸ùz·lk©êWǬjÇÿ*õ^„ÉÛ·´+/ë燨¨Xn‡=ÆÆSuLÆëÞ­ÄVÉsefùž_¡ãm¿måŒ#F»úÚ öâKäxvƒôàã'”9 m¼]xþš6xÇNö©[Ž÷›'zý)'­f‡œº¡Çî¤S¾qKÔ*Ï#ß=Ž˜ öàßûÙê]RïÍ}g7Ü\7{éDjýiô9ößÙoÙkÛŽP8ûbþvÜÈ_ÛÙ}ϲ}ݲ²·fÿ×Îu–ןßï\Û½ëžmÖköç1çÛR·¤VtëF·Ø¦6©=¼ùƒ~©v{¼·—Í^š»Tç¹%tš-´s—]4„½4ãÅ4/扩Oۇߨ®ý}“{ÒàÖY_œmÿ™ófZ·­ª®Ùào¾ÿn³»¿½'­ËaÃr=f;íÛuûc¿Ôj•8°òÀw·NuI óá%ïØûy+-Ûôô-l›KwL÷Cæ­3_µ‘7~œEà(]ŠÈÄ Ä Ä Ä Ä Ä üˆ*ÐÈÀÑ6®ÔºIæG±nÑ× -:Ùҗ̱^®>-8þ`ô!üŽdȱÕtõÈËåG,xé;0Å åè2’‚fT&„O5'À@Ò!#¦da’{°BâÈ;ˆÁ†6PýÔŸÎS6ØBÄA‘76Ò =}ñÕö8rº4}øâßíÁ‹I÷ažzê)[}õÔRžwÜ1¯'…%£Åß\èf½T%n«uï°Ò¶©Í’CP§Uÿ¿[ië>Þ¶länîì+—Þpúm6}Ýë–Ì|Ú–Nº¼JÌ’¶›X˧¹el}ÝY«Ì+Lܾ%e_žèöøSÅ'ÙiÖõ˜ôÒ¶²1Ç™•JšÔØ?í´ÓìàƒövÇ{¬UxŒô nSé^öNwaæ½ñ½Í9ofZÖüg4:¯gº/æ»ËÜfØÿ*W· ¶™»²ºº=tš7/±iS+lqa £ßzg®qÖä¬9ÜsçÚ¶žÛƒºþƉö[rUÓ¾BØ&£'žžn—_Y ÔeŽžz·?RêcuÊé_ºGÏë{‰¨ùQ!çWèx!pÄ ¬}ë“zóÕJàè¨_¶/ÇV؉'t²#[Ó>ûß|;þ„‰n–V‰ýó©ÏÖ’%¶ë^£­¬¬ê9åµnmv×½­÷Zm¼óSnØeWLÏë}©:Zî^±À³høð@ü­í*•}ßßßïÿsðšÙi}Nµwç¼c§~q†~€ öù9ø£CÜ'_•” 8ºe£›l³›yÃóÇ\`/Í|¹Ò)®=ŽXJ7b›û¨ìEtʧWQÊ8ƒ>J}×`”Žîšt·Ý6ñö´]G7]Ý~ဣ$My}’=½Ë£^¼Ù™[ÚÖJšø>à SH8 «ùXXXXXXKšpÄPˆƒ,q}0p`ȰƒÇÌ^6¡{[Ù᧸؉ðÅF±‰I_8ˆcÓ¹¡ Ç O\H1裃ˆŸ…Iåã ÁHžB0°W‹N¶´W‹Lö´ÄÁ='€L¼b à¡•?GÈC_ÅÖC}ZüBÿ¬ÀÑ‚9î¦ð°þ¶h~å(žö曩²Ån×ÝÁƒ»p5S-ùŒ-™tY5§ÒÕö·V½RK.ÊFîêÎjAʦUål¤$8TÒn k½ÎõÞ®ìó}Ü%UuIDÕAÜé»%gÍÖ<ÖZ®~HZ•s“ë–=¬Í{Ûl3žÒr0‡rˆzê©Þâü£½öÚk9¬s«JÖ)µžw¬]ÅhÙüe6y¯ ^Öbç–¶æ¹=ªèéL»aŠ-~lQ5y>žžuó ýÒK“ðyðá©v×½³l^0!ͬ}xrŸýÇV £÷Út•/É<öÚ£ûÇÞiñ”©‹íŽáSÜ’¨2[°âÒH+&Žð9èÐq~¦‘LØ›g›­Û»=Š*Òûÿ°÷ψ—RÌ"'²Ëèj¹Ë?W›ïù3^sÞ8ñZj†Ù[¯§òþj\™qÌxŸÞÐcWµãŽížŽ;¸ƒý^wÕ5ì±V/`MÀQ+7Áhøm½­_ß6>ÎónÙßE—L+ªN>@Ž—b£_v‚œ7ÒGpD9´_×}íýÎNG¿[ûd;¬û¡^wä'GÚØ…ã<¾dŽÂY>Ø/o×|}}äf - f,…±B¾àh½¶ëØ=›Þã݆˜qž?¸ÙiQ8s(ŽÈñ°pÿð_@ŠÖh¹†må–ª±¹x¸ï“ô´ùìq´Öî}l·îºy~éür»³ËͶÙÙ[ÙÖÙ¡šÁ§×¼ooÿé?Õt8ªV’(ˆˆˆˆˆˆøT  G!(¸# Grúü ä>"@†Vrx0 ìÕꇨb9•×Ó"^‚=„LrµÊ½âÁCôݯcß2>ñä§“>Τ:$_"Ù0¨’X£—¤êÐÇ{ÅÑøôU ü‘Ë^‡cÓ>ØO„üá±q·xéØŽMçÃxŠß»É§ª9™§›7Ø&|þ¶-[ʵ’¢n—×_ÝwØ{·Ýv“*gGÉådr,í´§µZëO¾[8r’Ö‚ÒTqtÆYc«=u­Ò²’[«W©=|/xíÙnÏžï*•yr…œ_1ã…ÀÑ !_¤Á0Gç g/¿²Øg›ŽþvewÛn›U½nßGÙ´iú:ª<¹\À³Ínº¾—mºqjo²gžŸa—\V÷3”M±ÀÑöoíà¾Sç&àèÄÏOr`ÎÇ>t8zf˧¬sËÎ^÷“·Y…[Šš¤lÀKÏþÜïOéen¡ßSŸ´áï´K*gº…zøB€£C»b§ôþ]2DÖþ ·š^nGÇ|rlÖ§¼e æùGÝwìi{½˜ší•ŒõÍÓcmí½×IŠ}ÿ‹áŸÚ'¿Rù?d`£ ‘ˆˆˆˆˆøÑT  Gü(æpúÈÁèÀ+Èy¨wÝ´ŽêòGNÍzœi-»ìçU—Z¯gmúßéuåÓ³e“¯qٯ枺ö”—-rKΖ/üÔóù¾´ÙäuW)ÊàÀ(í©8‡OQ[ôåI.þ'¶0ö¸ã޳¡C‡z§“N:É>ü°rï’Â"¹”Ýr±žÿè·Û¬‡gØ‚[‚iAy{¦ :¨¥]ö×~6gî;ñ”q¶ZÇR;ð]ü:ÙB=úø4·OåÒ¹pÃèÙs–Ø>n™û åClÐÌNþm7Ûb³ÕÌÿùÔt»êo3ªìsÎ8úÍÉcìÓOõýRÍ=-Xo]·Aòðpôø“ÓíÊ«³ßð§¦Ðó+f¼8 ÷fpžk8zäÁÊ'©eÛØû‚?wµÝvI(a|Nó¢akØÏ‡¬æÏ¸>g©¤ÅGáLG,?›°h¢ŽdSæ€è!ïì¬á«´wn|‡mÐ~ƒj{ɨ{«nÔù©{bZ’îœx—Ý>鎤Ø÷ ŽNë}ŠÜ-Œ³É÷+3^Éár÷_ÎEc/q¯ü×Su©ZX ¯Ìó%à¨Ë]í€ÿ–gÄ”ÙØŒ¶Gÿ+ëÿ!8*¨œÑ8V V V V V VàRFŽØâ&Š›v~Tq㌌¾n°àu mðÕM?6â¹ ¯À>Ù:‘·C.{Zl…8Ö÷+´GÆXb¨E¦üàk$ó%+8‰¬’¦O,Ã^±Cyx²Á^$^¾ØèÈ–1‘q1>-$1à•<öñ—Zƨ¶Tmò—»'ªí`KËˈQî½÷^ãb†vß}w›3gN5›¤ à¨õ†Ï¸mˆ:z×L@NýÈ­y÷S¬Eç½WØïèZ]#ÉÑ3÷«ì©ôIuÿ6ýËUÌͰ`Cl6ÒN_—™ãå’^zé¥ée}ìu4a„\æ¹unâH¯'ûä¶Y¡óŒ[JvÕܼl³yx;ñ7=-œÑ‚m/7KçÌÓ»ÙÖ[­RÅõÛ)‹íˆ£ÆÙ¢Ôä¯ 7ŒþÛ?èýböZ½ÄöÙk;ÂíÝÓº—vŠ.¿ò{âéÔ 4$ÅGìÝôä£ëû€_Sf‡9Þóù¾z~ÅŒWàè…çÖµUÚ§päp¶’ί««í“¥ÎYí¸CK»ü’~Þô‹Ñ ÜžIlßBõH öÅïí¿ÛýÇŸÅÌò™¶×n©k‚º·rODÜ"µ9ò©j Sk׬­íÜyg;¥ÏÉÖÆ-‰%7²–\ÀQ¸”Nºd{ÀûÛY}ÏôâBg …3Žê8êØ¿“ôÉÑÉÔ³ö'<7Î^8ði÷õÊM™)G™ë¥±±±±±±?ì 42p´½«®–§qÓ¥åGÜlóÃMþ‡çÐä²CÞ ‡¶ð¡ø„lÁ(ˆE9fùwwÛ²©wTœ®[¯{s5ùâÉ7YÅô«Ék´ÙäUWW‚e ­ì3·ü-$÷èì6›Œð’ò)·Ú²i÷…Ú‚ø–-[Úˆ#Œý¡˜µÓN;¹ýuT¥ hÎØÝ“öz®O^s_œcß_:;uÔhÝ€ýx:­VbS§º¹ >bXf÷ =¶£}D7oýÉÈùvÂIÓžÅGÍÝeðÚËë»÷ˆ…Ù1Çv©Ïp²éQ*™bί˜ñjÝvK/ÛxÃÔ2³½÷e3fò"wyÚƒ÷õµîk¦f"p„î¹§Ö³vm›ÙÒeËm·½FåÜ_jEÈZ7 ±96J³±4.s£ßª´•=»ÕÓJ=½ð벯ýþ@èrKØ~±ævFŸÔÖoÎ~ÓÎuv5—›7¼Ñ6_esËZ…Ú­oÃ7îEÃ'·;&ݪsò µïÙÁ;4g.R~ûïoìù}Ÿ´Š¥¹?c8RÅb++++++ðcª@#GÛ¸ZÖpch”ºAªÀ(¸™à‡<-6ððøƒuh¦}ýðÓˆúNå _ìÀ3àñW_ñiñW ñøÀãOl°åÂ’ª—åUÆYÔUÄ * ƒ‡ sBÉ)ad:YÉðG®8alñŠ‹OÔá£lUŒòXXXXXXr8ÚÖÕl<ƒºðê È¡I&‰¬ør@ò—œ¾xôØ{HΘááºé>¶`Љ2pZâ*1è+rñŽ­™pΗÂð “ áp`6ÈBb<NŽõ2üW¶´ÈiUÉp™ˆ>qЇc(>vðä-=¶éͱ?~å!ûÇ¥ÇÛ’Å•Ë~œ¾1sæù矷UVI-Qúë_ÿjÏ<“Yª¯„ÀÑ"÷Hûåå“]öÍ­´ý@kµöyi·²ÑG›-ú*ÝO2¥í·±Vý®N‹—ÍÿÀÊ¿:5ÝÃÓÖZ­}¾•Op{}ú¤ŸrTb¥¶³V}S@öɸ}ŒÖî býïñlÙ(·oÇâ ž/ô…=úè£Ö¡CïzòÉ'Û|Ph˜jök<ÐÃZvOÍ”H*ýò´«Ýò4])Iƒì‡Fßq×d~—Ë+êÓ»Ô¸×=ÑÏ=áìî{§ØoηpZ¼x¹uìXbgÿ¾›m¿íª>Ow»þ¦Yé¨ÅGÉGÍ/X¸Ìn¼y’þr±-˜¿ÜztofC†tt{;u´=öùÒ–¸¯ÆbÏd oà–-ìê+Öñç)`‡N>{m½U »îo)_|î¹oŠ[f'ü¦‡õèV9Ó¤ø»ïÚÚÎ?·—-,[f·»Ð³Ñ¢EöÄS¹¿;²ù&å  vû]Úÿ’ôð}žÍ_¶ÀÎ_÷Ï€I+V0ÚC©s‹ÎöÌVO{#ýmܵööœw `i©û×Ö-Sû}ŸÓl·ÕS ÓMßÜl÷M¾?Ê~ÚiG»lýK½œÙL¿ûüT›»t®µ.mm;®6ÈŽîy”üÑ¡i¿pÖ`ÕÙ_üÁ>u Ô¢ŠÅÖ¡yÛ¾ÓvvzŸSý29å‰sCGŒuÄ×ÇYÛn©™mô“4áùqöâ/ŸÉ 4Â7GÉ Æ~¬@¬@¬@¬@¬@¬À¡  \Qc9C‡À&8¤£Už’œ;Rað!¸%ïZeƒ_ÅǀǞlb|r}H†-}é›ä”Ý¢ª†À$ž2r’“d d$¯ðŠ#t:iZ%=RÎ â:¶Š¿úIŒmh¯Xè‰÷›cÏšòµ]uô&n3ìünüöÝw_ã±ò˯?üpûúë¯}?ÓKeÒ#[<áR«˜ýl6õ ¹›uÄ2–˜9Êê”¶ßÒLUg-_ö½•4K]+‚Ù¢1'Øò²ÏÔM·%m¸¥s·ù~Ùçû¸J%0‘6ª`»å–[l“M6ñ–Ôçˆ#Žp˸òºNsF/q”ïyÛÚÕl¦ß>ÕÝŸß{X͹Žá†Ñs¿_j{ïŸ[òFÀQM¶åå¶ÿAcl–{¢›¨Xàÿ6îIó÷ÞY¹‘´b&[6˜æm|ò±u¬SÇVèù)^!ã1«XàˆÇµßuÇÚÖÝÔ¬>¯ö~7 ‰¥†Éͱ÷Ú£ûÇÞ2«±àT£a  1Cçé-ŸL?Y-™Ön¤µÛ¬m§ºÍ©¡Áï ±Å¨p”´Oö™E´Ç{{:€©úl"f5 ßøvëß®Ò-Ý „;­6Ø.éÿ×´>ú5$pÔ{ï¾¶Ë#îû2ýïöOìÍS_͹§QÒ-GÉŠÄ~¬@¬@¬@¬@¬@¬À¡ 1ãA%çf‹›Xa€I’Ó‚3„`ìÕ‚{HOµØ@’Ñ'¦úè d ÆT æ!Ð ¹li!ÆÒ8^PÓ A ! @"\'H ztxZ7á À‡1‰£ø€:ÄáÄá¡R’âñS˜v¯1ðã ¾Z|ÑÓ‡¤CþÖeîÞ×üjs›6a”ûQŸ_-E—ë@IDATî¾ûîô&ÙÓ§O76}.+˼¡v.àhÙüÜÌ ÷—÷%Ùg3ø¬W¼´ð„•´èbËÝ£®ýo¿PUÉ»'®µî·Ã—RO‚ªT¤¸esßrcs•«þÔ8oѪ¯µYÿ^Ï–t3* *Ù‰'žhGydj@÷ ?vìØt¿¶LÉ:¥Öõ‚5­e¯VVáfæL9u¢UŒÊïý«íØùø‡FŸ~æX{û]}OÔìͪ=wokûîÝÅlÚc&éõŸÿÎqOnûξûŽM%õî]jºÙJP!{)B;‡­œø[–vu±—€ØøûƒçÙWM·£ŽXÕ~=´»×z~AH·P~ãm²qs»ùúu™??ßmŒlSQ^z~]kß®y•s=ô övÊɽ\ÍçÚég¦>W,ÿûë_ºÙO¶«\–6}F¹]vÅûïÛKìÔ“W³CZ£JüŸíÔÊ.¾°o˜nV>™WVÃ<…G½Ûô¶7»ßæ-g»¼·[:úó[=k[t´ý>8À¦–Oõò;ÿÜþ²Þ…öÒŒíü//ô²V¥-íoë_e[¬ºeÚ÷³ùŸÛù£Ï·)åßÙÀUÚõ®õºÝÞÛÃÏ pÚ®ã¶vH·ƒl`Ç­Ó~!óì´gíºñ7ؼeÙ¿;X‚vž[²¦ÙIò/_^nÿ7á6{`òƒ¥[Æ=óü¹¥…ŽaöÓí†Û¿¦¿`³—¦–"¢_Í}>;ðioÖ ò|Éç©j µÖî}lÈ]»ZËŽ­½hù’eöòQÏÛ×ÿ,ü»/Gªjlcbbbbb~LhÀQr†wÜÌ{€+èÆŽ>ÄM(vá ©ä²sj97ZÈñ!&mù ^ã(¦§ýÀGl‘xZtćW úÈÕwlnÂ!_Ò 0 ÁÀE'K«ÄÐaÇG~´øI'úŒ£8ŽMÇÃ^ñh5ÅR<üðGÇœ>¤ñhñ“­â½»ãA§Ù[OÝ–÷l#‚B½zõ²xÀš³Ó¯£O?ýÔÎ?ÿ|·‰rê&Í W¼„À‘B™{RY*5Ú¨E7k3Àí‡ähјãÝl¡/r;»eÖ¬“;Ü);°ÉƒEé¥k¹Ý‹ÑRC9ÄN:餴û}÷Ýg7ÝtSº_§ “HÀëxG›mºIsëÙ£…[ÒµÜ^|¹úŒ‹|SeƒævíJlÕUKüž<ì;4oËÖòPœ3¦:urã®Rbß»ñfº ¥Ãý–êêü”]MãÉ®¶m{·’¨Ûš¥n–Ôr›>Ý-zjb× çWpTÛzÈŸ%b«¸‰=e™pÙ†-›a·tàKÔ˜E´À-u[¼|±-[þÿzTç¢Ø©Mi[è bdÚ;IžØcÛ¡Ydž¶>iŸ}ö16úõÕWݲQ÷=š‹Üÿ6Ö^Åš»}æ~5Ç*ÜÌÀb(GÅT-úÄ Ä Ä Ä Ä Ä ¬ìhÀ?dêèÆ]3Œh!î"ÐɼdòCŽ­tôÁ&>ч°Ï ÕáXO’ÓA ç ñèKw8’;QÚ>/¹R’.*ŽP,DO1Ø ¨)yÉ僞8ŒÃa«ØW±Ða‡œ;d™$w"?¾l°Ó‘BzR>o·hÕÆík”ÿE묳ŽÝ~ûín¿–]vw¡?ü°½þúë6fÌ÷¦ÔŒž*ÀÑ'ƒœ»Ê¡Hy´nyZëþZI«nƲ³EŸí‘‡SØðy€4–¥ :ÔºvíšøÁ´n¸Á/éK #++µ!pôÈ#Øu×U]v*G–ÉÖÅÒOÅ‹mãT€ïÏL´öÚkÛý÷ßïUW^y¥=þøã™Ì¢,V V V V V V VàU&êð |`ì‚xx5Ø1 ÃìA6ôu ÇRZvè9°%>˜…dØ2޽ò“[ý¨ÔÈ l)¿plôI3*BC ˆŸNžƒTrŽ­f£„±‘va%¯ø:!ä2bpHO^T±‘ÉG¶è°‘N}ÅyÇéjEk­µ–±A6 RHGG}´‘*iºÖýe½õºÃ=hDÀ²QG¸M‘ÆÃ6 Úi§ì’K.©’Ëb7%æÆoô›cWQÄN¬@¬@Î „ÀQ.Ão¾ùÆÏîËeuM»»îº« 6¬Æ$#pTc‰¢A¬@¬@¬@¬@¬@¬À¤M8âf¤wàÆ<Š$sbo£¹näCCèÄãC\(l\[Å€`„ BiLâG¾ðê‡>â:;)xv‹Jl˜5€äJ"ì °!Š@üàÃ8€9øã«Ã±ࡎX¶ì Yá'[ÙÐBØ)}ì°—Nm‰½`ègÛl´ÑFÆHµŽZ­{§•¶]¯Zj‹']k3­&oLAM›6Ížxâ ?ójᦱQucÖ&Ž+PhòŽX»ß~Yö9+tÐhß(Øk¯½ìÜsÏ­qìÕX¢h++++++ð©@#GÛ¬(#†@#x€\B2úls„}xp Z(ô£ ÏXÂAÔ†öNíÇ$ö­l½`ŋƣKžP&»”&Ë+ù'Á@JLƒ ôQ¢Äƒç8C²nw_ƒXò¥Õ ¡W^ƒ‚h|Çz½|Y¶¦‚ц¾Ø(ã“SRæDé<ê8" ШK·yµ[F¦ÙPi‡m­Uß«<_–çRµVëÝe¥mÖõ>þ¥¢Ü;Ó–/ø°RÖD¸nƒš=zØ”)SÜÞ;õ¼ùN9ç˜F¬@}U zÀ€€Î5Ƽyólüøñ¹L¢®‰W°oß¾5fÉûÌû)V V V V V V Và‡^&â€#@`à `’Ã#§…Ô›„žJ`ÈÐiÆ’âá-ã‰G®>-¾P˜ rÙHÇXØ`¯âÁVä_D2ù’‚3˜N_xõ¥Ó …É“,zH-~Ø[¶Š§¾lˆ(²€"ÇzB/Zñ BÏ¡qˆNrÇú>m­—ª¤^ÈmþꦹÐîô*¾w€ §)V V V V V V V V V V V V Và‡WFŽºŠ K‹Ó#Pˆ¾@dܤsB…6`ô!lÃÈÐkƒ íå¯Üˆ¯1°'éi?µŽõ„o„c¾¤A•”  $hu:9ú’øà+ÇzR\ìà9Y; ƒd/PˆXðj±!/ùÓ—3ŽDè5žrÅ®éGÊ<¶±±±±±±±±±±±±±?‚ 42pÄR511o ñð8_°0 °Z6ôå£=<-„-þ!ɰ ù` >—ˆ)=8}ùÓBèái!å‘êey•qu±˜P¨AI ^¢/PƱUC® ® A ¨£Q|É9)xäŠL³( rÅ"ù9µ·Ç=öʧΗª¹Ø‘bbbbbbbbbbbbbb ¬@ŽÀ À'8àÁ9 x@Zð x;H@­ô`ØCŸ…qˆ rÅ’UÁY” x<ãˆäC ôÊõeçDÙ) œÝ*¥ O’àø"ƒHF±dG9}ñ$L_öÈI9D+[ù…-ãÊ_`öìÂXØÓç€PäX/S,L8¢2‘bbbbbbbbbbbbb¹M8ÈC5À/î€1p ƒ‰o€°¢JÂJh Û@©È#üBò”U*|‰#\>ÄWÈSX‹c=)žúY[%–Õ Pd²ÕàJŽ‚ÁcC«b ØÁF~ðŠ/Z ¢“ õðèà'iO_c¡'~²MòØ¿íŽH±±±±±±±±±±±±±±\&%p€iÐB`€3´Èh9òÑw¯0‹ÐÞ©ÓX rÙ+zñj‰ cIõR¯Ø ˆ%[b‹Ç†qϱ¹ ãB{Ó´1Œ–Ä•ŒcÓ…‚G=D ¨£äáuÒÌ$‚°A¯§¡16q xl°Ç;ì¥W´Š›ŒÃØøàg¹"DŠˆˆˆˆˆˆˆˆˆˆˆˆhì 4àü¼A}žTŽŽÔ§ÀŒCX¼tZž&,ô`ŠãXe(¾`øb§¸ŽõDŸq!ì°×¡|hÑ%}(;)hv‹Jlu¢´œ¤N^'L+%¥>¶$©‚ÐBøˆäƒ„¯À úÒÓ†c ü‘-:bÐÊDZ^FIø`7Ǧ2‘bbbbbbbbbbbbb¹M8Þ!PÐF<$Fø­ð ìÀÉÇzʤG=؆@%?Ä%†Z䊉ÆENBa—É!cÙ*)ú^ú0Qôô!ñÒ#Ç“× àP(ùÈV`Sù8ôñS.ðØ*žcÓùH‡ žCù․Ü"päŠ)V V V V V V V V V V V V V ±+Ѐ#>``ðá LC@:ù96í'Ì$Äã>A,ìèkLä}a'^°âE¹à+[ñÄ’æà!Cã!ÏIJ<§Ñ %¶:4°’¢Õ ÐìP>$&G<±°U Çz{É”cGcB.=}bч?ÅÀ^r«ØÅ¥j®M‰zõêe?ûÙÏìóÏ?·÷Þ{¯)¥s‰ˆˆˆˆˆˆˆˆˆˆˆ¨Ç 42p´íŠSÃ`¦˜<Þ![¼A:ìC9:° Z°GÈ!â ௘Â3OvÂ]h…qHG9~7Œ\çãØì„S¡$€@"â “^I«ŽƒŽÄuØÐ§EѪ°š„@$¤ö6—¸ð² eÈ¥slz¹6P£G%%%ÖµkW«¨¨°¹sçZy9ûhÕ5ôxµ9“=öØÃÎ;ï<{á…lذaµ }ó¬À6Ûlc«¯¾z5ëI“&ÙÇ\MÞÔíÛ··ž={ú4ÇŒã?gM=gòëÒ¥‹m»mêÿª—_~Ù-Z´2¤ýƒÏq·Ýv³C=Ô^ýu>|x“=ß•%Ï&[À˜X¬@¬@¬@¬@¬@¬@“¨@#G]À„K$â†DË ˜‡xÙ À¡•Þ±i?ƒø-¾ðØCÈÀAé°SŽõ<-zÅÆ"9m^Ä@ù’€…H€€F ÒídƒL'Ú W Zå#9}xˆ–i:Æ8‚”—ò¡á#=2úøÒBjkµT­Gv 'XÛ¶mmÙ²evî¹çÚ’%\[™©]»vö‡?üÁvØakÓ¦MÚè‹/¾°[n¹¥ÎgØÔÕxk¬±†}öÙ>_f=ôÐCéÜëŠYuÕUmà 7´C9Äh“'O¶|оùæÏûí·u5TŒ“¨À<`}úôIHÍ>øà;ùä“«É›²”ÏÒ¦›nê£Ýwßݾÿþû¦œr:·]vÙÅ.¼ðBß?üðÃmܸqi]d§Íš5³#FX‹©ÿ2:è ›8qbã$“cÔ•%ϧU±±±±±±±¾M8G;´ /à9¤YEÈü “Ø`“0 b`)}ì8İţ/a(“™|äG_1‘1>-¤6ÕËòJRù’ †=~  )9tœöJˆ>zõÕ:‘'@N„–¸Šæ'@HqY´B& ˆq>¼(ìË?Æà.Ÿ¢f5oÞÜŽ:ê(;î¸ãŒUÑÏþs[°`ºUZfAÜqǶöÚkW‘‡³Î:ËþóŸÿ„¢¢ùº¯´´Ôî¼óNëß¿¿Ïå7ÞHƒHE'8R¿ý÷ßßÎ<óÌ*µ L<»ÝvÛ%E±_G¸ä’Klƒ 6HG[sÍ5=¿2G;]~ùå>ÿ[o½Õî¹çžôy5sÑEyPnôèÑŸ‹"p”«:£aö# <´çž{Ú¬Y³ê-™B®—0‰†Î3;ò±±±±±±±uYF޶vç"|BÀ‹fqã†À`D[ì8 dâ±èƒC¢/Å”|‰÷ÇÐÌb(7r !£O,dÒ)zpúj››Vaú0¨ã'O+½–OòDCòÑI"—£¡—/:õ•-•NzõÊë‰Ú!/80`€]|ñÅÖ­[7ü«P6àpä¾ûî³¾}ûz{n"o¾ùf›>}ºpÀvà¦ãÔÅLƒºïˆ#ް“N:)_]G{íµ—Ÿ©• `–.]j€tPŽ‚ÂÔ3ûÊ+¯øq+pÔªU+{ê©§l•UVI¶¸†“{ì1ëÞ½»}õÕWÆg)Eà(WuOÇ÷6û®ñyøðÃë5‘B®—d" ™grìØˆˆˆˆˆˆ¨« 42p´ÍŠóËàlW ¥/ B€Žc½Ž>r6ÂCd§XØÒ`“£×öáñ í…¿à/LE2ZÆ ÇtÝš ‡|‰dH–ÄB"†/á€H=>©;þ”LÉ:±?I¬|ÕG/™@dò¡X¢0ð*¼ü™U„ç!;Å¥}Ûy7¦¯¾újÚ~ñâņL” 8b¶ÎÝwßíÍæÍ›ç"-Ÿä¹÷Þ{muÖñúgŸ}ÖSŠYL[Wã­µÖZ~Y9Šê8¤Ð2«¯¾ÚE§žzªÿ+ÿ_þò¿Ôj«­fÿûßÿ”Blë¹++ptâ‰'Ú‘Gé«sÊ)§ÔùÒÏbÊ^£b*üÃò)äzùay<›XXXXXXXT8bÃQ° 0µ` ‹Ý!ÜüIfï¼óζpáB™Tkr–/×iVS[]Œ¨Ã,©äÒººŽx‚Ú?þñ̾ÚgŸ}¬16Çf9uoª$à.ùž’w!¶µŽ +¬y±~Ä`¿±G}Ô‡{ûí·íôÓOCgåk3fÖ ¢ p”톪ÆÖô=QÍ!3^à^[›÷ 6¾$Y[ÿ|O4ßq ¹^ò»»†|ß É+i›o=“~±++++++Ðô+ÐÈÀ›cCàÜ  yô?Â0À Ác‹?<7íô±ÕžGô‘£ÇŽCñG±±a)6Ø„ÆPla* èãOÜ0¦ëf&É—4˜ìT'Æ`èO ÉìC[d$‹Ÿ–­ ôà6ô%ÃO¼À$倞_cÒ—žñ$/h©?ª¹¹ãÆ”§¡Aá,‡LÀ OãÑò¤ ›á3¤?þñöÚk¯…"ϯ·ÞzvÁø¥/ÿûßýÞCI£ºï·¿ý­ßljølRÍS… ºŽz÷îíc Œ:ìºë® òT5–²OÕO~òÿD1f‚±…¢GŽIJžØÄ<Ü úþûï÷›uKOK͹4ûìúë¯7f£…Ĭ*Á-·ÜÒ8o|Ø+eæÌ™ÞvÊ”)¨C÷Ë_þÒ?QëÆoôõà½4ºêª«üXnúÓŸlóÍ77f¨]sÍ5~o-6Œ °Ù¯_?ãÙè].`²à(ßz†54ýÅ/~aC† 1Î ð”=ÂXÊ9jÔ(ûüóÏ«€¬¡oÈóÙÒ†ØÔi¿ýö³iÓ¦¥MŠ©‹|òä“OOgËF,£dÃ{ˆ%¨á>g…Iàè믿öïç1Çcü:þ|ãikÌPüî»ïª¥Ã>7ìƓٸ6¸v¿üòK¿¼êᇮæÃÒ&êýßÿýŸ¿>¸~4× ûûð™È4^µòÔö}ç½á{b‹-¶ðK†ù}òÉ'þzá|ßÿý4ÌÓ9 œÏþ|&6ÞxckݺµÍ˜1Ãx@ß»È\‹lÒŸ¤Ù³gû}ê’rõuÍð „b?…\/ÅæYï;ÀÍ Aƒlûí·÷{xQÇ9sæøï3¾[x" ß_I°[µÊ§-¦žò!~m>·ùämbbbbbbê§ ±T ü€# •¬8clh…Y` üA®Xa<â`&!L„8à´"xlñÕô×±iù¢W, ÒX©^ ¯rªÁÌ«±åP’´ôIŠ$tRê+¶ZgâíÒÐiƾ6øWöèKvôŒh•}Höø+­äøH®=” Žœ5ª 8êØ±£=ÿüóÞ@â׿þu:7>ÌZJÀ`M’Xî¦MªÑ±7Ròicu1^¸Ôøz5cÖ%pÐHÁMôÄOØØ±cýFÙܰ6ÌËëú…/#j©q“ñÃúcsà 7ø›Tì¸ñ¤!0ÄÆÞº禕eRá O¥ã¦• ˳qÙT.¾øbovíµ×Úi§–vᆔZr×f½(¹éGÆ^TÚGçàƒö³Û6Ùd“´¿À*âDd¢B£Bê©ñxŠ3„rÕ„k/ÜÿK¾É6Ü› è“L/¦.Šu@–À‡äØ€VÜ PðDEfr-èÚúÍo~ã]¸n»í¶¤»}üñÇþ@GG}´í»ï¾þšH:±Ì•½ÐŸÙ¨˜ë ù°Ã+êz)6ÏÚ¾ï|÷ð‡‹Lûü…µQ=CY!|1õ,ös À~§’g´ˆˆˆˆˆ¨Û 42p¤¥jœ”€#x°fW@ÂBhÐH&½ú´`šUŽ!ÅØ„¯püÀ08°ÃRŸV¶ÈÅKO_ÄøÄãÆ;”K_­%H¾Dp k 鈥„‘I€Ž áØ4à$žVvÄЉ(±ðç€×A¿…; üd‡,Ì+iC>Øsˆ/h#çWjŽ˜A"èñÇ·+¯¼ÒÇhÙ²¥ñ×å.]ºøN$,ßbI’SØøWÄSݘ•RmÇ#/Æ쀴Y÷[o½åûu ðØc­¦ùAÜK}GÜàó~xaÏ*fw1›K³ªÈY+S§Nõé` È(áÃM(`u¿âŠ+¼œYh! ÃÍ.36t“Å_âß{ï=›4i’Ÿ9Æ7™€#4Ë 3L ½'áV¸ÿ±9—Í6Û,óaO©LTpTL=óÏþ³B<ÀÄ‹/¾ègõpÝq³ÏAÞ\¹ðà@@Œ78!S€>ŸzÂЧŸ~†õ|Lp]1H@q5ã ÀR=.¼¡¤éСƒ÷`'›23“ˆó„XÞz饗zž\zè!?S€}bªq‰†šÞ/¬ñ¨q&0Eqóm‹}ßèt·Új+?×?O dö'ï7Oâó– 8 säšã:ç3¡ýåtðÊlD@ãåûù ~—« ¯—bó¬ÍûÎwÿWqÝC¼\›ÌôêÚµ«ˆ¾[UÏ\ç“Kן[®­H±±±±±±M£M8`$„› 0ð °ä؇€ȃN‡c½}¨æ q‰AlâBȱ„ƒŸ?trHÝ Vާ|Kq_Å€ÏI8BŒ¤à9 VâjÑ%c+qµÎ$ípCÒøH¯1'eØ£W…ȹ !Ť…rù©}'eRükMÀQ8«ˆwÝu—ìw¿ûÿK3‡ÎÌí÷# ™3RÎ:ë,/x`VIrožÚŽG|ÆÂÙõqCÆÍ${…ÄfØÔ¶.ÿ ÌX€qXN ¶ˆ%bgœq†ï>ýôÓþ}‘`)|´;³Xö£Í½ÁXb%â†;–O@Ì8cv’6FG¨Ô§OŸ¬ÀKŸØOë™gžÁÜï=7¶ÌLb©Ël~õ«_U™q$[–p1Û‰ë$ù;@…$È‚_¾ÀQmê ðè1»&¹ŒØÔÓ\~þxït†>á ¨äùÔ%œý@ P›$@CÀCÐð p˜Ü!>¢LOä0”Âzù°ÄŒ%?ÌÄâú%6Ë‚´o€ Ÿ!ˆ{'œpB•årÔÄÓÝX¦èYÈx'N´ƒ:ÈǨÍK±ï;3BØ,âZæzg&XH°ìÉ6nܸôŒ¿Ø“-Ÿmf“•——WùÌd:Jx Ž4VM×@L±×‹ÆP›ožµyßÏ>ûlÎ2&ßCü?.c —^g«§ò­©mÈÏmrænM¹E}¬@¬@¬@¬@¬@¬@ýU ‘#–ª §gܸc åG>2t7+ØÓ瀇s€E Â_¤˜Š‹¯ìá‘CaéC_—²N倯ra|ùÉ]¤j4tod«è£IFƒ#C/R_úp|ñø3¶ðÈéÓâ'Þ±ž×L¢0é¡—úÄâP|QïKÕ† býë_ÝÐæg¦üóŸÿ4ö˜`¯ › f0ËF2@ˆLÔ¹sgT0ƒ$\%ÛڌǾ(û«pƒ©wÝgµ4~±-y³q¸6 '3w¸afA]³Yø‹=PqÌ1ÇT©!µ@ì½÷޻ʰ!pWEá:,g ‰"š9Fu…ŒWhâÉã¥Ø÷=œmÉr5öˇ’ÀÑ¿þõ/@é»3¼6²ù2a,r+æóWÈõ’<ÿ|ó,ö}ç)›™ßc€ýÉå‹õ å[ϺøÜ&kû±±±±±± W&!€Mè;€ç@ÆÀ ‚È!_xâaÃŒ–xà’ÓbÁãG,äô!ùÃCø 6Š Úºnþ„c¡D¢$®Aá• ±èc…6˜…‡ìTù““S_­ Ž)ðˆ¸:ìåƒå&=-:ìàSë¯S,…3Â%Š·Í6ÛøY'ôùË27yÜ<õîÝÛ›ðW|þšÏì þ½óÎ;éek^PÀK±ã±Ù2 Ka¸©b6y‰ê8b6ó%f„ÄŒf×èf/ÔÂ<8½¼ G€žbpƒÍ>1¢L7’ä‘Üç„å2€8ÉüBÛl7»¹€£‹.ºÈÏ6"¯ÿþ÷¿>­ñãǧ—ÔéF5pIJ*6ÕN‚1áR¡¡C«!:ï|£ÚÔ3œÝŸlfÍ2/6ÖÆóÊ'SKM ±õ™¡ê„¬Ðº„³y’àP8“‡§¸q$© ¼¡g6@SrÆß!Úˆ›ÙfÜ$æBÌvbVY& AÏóÎ;Ï×¹ÐñTeŠŸ¯¬˜÷¥O\õØi§ª}Ö²G\cŒÏL#z^â^&?ÃØå ÈÔæ:S>…\/òQ›ožÅ¾ïüãøã÷Ã13“T_ÀQCn“çû±±±±±± WF޶vg ^H–@ 8ÊèCèÁ/°H#?éB?g–°C‡D~!¹r» [áèu “ ±‘«u¬÷Ñ8ôsŽùsœAIžVI6ÄUlZÉ±Ó ÈFöÒ9“´?~EØbÇGKwð&Iœ|°£•ܱ¾¶²‘=ºz_ªÆþl$ ± 3<Ø<bö€6Ì g!àhßoXÀK±ã•••¥—Î1\ªpsʇ fÁpÃJðü$8á•E¾ðWlnl“Ę µ¡pë|âðä¦pY>ì©óÒK/¥—§!c”L`Ë´OÍ!CŒ')pÄæ·Üä†ÀËYjéF5pPÀAI —š Z;à¨6õd/®q-ã ó|óÍ7ýY®˜Â ±Ùœ ³‘ꄾк°ü àRï#³Ô¨7Ÿ ö2BÎ{Ïu›ésPÞÐsƒþÙgŸU;%>—\KÀûÊh{gé»%éHµ‹¾wŠ/°’+W¿˜÷úì`k9i®q¤ #f4ŠJù2µ¹Î”S!׋|Ôæ›g±ï;K&y*$¤?xhlµõ5ôçVçÛXXXXXX†¯@#GÛ»3sà?à/Ž` ÂDÔ‚C@ê«Ï€ð‡ÇWx6ô‘s §eZHca n¡<4^h§XjñÇN}ñô!õ#%Íò*§,êjbì@'CŸàDipǦyÉåG«ÙGø*Q숧#ì;±·ÃÂ_ñÄKúÁ#'&$@'ühuõ±ùµnôݸþæ’=Jøë7{¢h™!C*—´Õ$)v<€+€’B)Ó,«Bc„öŽXŠ2a„ôS˜!ÄÒªÚܸº0[b ûJe#ÆdI‚=ìľFÚÃÿLÀzˆÙ ÌÎÉD<òˆõìÙ3ãGšñGl ÎŒ4H7ª™€£lï KY’e»ËwÆQmëÉy‘ K¼x4z’fJ´\RúpCl@ž>–œ™#[ZÕ ¾˜ºP'í-¦%qá2Dn¨™E˜‰ ÂzmJŸŒÎb8b¹×ÄrFå™ô —¡V²´²˜ñjóùSN…¾ï<PÀ 3ñò¥8 ?;ùúc—/ SÛ댱 ¹^°)ß<‹}ßy¸ƒö‡cÖ%Ë“›úþJÚäÛ¯m=kó¹Í7Çh++++++P÷hdàˆ=Žð° M$,#y`ƒ œAX >à´Â@ëñõ±ŸÀG :p õS¶ØC´Ø*<þ`&ø3>±!ü!ù§zY^eœE]EÌ ¢0x˜°€vJŽV'!¹â ¯qt’èƒ õê+€ ñ°D«X’cË!ÛzޏÉe¯’$q³ÌÒÑ\~Ìô°aÃÒûIŸo[ìx½{÷NŸ „™ "- ↞Ü\7íòÉ·pÄž&üõšV³=Â'å/´ 7$/fö›`3{Œ x“Dì÷ß?-ßöáf-IáFæÌâF'ÜY7^u‘?õdI ˜•é½Ë8ªm=Ãz€ðþ²”ˆÚ‰’˜#—‡îi©–|’mM7 5Õ…|±T7öþb¶ ”ifšr0>Xb(]²-憞<šEóÍ7ߨ!‡’ ëûäÈ÷ ÄÓØX[Ìxuù$V¼äó¾÷êÕ+ýà€B7énJÀQM×%)äz ë_ßÀ3-Û·oï‡ÕlÈ0p˜ÈõýÚÂ7æç¶<£m¬@¬@¬@¬@¬@¬@ÝV ‘£mÝÙ€S¶€qè°¾ \A`-„ õ 9~Â@à!låC+=rñ´ø€c('pdÊ a*Ø+ã6%à(Ÿë¬ë%yþõ …³'g+ª²[›´“[}GùÔ³ØÏm²¶±++++++ÐphdàˆÇaƒ#€@à` àÈi±û@‡ 9$9-~´Ì<6¡LNäu´l°WÄ‚œñÀ4 b<ɰ¥/c³“œ²[TÕ„'*N–>‰“t˜$c #1xÅ€ÇâÄ•¸N:,2Ås&:ZÉñ mÑ1‰œd¯€#þB XÂÒ/ÍbpÆþIÚ¬š¥},Iª Ìq“Í ˆ}›^{íµj!¹Qeé¹…€Ë£~ñ‹_øÙ:ÜL±¯Ä“Úx/µ´Š}YøâƒØÔ˜¥7ùwÜq‡1ËK¤§V…ãèÆ«.fm¶Ùf~f=@<µ'úe¢|£bë‰-^…+—p† O÷;ì°Ã¼Š:hCluùàzSŽò½Î ¹^¨KHõ î¾ûî~H¾³¯¾újÏó™àû3„¤ï¯PŸïÿcù¹Mæû±±±±±± WFŽøK+8X Ö€L2é°CîPù–”?öè„{ q#Ê!™Ær¢´LãÉ„œüÂŒ$Œ/[ZˆüðÉ›^i9 ŒŒ“€BH:äâiñe\úÒG}?ô¹³EÇ›¢•¿rAÎÞE@c¨Åž˜tÈÞò’<_¸qÝj«­ü !¹ :Ô6Úh#ß½üòËÓ€@f‚hš?üUW]åiÌ’.¾øb…ó3BÞ}7óD(~¨³¯‰ˆñsæÌQ7ÝÖÕx逎©à(¸Ø×ˆz$)È“´«©Î’áæš ƒy›`ó×rfqx¤§H±¯³ƒ4{ˆ÷\7sÄò Íb© ï-Ä’&€Eû´ÌŸ?ß~ó›ß¤¯éhyo‡ ’¾tãU pààA@Ñe—]ægr©Ÿln†ûÉp­%Á¦bêÉãäÜX&÷ÐCù÷WÌÚ¢žìÓÃç 7Š7Äff7ÊùPxZ›ºð¨{Þ{˦¸VBF:µ<ñ!Αëàƒ§²©:3„”ØX»Xà ò™gžI_ƒ€®wÞy§å؈'ééÚTÂ*v<ï\ÄK±ï;CqŽwß}·ßŒ>à+û{±”ï >»|†¨)ÀT—Àñxzžè«¯¾ò³Õ¯‹ë¬ëEãªpD?WžÅ¾ï,Oã»CôðÃ5,_sÍ5%N·úþJ V0ùþ?VõdÈb>·Éœc?V V V V V V á*Ѐ#°0Z0 @x°DàèhC^¸…d™œ™· ãã.AK\xüÀ.äïX?6}°’pV:ˆßdô“1(;á/é\‰àK±HšX:)ÅÅ>ì»®÷ÇO:Å£[(öŠG‹”š2Q釈™rИøÉÖ±^Ÿ¡A›2íW”ÁÔ‹„˜1ƒeøðáé›;/L¼Ôt3 °Îx”øè£QRݺ/ \ßÀQ8V’çf?Ü;#©/´ÔQGù%N5ùQïp_£L—ó—ök¯½6N WË–-ýMºöhJ¬`˜]À ¯@–(r£+ Q7^ÅGɱÔÉ’­€£¤pO¹†ºBꉟ„0F&žãyz³Ñøì±lÀ €ZeÚŸ)Sœð4“Y>uøeÖ‘–L± p.â½$c‰]6,e–Z±7ôÄ„eÖ‡¢Lcñ¾òÄB=ý­6ãeŠ_“¬˜÷=ŒI-ï¹çžôÍ¡.äÙŸu …ãðDLÞ[Q]\g…\/WmIF›Ì³Ø÷ï!¾ Yz™‰Íß¹ž!@¼L j¾ÿÕE=É£˜Ï-~‘bbbbbb§M8ãìƒ€$t’;6 îà#p,B}ZàOHèÀ1h‰‹¿Hrúa â0¾ðùâI/?ø¼çBHI¸*ÅB†ž“£… ࡯ä%§Olˆ8ðê£SlK,døc‡œö™N9‘Ÿ„>yȆoc˜/qSÆÍWÞäòMnÀÌÒ$f¬²Ê*UÜøq ¨Ä‘‹Â͈µÔDOeËäWÛñ˜ÚG%¹_RhS(Ï 8³µ† b ð7„;vô7¿Ìn¡VŒË,º& –»hoÅŒ`F›3c‡Y!l\}øá‡~Ÿ›L7Cá’Eòåæ ;fÐi9qØÄ˜¿ÜóT·ðÉ?ìÇÁ_îù‹<³oX¶¦±Tÿp–IqäÅMV®­I“&Ù­·Þj#FŒHä’‰8wö`J’žÞ–”ÓÏ·žØòùá¼Y–¸Þzë!ªF<9ŽºQ+(¬ï9çœãÏ£šSA]Õ…ë•Ù<\3 ,ðËvr}þ”K´˜¨YT’/]ºÔ_gÌPã}4h]qÅ^mçhÆŒ]8qíp]õïß_Cø[6ç} ©˜ñ£Ð§/23ëßÿþwQï{˜/<Àá)§œâk#û^1«JïKV…ŸÐ¯&ž÷ŽÙnIJnÜ^W×Y¾×K2Ÿ|ó,æ}×uÆµÏæê²ü?Eì©Åg•÷ šN|g¢|ÿ««zû¹Í”{”Å Ä Ä Ä Ä Ä ÔšpÖ¾æ Ї¢è ôØâƒúÈà±¥…ƒV$;l4.X}lþŒ‹?ɱCi d6È”_86úŒ¤À• !!ľxú$ªä[ÍF cƒ½ÆVÂøˆW|r1|9}x@ø#ã½c=aƒ?ìèCðµ~ªšTÀ 7;Ü”” ±œm¼{â’ntr…â0AgöÅÈ‘#ýÍk.{tµ¯¦Øõ¡×Gu PeË ¿wîÜÙ/maÏ+ÍÈÈæW¨\ïc17¸º+4V.ûðF‹ý ŸX¶HQßTL=YJÇç S§N>W6?gy'ÀLH̨aO,>' …Ô¯®êî?Å2Ë !êÃ90«„sœ„ð(„bÉŽ¾H³¡ Zª&çØÖ_ƘEæՅޘ×_V+GäðF+\®³rd_YÖE]ýØì‹Ù~ÌDËw©\ýYÃG𦞅à8Ozü¡S]\gM¹F€·ìqX”‰˜%÷ûßÿÞ«žxâ ?Ë.“]¾²º¨güÜæ[íh+++++Ðt*ÐÈÀOûW¼nÁ!@G}Z9ø…Žd?œ°ÇN1À(è3–p° çX¯§E&L…>~ò¡/b|ì å˜É.e‘å‡|‰“` ‚4à 2%ŠN}3$ËÆÕ´*±äK«B¯¼4†€ŸðDå 裂ц¾Ø(F+ÇkLÉÈ p£T=âë uq£õ(CµS(¦.,bß!f‹±ô‹%dZbzñÅ•{—U, ~´(æ:[™ŠÔµkWc/#–lN˜0ÁÏ dÆ&àI“¢C9$½ÌT²BÛbê?·…V9ÚÇ Ä Ä Ä Ä 4½ 4àœB8Ø„ úFàÈÐi‰›âá­°ÅUŸ_|…¿Ð‡Ð… ±å æ"ä5’×hè \à BÄPÂÒ))Z½NR-:l&)–ä´²Ü¡ÈŠë ½üiÅS(=‡Æ!&:Éëû´ ¾TA#Å ÔGŠ¹Ñª<šZÌbêÂFÜÜ$'‰Í°Ù;R¬@²Å\gÉM¹pÄ žšˆ½ÔØÄ¼¶TL=ãç¶¶Uþ±±±±±_FŽØÈQXX˜Ø‚@!úa¡ã0„ mÀ$èC؆1¡× -ÚË_¹^c`O.ÒÓ2~jë ß Ç|Iƒ*)@IÐêtrô%ðÁWþŽõ¤¸ØÁs²v0@É^ ±àÕbC^ò§/?Í.r"¯×xÊ»QH?ˆ üñþÑØœÌN;í”~$ùâäjqÅÔ%¹A8³n¿ýv»ÿþûëe_¢Zœ^tm"(æ:k"©ç•ˈ™I4xðàŒö}ºßø¿.ó.¦žñs[—ï@Œ+++++Ð8hÀ¸ø<8hC æa è¡•L{Hà²0ñ±A®Xòq¢*8‹rÏ€g‘|ˆ^y`á¾ìœ(;…³[¥4áI_dÉ(–ìè#§/ž„éË9 #‡he+¿°e\ù ,Â’] {úðŠëeŠ%€)GT&R¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@#W ‰Gy¨ø…À0dÀ#ñà öÂ@4CIX ­ aÈ µy„_Hž²Jå€/q„‹À‡ø y kq¬'ÅS?k«Ä²ŠL¶\ÉqBð"xlhUL;ØÈ^ñåOKAt²¡½bü$íék,ôÄÂO¶Iû·Ý)V V V V V V V V V V V V V V ‘+ÐD€#°0 Z,p†-‡@"úàòfÚ;u+A.{ÅC/^-q a,©^êô±dKlñØ0Žâ967a\aÏ`€ †‚Ñ’¸’qlºPð豇hu”<¼Nš™D6èÙ›ž±9ˆÁ#Ç_ì°—^yÐr(n2cãƒoœqäŠ)V V V V V V V V V V V V V ±+Ѐ#ððô»! ©/üŒCX¼tZž&,ô`ŠãXe(¾`øb§¸ŽõDŸü ì°×œ8´è’¾N”4»E¥F¶:QZNR'¯¦•’R[’¤XãØôÌ xù`á‹­HzÚp,Å“-:bÐÊDZ^FIø`S7»x=R¬@¬@¬@¬@¬@¬@¬@¬@¬ÀŠ °WWIi3+-)µR·gÞòåËÝÜ×w?Œ+*lyŲT»œŸÉ‘bbbb¨@Ž„w”´À „Ñ8­ð ìÀÉÇzʤG=؆@%?Ä%†Z䊉ÆENBa—É!cÙ*)ú^ú0Qôô!ñÒ#Ç“× àP(ùÈV`Sù8ôñS.ðØ*žcÓùH‡ žCù․Ü"päŠ)V V V V V V V V Ø 80ÈC­Ûv´ö{Y‡.šµZÃ*šw°’fí­UË6Ö¾m[kÞ¢¹•”TØÒ%ËlaÙB++[àÀ£2+u‡-žn g¶y³ÆÛÂùÓmÙRýaºØœ¢_¬@¬@¬ÀÊY& ô3«à€d`’ÐÉϱi?a&!ø#ð baG_c"‡è ;ñ‚/Ê_ÙŠ'–ü0ÑyNRâ9V(±Õ¡•­N€`‡ð!18≅­b86= ™rc,âhlâ@È¡§-`±iñS ì%׸Š]\ªæŠ)V V V V V V V V ° 0›hÕÕz[—µv²’N»ZÙòÞ¶xÑ×ÖléÇÖaÙ[«ã|Ûº©m3 Äz¬Qa­Û7³fnºÑ¢…KÝÓ+ìÃQËìÍQf£¦w²Y=­´õÖ²eokS:×Jæ½bs&½l³¦~aË‘êŸÜ)7+ªþlj#Ä Ä Ä äª@#GÛ®È ƒ/_0x¼B¶ =xƒt؇rt|³Òr€E8Ò7.ññS^1…g0žì„»Ð ãŽ>rü4n¹ÎDZ٠§BI>:DÄA&½’V?‰ë°¡O‹ ¢Ua5+H:Iím„/q9àeÊKçØôr5l ¥ê_cbbbbbbbò¬@›v¬÷Ö²Ö}‡ZiÛöV>í}›ôß3lÞÔmõU›ÛžÛÚн;ÙÀ{Xé½­¤mo·;gWÝýœ]:×–/œ`6ëkõÁ$»÷™9öèëßÛø)ó­õ*=­×ö—Y›^ûÙòòÅV>ñIûöýólÎŒ¯ó̬8³R‡m7 —M5Ͼž:Ç–Uð¾÷tûÖ-Ö¯{;qM?ë¾U_+éòs[Òfk«hÖÃÍäiç"§~~.w?o+–»}V+fXé¢ÿYë9¯Ø¼¯FÛøÂF¼?ÙºöÙÖ69öa×v³ñ_,·¹_޶ÿ=¾¥-q@R}ÒY¿ØÚ¶\«“½ðÑ${îãñ6m®[Jæö•Ó@IDATǯõH±±± X&#€Ú€GÐpƒÒ¬"ä~ÐÉì°I˜1°ƒƒ>vâ‰+lDñèËGŠÆ¤E&xlé+&2Ƨ…Ô¦zY^I*_RÁ°Ç”2%‡Žä°WBJV}µÎÄ 'BK\Åó ¤¸Î, Z!PDŒG^öe‹cHŠ3ŽT­ØÆ Ä 4jºtébÛn›šûòË/Û¢E‹5ŸB_}õÕm£6òç0`À›3gŽMž<ÙÆŒc=öX¡áÒöÛl³;I“&M²?þ8)N÷‹õKXɘÝvÛÍ=ôP{ýõ×møðá+Uö\/}ûöµòòr{ñÅWªÜc²?Ö ”Øú;ŸokíužõÙÀÝM8<çÛÆ˜}Ñ0ÁúoÕͬûÎVÚ|u·ÑR·6G…Û߈Ÿ»%ŒYZQbK–5³òŠÖ6oq¹-™õ‰-÷®=÷A ›Üj ë¾ÝϬCç–6ù³‰o¼e#þ¹Û©þ€£Í›Ùï÷h9àhÖô96wþb{䃉öÙ„ä"ïH±±± SF޶vg)|BÀ‹f ÷{0¢-v2ñØ@ôÁ! Ñ—¿bÊF¾ÎÄûcàC¹ AŽŒ>±°‘N±Ð ÇQëD¹‰`…ö¡ƒ*á0 pò´Ò+aù$O9$$òp9zù¢S_¹ÑRPé¤Wß©¼žø¡ò‚€£cŽ9Æ6Þxc[¶l™{î¹¶d‰ÞCB¥h¿ýö³Aƒ¹Ëí‚ .° HÛF®7'C‡­’ÅÒ¥KmÚ´iöÝwßÙ|`£F¹Í"åU}x2ÌŸÿüg[¼¸úÚ­¶ÚÊßÌÆÏCÍ%Ýe—]ì /ô†‡~¸7®f§&bqÐAÙé§Ÿž1›‰'úbé°>}úTsçózòÉ'W“KP¬Ÿü mõyÈõÿC}}š¹MyGŒa-Z¤f3Poê¾²×=×?ô“ŸüÄ?ejeÉ=æùã­@ë6í¬Ç “mÝ]±î›o쟔Vö½ûu¿ÐýuÒýmݬÜýèt_»ßïüPuÏTKý æ×©,s/Ë–;ðhy©-©heåË[ÙR÷ ¶™û¥êöÒ¶fî—ðŒ1_Û¸7^³oÿ}Íùm½{ËõzÚñCÖµæ@Zâ6ï®p¿qç»Gÿ9ÝÞýòÛÕkõcðXX° m³"° À¾¹ié‹ç«=ÐÑG.À„ì â@Ãý“£×öáñ í…¿€ÇS‘Œ–qÃ1]·fÂ!_"’%±ˆ!ä‹D8 ‚G6P˜,}âêdå«>zÉò ƒ”-À’€*ú*¼üù<ôSîœñßvGÞtË-·Øf›mæíwÞyg›?~5_À"þò "M:µšM4NÂól|ýõ×vâ‰'úÙÙl¢Wà¨gÏžöÈ#ø÷ü¹çžkÙ_Å@ÅúU¹ stÂÏCcG9Rkòª5ù·¨V þ£-šÛÓgôµwïbåkí`‹JÛº?G·°Enb|E«¶în [ZÑÒÊÊJlÑb·4­‚ŸŸN\ºÜZ¶¨p³]Ûl‰•T”[ÉâÖ¦ÄyV,ÁÛZÍüܦ¼=Ú6;w¢MŸ]}†{­Þ”À¹SûÖ¶Íú=mï}¬ëªmüRµ¹n‰ÚÔisláÂr÷G´Åöåä9öü˜éV¶„û™H±±±õ[FŽXª~À‘„;Àp`C+ÌB_’+V8ØðŸ6qÀ)hEðØâ«1è+®cÓþòE¯X¤±R½^åTƒ™WcË¡$ié“Iè¤ÔWlµÎÄÛ ¤¡/ÒŒ|!lð#®ìÑ)–ìèÑ*úìñW …ä^ŸIàˆÚ@ùÖÛ†8?ö|»í¶ÛÎïû8SßT,T¬_¾ç~ê 8*æ}Ï7ßLvÅ\3…|n3‰¬ਘ³›¼¡Ç#§bÇ,Ư.Þ¿šÆ-ä{0Û{Ryf‹]ù†ÝZÛ§×®c%}×±¥×´åÍÝL"&µX¥•Íû¾Ì>xm‚[®Vj[ ^ÇÚ¸Y<ËÊSÿÇ•6/±’åKläãlú·smózZ·µVµeóÜ*ˆr÷Gç¥N_æþ0ýÕ§vòu“ì–W¦×&ͬ¾k¯ÑÑŽÙi}Û¼wkÙ²…Ÿ]4{Î|›=kž›A¼È,^bsç”ÙÔ¹ mäìEö­°–Uä÷ÿtÖA£"V V V † 42p¤¥jd)àìƒYAúB‹ŒC2Çz^}Z0 Í*‡â lrª4Î7Øa©O+L¹xé鋟xà!¡\új-Aò%‚“`XIG,%ŒLz|tTǦ'ñ´²#†NDqˆ…?¼úšQ„Ÿì…y%m8á#¾Á÷8bóRn*¶ÜrKc–Ks7u™½(¸Ù¬˜2eŠÝwß}.E³ÚàÁƒýfÜ×]w¿iÄ÷È#ôË«æÍ›ç7DE·p¡Ûqã'B<ùä“þIHŠ—l·Ûn;Ûa‡¼˜™9+Ëàáy&àˆºæškÒO¶Ê´OH¾ïß¡žá²®‘䬓\ÀSã÷ß_ïÍ7ßÜÏöúòË/e6?ü°ß°üÿÙ;»ŠêÿŸÝM²›Mï!!©¢Ò¤#BD° `C ±b‡¿ÊOƒ¢bCÄŠé"þ@i (?E©é„$„ô²»ÙýŸÏ¼ý¾Ì¾}o÷½»›ÝEæ$wgæÌ9gÎ=·¼{¿wŠì¦Y_D6Ûl3;î¸ã¼¬ÔÅ5Ä'zK=ñÄ…Ít¹\‰Ÿñù pÄ|[\ÿǼñÊœj ûÕ¯~U26=½¬Ç5}âŸÇ®TÐvÙe—0”ŠakܳÞXH ¥;_{íµ…Õ¡œʪWÔ‰"Ìøz¨8bˆÙûÞ÷¾`ñg?ûY8'ããÎ9ÊüEœ£ £Mš4ÉŽ=öXó髯¾j—^zi¾ÜY†{Õ!‡bÎ~þñsUq=þßÿý_½+×-ýÞ{ïæ(««« óÉშØäØ=y^3OÇcúôéFŒëëëÃoó©±xqá\e=¯õÁdê›ú÷]¾’f9~Ü+¹A<Ÿðœ‚ÿ<‹s—,Yæ6ûâ¿Øæ|A§’û ò11Œœßk†&Ó ˜žÁ}…x˜üÚ¡#íìÓ¦XÕ„)fý±Óÿ·Ô÷³æš*»ëÚ§ì¯wÌñg»fÛïè­í€ãÞlýª]ËŸb›ûUÛw=o¿½øŸ¶~M“½q·1ö®“w°a8Ùj¿76øã±ãF-//°§ÿò’íòÍY>LŒÇß?:m;Û}ÊpÛPÝÏÖ74yï¢FïÕà=¤ý¼_o+×ä½5|°Õ ®µ¹KWÛ=Ï,²K|ðD)))›(}8â!•›.XàÃm¼B|d„CȃŽ6Ïù¸N˜2’°­›<|d x´}pê ùR{òG¶dWv$”:ûƒr%¤ÆpŠ<;AÃr\)u…¶å¸RÉëܰƒè¨^mÀ/ä!O½‚(„ðéË&e(æKOé_s"åý-çÅ€œR“c7.??úÑÚ‘G™¿ÄN¬X±Â–xQ‹©'öö†š÷k÷Ýw7@?è/ù‹ýë_ÿ yýa¥¯Ù³g‡âÉ'Ÿl'žxbȳ¯"…tÝu×…—õY³f…yÆ ë)g€²êó¡/¾*ŽÊ=î:Üç·ýöÛÏÎ?ÿüv®ð¹Ž:#^ö¿ð…/ä¯JÉxàù•^·±M^ü:¢BਧÎk|6l˜Ýpà ÖÑo4÷6îq¢¬çu–ßiÚ̪‡nÖã·ãŽ; @gŸ}vÞù­,F?®ޯe›{vü;ÿ¾H¦·ÒºþÕöÈ—¦Úö»Ž´ª1£ÍjýѵÎ9‡ô³ÿóªÝþ‹§mÊu>‘¯¦6¬ÎŽýâ.6~ë¡Öâ=vÖ.o°¾ó¸-xb© Zck|íýŽÚÊvÛ¢Ù*Ô]ç›N-+VYÓ³‹mÛ¯¼h³–ä@œîÚßµýí¬w¼Å†Ôl°õë|e· þ@ÝÏÖ¬ß`+½·‹û0¶QÃë­Î‡Ö­_·.ÛQ}¬«örÒ9Е/´/=ÉR×)ÉN]@àdµ}Ô0ΨqxÔ‹TV}ܾòèÓ²äáS&EOyφ< ûCYþR/d(c‹MöÕF Uã!/ÆzhfâåGyÄæÍ›¾¤ñ°ÅKg)àp¦¶¶ÖwÁ‚ «µi…7x<ì~÷»ß%ÛæÁ²\=ôø*¨‹x1ÿç?ÿìÅâH^²yÙî ><ô^š8qbøjÙ™-z!d]©.~A+m·Ývvùå—7b™J_oų³øug½^”,ù ý±},˜ÿóŸÿlŸûÜçB¾pDl8Ž|͇@(óBÌ ]\ "wœ'wÜq‡Ø¦·ÜrK›•¾:9_µêÔ½÷Þk÷Ýw_˜^.¢®®üÖ?ãó“X 2$¸µxñâГ‡Þ=¼ØB·Þz«}ó›ß yþôÔþÑÖöÛoo—]vÙN‰¡Ÿ7ß|sËú‚]ØHV(«^aû¥Êº¨Ï uvÜíÑc•Þs">Xì—!ǽ{×]w êôzáº}øá‡ÃV±à¾'à(Ëu+ßèåòãÿØøý€hž…ôh;v¬Ñ‹U×f õäy_|4 PÇZbmR€®‹r€#üûÚ×¾ôù-¡'=McâCÆ”)Sì…^ä³\·š“ëóŸÿ|–Š}>–|êSŸj3ì®ØG=}^ã€,`¤{X(´þÁ'®?€/QÖó:ëïtV½®¿bÀ÷Jz)744´yæ8à˹g½*®Jé­œ;ô¾îÎUÕN–tëñuö·/L²ÁS[µÏ_TU땵þ<3¸Æî»ý%[¾fƒ½ýØ©6h`]÷ƒ§í ۱ݜhÕÞSvö?–Ùo™o‡ÏØÒ†;pô·›æÚ+sý·ôØ)Vã=”ŽZŽV¯³†ù+í”Ë_¶_?üj7KêÔù\L'°“ízðûlÊÎ{Y½ƒEÏ€]ˆ‡¬pêµÅz؆¯Ô³AGíPîP,—h˜ ã4гäIåÎ#ƒ]Ù&9í€d$¯:Éë£P„,rlȱ1¡5 @Hõðñ9Rñ=Êq*ÉS—y¨Ê‘€£xzW0¯KáüåGšO„¶yˆåk>_C¡#Ž8Âxy+|°,W¯Â¼l³D=4cFÛ¡Ztsÿýïê˜C/÷Qázf`‹‡ðJhSG¼ßtÓMÁ¥»îºË¾ò•¯„9x÷JŽ_OƳ’v—¬^”Žxe^Qpô”SN) ÑHçl±¯Ç…/ŒÅ^à*™3ƒ¶hÌb¦^háq¾ßvÛmdCï'@°î¢Jü,ŽèŰØW€zÅAôÒ€zsÿ˜ëŒD¨ð~˜ÑŸ¬/Ø‘‰Í eÕ+l¿TY×õ]Ž::îB•Gq/†«Ýÿý¥v+ÏÏzÝNž<9ÌY†!~÷;ì°üp;/õÆy}ÕUWsNA|4øå/Ù)H‘õ¼Îú;U/ëñ#…ÀÑwÞ€GÝ›bŸâcèB•Üsmÿrf(4½Õf[‰Þ)í8©ÞþtæŽY¿¡ƒ4êïà‘û2 Ê.Û`µƒûÙðá5VåÀÑ‚9ëŒéÆM¨õI±ÍV,o²e¾Mž\çOÖlÕŠf[¶¤Ñ&ŒêgÕ/¾ôHƒp]9Šö´ÅegçåÉ£/{Êc ŠõÈÃÇ&$@'ôÕ Iû‘8¢×BÇ_% Á¡¸'ȧ?ýé0¬,Än¸.ôÐCjBô&â%†‡wõj„p0V8·K¹íÇ/æ¥zÅ/9ôJá8g=~øÕSñ,7Ý)§eG<ØóZ=P>þñ‡á guVh–ëù¸xàP&O¯¹beþò—¿&…Žå*y™6mZ~XçÎyç› ,ó-‰Š½ø¨®Ò´?ãó“/ëÌoV8 &¦g‚a^¢zsÿp´ñŒÐõ'+pTÎq`¸±å\®\àˆ^±L²q~í¿ÿþ¾ŒÓû(ëu«{=íᣠ,*õÆy÷>Ä·E‹…Éè‰×òåËån›´;€£J~§³ü¾˜g=~ìl âDO#õ| ã|l+w*¹Êæk!ÝÎWT»ïÓmˆHµ#Žê¬ÊA£ðÄê Q@Š6ø#/ˆs1é5äCÀ¬Æ·µþ>ÀdBô¼f[å#ü¿/¸æh£5­\e«ç¯¶3¯^l¿z¸{€£1ÃÙ'÷߯†Ö×Ú¾§}Ó®þíöã èÃÒúÙÍ·yo£Õ/Ù/ù¦¼«lô¶»Ù{Îúº}Æ? ÝêwÚå­öýï]`¿æB›õÄ£öûVÚ‹ —†]JRRRº+½ 1Ç‘îÆ"幑nÈÀäñ›y+A<‚´õÀs9\BeäÁ)h}ð êHU–MÉ"‘²!+Ò3AŸö± ¡I?W*ñWÂ%ªÛ°iTÆH;‡œœ#ÕNˆ/;Ô)¯v´“ÔÑ)׫,Ôû[°D*[â£Ç&ÙÌÀQ©‰Âɱ6£ùJ¦OŸî«U¬ÅŸ6ÔpÄ‹ôK>·B!ÅCfÌhU¢‡m^r$éÁzÏ0g| ùjÜ•îâ q¡wój@ê ð¥/}Éøâ¿4𠪉9ƒpÆ?ñ‹y)àèMozS~E¢™3g†^QYnöT<3†¤KjzQp„1Î}b 1Ä…s†àRïæø:ýôÓ¯ð+öhþ, Œe*y¡”V­Šm”ʳÂT<Œ´”\9üJüŒÏO^F 'š¦=@/î!€£ÞÜ¿…Cþèz Pê÷É­Ï8㌠¯ Y{0ý)8âaŒ•ù @{»‘©vÙ¸÷O¥×-C-YEb˜]ápFøÅ€£Þ8¯jÊpìx•7üƒ˜ü½âàwpTÉït •«Hß•ûn sÎ9á÷š}/—*¹–k³/È6Àþö¹Ím¸÷8¢ÞªvàÈ;ùT9À]ä§<‰é çyje4 ¯^I꽌päSËú5Ö°b­œ·Ö޽ìe»ç©ö ¸FEÄJjïßw{ÛmT‹5kûŸöu»èÒ+ì—¿¸, 1¼ú†mr]£Ýþ£¯z¯¥¶åîÓí]§ÙÏóSì¾{ï±7nÿ&ŸOó;6÷þ›ì±?Üj+ ³ëyÁ£á›9”„SRRŠD —£=Ý%îЀ-Ü™µ Øá®®“À“OèÓ®ì¡ _v<Û9¡\.Å ;ÃqÃÈBðb¢=6œG_<í¤ì’BÔa2§-x ’gÃÏ2)¤ºXŸ<|Rñ±‰ ÙÆ§M>96=dxà„ø RŒ®¿þúÐS pˆ‡A(~@Ô¥…º_|q~U#=DfÕ“mìhN–*ÿä'?Ùf¸/téï.ŒœèeÄJZH ½ƒ~ô£…‰ÅC¡ â´RÀmÒ6Ä0ª§žz*óñ“«=Oµ»©S½(ÇÀ#Cý˜ðbî"ÍûÅ‹2½’8סxíÀˆþ°Rç6¨Ê9S%/"ÌYD¯ ˆ¹I W[‹íò¥0¶¸Ë•›¯ÄÏøüdò]€·Bš9scïEG½¹Ý é>øZ\UMÀ Ǭøx '„!œÈŽ*9îØˆIíw4œ yVædÈ/ÄG‚¯ýë!ßÑzÑf½n¹h~1zÞâ_!Å€¨züõÖyÍPYî? Åf©ùB Ç_ÍsTpTì¼Îú;EûrÖãÇþÇÀÑ™gžiýkEßܺѮ¼úZ›û—[íî_^l+|~¥Í·ÞÞ>:ó"ûñeWÚÅÞ+iÿ²óÎ=Çþü‹óí©¿=h«[êìÚGgÛÚ´ÂZ¦ã‘”RRŠG —£ÝÜ+îØ`x˜ø|Rd¸“S<|H|RôHÙèy$l‚¼ˆ:‘dàÉì O{`Ú$_¿6ä!ÚYʪóli’Ri‰¶5ñŽªF;KÇq:v’6àáyÙ ¯ °ãr\;žlÇ>c‹:RñÑ‹e©£'>I^~²©þÏ—MzQF¡ÔåÂGLbKˆsx9,$&4塪8â%œ¹*4!1 àTg–¥ôäóp,»LNÌ×_¡R/EÒ¯4—•fÈÃ×Î@¯$ˆ‰Å™,¶«¿˜Žxaà…J/ ôªâ%'ëñ“¿=Oµ»©S]1pD›Lzþ­o}«]ó¼(Ó“‡ÞeË´Ó£qŽ{ŠÕØ´6&2 ’g"^–††²|-—,i%~Æçg%Boî_%ÀQd[¾ýÝvËí°7½ùͶ붓í¶žc/ÍzÞç ¬±õ vÀGN·ñoÝßWï½ÑöÞkO›2¸ÅnøöçmÞœ…6U•Ýþô|kôIö¥¤¤tWz8¢Ç75° @!ˆ›7%Ó1b¨ítÀ‘¶õ.ûx¯§Fûןn°ùÿyÌšªýCÏŸš`¥ÕjÓŽ>ѶÜq7÷w®ýó®퟾Zæ‚Ël¶GÏ_âÓ1éý©+Þ%Ý\úp¾Á ›” P†P.´é¬Ò„B¹¤ q9‚.ÁÂili§d9òqÙ‹A=ÕÉå88ÈjG‘—=Rä ‚I6ˆž|P›èIÖ³¡~“Ub> .ˆÉmµj/I—^z©M:5Ôñ'fÐуåN;í怡÷¿¨dÕ †Zÿð@Oo&ÙW]ÜŽxYS†|àù—Iìл‰É7i[ÄÐ5†°1q5À›æçP}¹iü‚Vq 8zqåOíd=~±_]‰çèÑ£Ã<ô¸êª«òCçbû½‘×Kpa#|Ùu×]Û /XÀP½€±²Ã>âÉT!®&_·n](ëO ,É®ê Óz>ÀK“€Hõj+”ã…šIÙžØ]T‰ŸñùY pÔ›ûW pÄqbqHs™‘ç¥ü’K. ©S†^‹ÀQ ³Ú%såÄÄõË„û6€JÜK¡¬Ç=¶M¾\àYzðhbzz¹²bg‹d½nY™’fÐ 7Üàó£|7äùmá÷p=öeýpÔÓç5í1ñ8 {Eá[ÜSëÅ_Ì÷bÌz^gýΪ—õø±ï]Ž*¹Ò^L}õ÷O>2\í†mÇmÚà1µ6°®ŸÕxÏ£õµƒìþ_±G^`Õ>TmüØz_Á³Îêëû…‡Ï5>)ö+¯¬³/;HäÏB»ï5ÑöÛgŒ X¿ÊA£ï…ä+­-k°W篳Ã~ºÀ^\²^MfN‡ `»o=Þ޾ݛ8b õ«õR-MÞS½Ñ{K ·¦†µÖ¸~¥ƒ@Õ¾âÛjïôäÃå|¦fŸëuìæ¬¥i½­\¾Ì^YÑh«V®³þƒëmÞêFûû‹/Û¢åkÛü–gv4)¦¤¼î#Ѐ#0À0á* H¢ŽM|ÏæÁtE¨L Áü‰‰:p Rì¢/Ÿrl;´Oªvä—³ò|òÒ#_a°’“.8AƒrL¶àQÏΑBððPFŸ2¶!ìW™:ÙV»Ø‚‡>rðIá!/Ià”³Bo$ê 7É`ãaË%½(#_I#ÀV˜у†—íSO=5ß…_u¤š»$~@dÕ'VF€àÁú(_UtþùçÛo}µ QV=é+eŽŸ3f¨&5å…?~ÉÏWfȰÔxáÒçÌHÀq `ø+L}æ3Ÿ /ÅzB•Ó|ü‚öïÿ;¬ Ç—î­¶Ú*¿Ä9vèC|Vd=~…>e'/Zœ¢bÃ|Tד©®‡bÀ à–ŽÇ/<€s·Ýv[¾—ó[!ËË󯪇 ²…ÄùÀyÎñÌŠnLÐÍK1íi÷¸÷ç/o?üðÃaèç€$ç6ç\©U« }(§\‰ŸñùY p„½µ•GñË'ç ÀÊ€ÂéC‡mÎr€£¥K—¶™Ÿ‡aÁÅzùȰzUª'ýÎRî#W^ye^Œû<½I¹zsÎÑÃb¦Àñ®÷|cžpU/EÏ?ÿ¼1×^L\ƒW\qE~PZ€sB  ×÷®%ö!ëuEƆãC¼Æ»òŽ(ôäyͽŠÞQüÞð±º'÷æ;¼`Þ?q(³ž×Y§³êe=~ìo¼Yæ8ªä>H{1õÕß¿ØÇ6¯µkŸ`õcØÐµVW[mý}~#TgKV˜=óì ÃWÚÒ¥kýZʽÔùðµ‘#ëlÊä!¶Í6CmÌP¤]³Ö6¬vШ±ÅA™F[óò:ûæK첇Vt¹·Ñ”q#}%µ­lœOâÝâCÐjüÞËäÝ+}±LJì-ØFo±­-^¸ÀVú½´©©)<ëUûüXë‡Ø˜ ›[ãŠ%öÀµÛËsgÙ°áÃlè6°¶¿5yo£¥ËWÙmϬ°Çž›‡&åSRR2E G€1` ܴɃA€9hÓ0ê ê‘E•ᑎáÙ¼ äD’CVí‚UPfC"Ú¥=ñ‘ƒÉOx2ðä_Ü6õEI†‹V01 Ñ z”•§Œ£rγídä02È«m9ŒŽò²¯‚ÏÁÀíçLžlÃc“¼g!=d)CäÿreþÑ‹2â•G¼ñò«•Ê ›ãkêƒSøú? ê¨?ÀŠ—UOúJ™—‚— i`å›;ï¼SÕ]Nõ"'C>úh `ކrRüÂUX×Y9~A+%Ë<Çw\xaLÖã'}¥YãÉ LÜó ò±Ç“Ù^Ku=Žpjûí··Ë.»,# C£·¢¼P”áüøò—¿œ¢ªp-ñ’Ç$¥PCØ"Ãq=í´ÓJ‰çùÝ qÍ—ëg|~V õÖþUq¬‰K1ø`n î…åG…6æÏŸŸ¿‡ÖQ.¼ßH¦3=É•“rm4wDô8åÜuõ¸ËN ‰GúóŸÿ<Ä=æ‘çÜdÅJM\]X¯r¼0C–ë@MsÖÉ®Ræc¨âôéÓ+Ž`ôÔu+àH~•JÆÌêp . "YÏ묿ÓYõp6ËñC¯«ÀQ%÷AÚ‹©¯þþÅ>öó§Ë3öfóafu£ûÛ`²V_çàLŸèº¾¿µôëo««lͪ ¶ŽUÓœêjk¬~H ê×bUüùM£mh¤¾Ùñ#ïm´¸Ázr…}üú—me;q žrÈN¶ã÷¥Æ‡ÒyûLµÔì=¡Öú¸††&Ûþm‡ØÛŽù˜ Ÿ°…÷ç·ï“÷VZ¿¾É‡Ý­±UÍì§÷>iM¹}d?¥¤¤d‰@Žäz iˆ7:n§؃$É‚3Àe¤Còž r…)õȱ…ÛvkûÂA<êI‘‘]ÚîA^eù"›^Õ1!X.I–†ã_NÄe7´„“äc;ô Ò¢/è“Ç6yH@O®´Ñed°+’…'[ž yÙ£LûPECÕxØ×Ë(Óê‘’3•û/!̾nC|± Š_š˜˜ÞBÌ鯼¥—ÆŽçÍ›–¨¿çž{Ú½XgÕËíÁÆ¿FôáK%«cÑÓ…‡æî"&=æ«6„]z|ÐkâK4 ÃÈ‘#C™?Ì“ÀJ[Y{<í»ï¾í†ðUX²ñbɦbû˜åøåoÍdgóÛS–ó:ëïtV=ù›åøÅ Z¹sb©=¥åÞ%¯´¯þþÉ?¥ƒkÍÎ{çh;lÇaV7*Õ:¢ÔÏç4ªñálæ«™9 ãO«­»¹ñÇàÆfkvÀ¬¥ÁókÖø¼F¯4Ús¾’Úi×ùµWôœ¯–*O™ûœwílC}Ž%xÛàm3ÇÀQŠHä+Žn¶õvö–·f[ì°» 9ΪÜÝu+WØœ§³'ïÿ½Í}òïÞK´èùÞаÁV¯m°už6¸ÿþ»»ÎWa»òñù¶r]÷='V¾·I#E Eà¿!½ 1†ž›5à€ nÆlð¨S™‚~¡­°NÈ#'`”i ÛäýœoϳGŠuÈ@¤Ò ŒÖ?´$‹Éå$JüE¡\b'hHŽ©1ÀxrÔ³ù2:Îz?Ø*Ø’.©vˆzù¥6ØQµïÙP/]@Œ4ÖEF6üg<øóð pUp”SÍþ—s^xC"ÌãˆBËñ"“®òPͲºôðàå¸eÕ+´Ç°±™3g6Ë »èNHahìL6 8ûÊC./J ¡èMªôøúš5žÄ‰/¾ £zâ‰'ÚÅ©°×Z™]&Lç4Çx¹w¯„øâÏÊL|ÕFwÁ‚^Ès Ž5*€¿\‹ s*|Y­Ä‡rd+õ³›ÅdzkÿŠùRŠÇд)S¦`k{SǾ”›ŠÏ1àœåºåþ6gΜÏËMåK9v™gŠß&®!~›ŠÍóÛÊrÝÒ³ˆk•ë `´’ãÞ“ç5CÂ9Gñ—ß¡W_}5Ä¥ð7*ŽùJÎ묿ÓYõ }Írü md)Wz|-ýþ Xe_9d¤±ãp«õžGu>ŸÑïuÔ¯ÆÁ#_‚ÍÊÛàF`G|!@œ†¦f[ï=Ö8hôÄì5ö…[Ú3‹ô¬Ÿ%ÒuªýÙócí`›Õ¹=ïýÔä(U³7ˆpmhj´fïù4p`?Á†úÇÎ~ÞÛhÝê¶r±ß£}þ£kýzèïCéšÃüGk½§ÒzŽ7xÙ{kû#ªÝøì"ß磻¤d@ޏ‘é&L|²^ÀáÅ2ÔQ›DÀ,u”±%{è‘GVØŠìªLŠ.DtáKFu´… òØ–.y°éÃï”h¨\’q–ƒè’WYur*vµ“JÑCÛ’•=•% ¸Cy6õÒ'Už@AÔ³©ìS'¾gC™´¢¡j(ô$ňñpÎ|ȪÛåÅšI¡y¦— óÆÐ+#Q¶¤xf‹[ÒJHHøoŒ@Ößé¬zÿ1ì‹û䋪ÙÞ:ÈΜ6Æê}ÈZÝ`êªÍq˜äO¨GM5:³~õ[¿ªÉn~t¹}ïÞWmÑJ¡»vÞj’:¥Öš8jñ®DÍöÉ{Uù¿À-˜jª›0ò‡dzK9hÚÏ{JÕøV]ÝÏšÜa€¢u>4-ô6òt=½ŽV®µU¯®µ§kÙ³‹–tŸÓÉRŠ@ŠÀë6½ íæ–Á ™ lA e0ð¨c0¤›x,&AB6¶:~)°¡_ŒX^úò äÕ†gó•êh=¥È@ÔwJ(–K…N)ðÉ“j´s”ÅðÁ)9îÙ@²‹yvVÀð É‘(„-òJ‘ÁéS–žz9+Ô«=ùŠ\ŽˆŽÝÈ™†^5|%gâZ@#hæÌ™aˆE(¤?eE ų¬0%¡×e²@Yõ^—Aî¥öÎEöÖÉýlÆ#lÏ7 ò•Öj¬?àÑïu䞌›|ìFŸÓˆÔž_Ô`—ÿõ»ýß>ç^ºÑzní¾åÛnp‹ÕzȦ¦ŸVÉ{8y[MÍ^…9˜—©ÊÆ­µá>yw?¶xo(†¡1Qc’Öì½}n¤Ÿ“É_•Vú>,òmîÚõ¶Ü?.vÔ‹¾w'™JHø/@/G U(¤»²@!¢ßàØ˜X)²Â&¤£”zò¤²èÇ$Yì k‘²ä±O>—°©zpÊÒ'…¨'O É\©Ä_ —¨nÃ3Õ(N‘W€(“(ãÙ6ŽÁ×`—M;裑}ꑇ/Ûì¨z!a¾l"«6ÈC±¨G^þôèP5©„²> fÑcU¶±cǶsɰ™;QeHñ¬,^I:E E Eàõ,¿ÓÄ'«Þë)¶}e_ûû“æÔ‘Õ¶çuöÖÍÚÔQµ6Ú{ õs@æU‹æ,m°½´Îþòâj{z¡ƒ8¼†lB⸿÷|êïíóÐM"†Êñ¯õh¡mýýªq°)ºä’K쪫®ò/S:? [JåRHñ,™ÄOHHHÈò;MԲꥈ÷nè…4ÀŸ<ú“,Œwà1æöc‰RRRR "ÐG€#);¥ëÉS/Û?…ò”ÕõØBO²…yäö­ÏÃÆ˜HŠWµéÌá,z¬öF#&e²Z&.e2îDÙ"â™-nI+E E Eàõ,¿ÓÄ%«Þë!¦iSRRRþ;"ÐG€#°60 R,p†)›@"ÊàÒfË{u+/yÙ£^y¥Ø„±äJ¹¿ÈP/K²ØVÚ‘=ÏvLWBÈÓ˜ É `¤8.g<›yꑇHuäv”zÊòª‡‡;¯À!PÒ‘¬À ¯ v(£'_È#+{žÍû£:xäÙä/:Hò-GŒD)))))))))))))½> ô3«`#oðÀ4$Q'=Ïæõ„™ÄàòèƒO` 9Êj>DYØI`´þ‘/èJVylIqðµ¡öàwHr¼C¡ÖJdµ©a9Eª Ø!|pLŽòØBV6<›ï…O¾ÑvÔ6v ø¨§Œ)`¶IÑ“ äÅW»²\ªæAH”"""""""""""""ÐÛèeàhÏÖý৘y¼‚¶ zðÕ!ó©› e‹p¾0 ÊäeSxíIN¸ ©0ÕQ†žÚmÃ×þx¶4¡T)IG; ;ðT/§UŽÛ¡ǵÈP&…‘*°ê„Œ@$í¤æ6B»lä%óà«Î³ùájÈ@ 8ÊÅ!ýMHHHHHHHHHHHHèÕô2p´›ï<øp DL| "eCÌ‚MyÉ À!U½gózj=ð RtÉ#Á‘ tT‡œüðlÈ“R/{ÂXÄ'-‹h¨\PC£°ÐÈAʱœdài§bø²A*ħL"%@êEDíAòKþŠÐQ=<Êè’BJÓPµ\<Òß^@ŽÀÀmÀ#( ¸©W|?ÔIì°I˜6ƒdƒ2rlÊcW؈ìQ–Ž0µI*¼…” Ytdí“BJs¥qª\RÀGä<íu8‡¼’³*+u‘@€<ì)ve;öO€ìºX´‚' H¤L^—%‹mHJ=Ž­”¦¤¤¤¤¤¤¤¤¤¤¤¤ôbz8ÚÝw]ø„€õ6îÖ`DYäØ xÊ#Q‡(DYú²)éºHÐÇ@†|þel!£:Ù¢^8ŽRguL«„uhTÇvp€'U½–NáŽÀ‡¤£„G£^ºÔ©,ßH ¨êT¯²W…zìÇrðpD¥¤¤¤¤TaÃlÀÛöµÆ§ž´–^¨T;ɧ¤¤¤¤¤¤´‹@/G{´:–Áø®@JYy0:ž u”á °A Br²… v µ†!0 >õÚâ2ytbyá/è S”vã6½Ø9¡P.á ÎâXLØò…#l‘§ùžýÇ@IDATd ØYÊØÕÎJWeêÅÈ’?¤Kª(+ä¥O¯"ø±ž|g¿°ÿ°o™©zǬª®.¯¿áñÇÌ֯ϗ+ÎôïoU#Gµ–—_®X=)Dà5ϪÍ6³êÉS¼#ãzÛð˜Ÿ?eR1½ª-·²êÑ£ÛYh~iµÌ™ÓŽÿšc h5;ìÜÞðøG÷ä×ÜžX±ã·É÷â5p=t)¯ý+ë¸û~˜ŸëUu­eå ³µkË K·ÿ•ÑjÍn»Ù¤üÐ^ù͵¶ò ËÐ(_d´ßËöÜ37?åÝwßmëÖ­+_9ƒä{ìacÆŒi§9oÞ<{üñÇÛñ#E E E E E E`ÓD —#>xÉ`3P ÆÀ‹¾pð ðd$§:g°‘@!dà dR=íÐuÚ¨Ã6vAÏ'aðÐdSu”å7õ²Kª:ø‚å’@ðb'äyˆ:h£eçb/†Çê)„M‚BvУNvÉ˵#yê(¢NÁ¥,HØÇelûÛgvšrÿVUC“9šsâ ÖüŸÿ¨Xq:ê[ß¶!o{›µø ñì°lõ»ììžn°eçŸï{©pn4]µÅ6ìÃö=mÉÉ4ÖDD 0žÖÀµÖ·hÈgب£ NÍzÛ>á8–ãa1½q—\j·ß¾úŠ{î±¥ÿïœvü×£fçmÒE?nÏ:â]f¯¼òZÛ…¼¿ÅŽ_¾re^ ×CWvýµ°¥Ž{ÕØqV»ÿ4í÷ò~#Gµ ¿Koº1Ìù pwÿÉnÑtÀ«ž:5üö ;à@[ï½_z‰m˜;×Z–,1[á€Wéàƒ¶ÿùŸÿ V>ô¡Ù ›¸GÓÕW_m[øïi!ýýï·O~ò“…ìTNHHHHHØDèeàˆÉ±!p^¾y¹Sˆó^ À0 päÐ!¬òÒEVsQY䨒‘ÙFV„<$Û"aCG¶©SÞ³'A_2ð:$)—Ô˜äÂ`'¨—=êb¢Nò±,<œ• t$+ˆzp]Êâ¡§¼À$ùŸ<º<Ú¤¬zÚ¿KCÕÆ\t‘ Úy7—£Yï<ÌlÙ2+J«ý…²¿øC fδ†;n·Q Ù÷í7kÚ~~:q>µ¥~Ó¦Ùæç}#0g|ÙêÕm^§¥bñŒC1ô¬³¬nÛmmÍ?þa«.¾8®êÑ|©ÉΜ(¦7ìsŸ³úè|¬2%˜IÀQgÑìùúbÇ/ö¢»ÏÏή‡¸í×bþµ²E»÷ZúÇ?uöuÏ$îÎߣböÅëç¿I›ó›þK«Ÿ^ÕlLgí³÷ÆBÆ\OGßøÆ7ìo|cÞÛñãLJ|Žò!I™‰@/G U?`+ôðÆ ©0 ä!?ðe+¶‡dÀ$°NA*",ºjƒ²ìz6¯/]êeKmÃÔV®ÔÉ_)u"ª‘e““¤”q '´S*˶R òi(‹r´CðA»’Ç®l‘‡(‘ÊÊäÑ—=RñÑ_s(u 8ª?á{ÒÉn6G³öÝ×½RXÄ-#­®¶ÍûÛð¥¹aþ|[pÌщ–•º¢"%âËnv寭ö o°5ÿzÂzj\Õ£ù¢/’exPŽÞäÿ½Ëª ²•ÐéìøuëùYÆõÐû߽ͽ†ö¯èqoŽ/²Å?û¹5>ö¨µøˆª¡CmÀ^{ÛøÏ|&ß³uÎÉ'[ó“ÿ.¿nû=*j=ǬÙ}›ôýï•hil°ªþü´š½£ÂúÓŸþä#ZŽ #“Ê)))))›6½ i¨;)àˆöÀCb¾êÛ¥)—0Žƒ±a5¤:lÉaxªG@GðlpRžTrØÐŽÈ¶Ðg#¯2À„žäàÅ~ÊpBGù.ÍqT³×^6é‚ïº9? |N¢ùï}OÈWú§îýï·ñg~:¨Íþȇ󓌦G•F2'_*ž±µn}1 W˜/ú"Y†rôpTF {I¤³ã×çg9×C/…¡[š}-í_©ãÎPµ–EÅ絋ÁšÅ¿¼ÂVÿüçEãÖ]¿GEÃôF“½gTumm™wö—Ðêg¿þõs lUC†XËK/•4SnEO÷8*ô+G…I垉@Ž ð^áäòP§Í³ˈë„y # ÛØ…à# Á>8u|€©=ù#[²+;’ JýA¹Rc8Ež a9®”ºBÛr\©‹äõnØAtT¯6àò§^AÂø „ôe“2ó¥§ô¯9‘l«&L´)×_”—ýþ6[æ]Ý+&ÿª<õŽ;ƒÚò»þ×^=÷ܼ‰nŽZà=™úÁÓK5øò”ÙÈCðÙÀ"¡/’MÙEWòäs;mí¨>ÖU{²+]ùBûÒ“,u’èTÐÞHVNôQÃ8£ÆáQ/RYõqûÊ£O[È’‡O™=å=ò€>PìeùK½t¡Œ-6ÙW]ªfµ>7…wg‡ø|D ¿»5ä+ù£ ]tfùd ¶fãE]Žúõ³þdçÕmµµÕø—à >iéj ±æ‰'¬á_ÿ¶æ'þYÜ]ÿr<è˜clØ!ï°'ZÓ«¯ÚÊûï³W]m-óçµ×©¯·z—‡Ö¦­ZeU>løñÇÛnQíõ¾º×êGm°uÅÏO:Šg,šéÅÜ_Vú½ýí6؇$ñ%¨kQsóÓO™9€2ò‹_´¡oßÏ–üúJ[uÉ%þ?°s¯÷";\ï÷б'Ÿjâ©íD»á÷¨Í˜á±™zÛïgÃÊ•6÷ÐwXÍ®»n²UÕ £_|ѦM›fÇ{ylA†M6ÉDz^,ŸP¬”pÇÕ(y9ƒ-ÊÈ@± ; gák“œ ]l"¯²R"nSà6!ê—|dä›êI©CŽüC¾u‰¦Üw¿Uù‹WGsO”j Í„®ßú–5ÜêàCD]Ž|8Á¤›n¶šáÃ#‹m³ëüÅláqiËôR•/I<ùÆ›òój ÌÿêW­ñî»Ú°«&M²)×þ&ðæøœAõû¾ÍWjo6Iülã€:Œ§¿Èõw¬ª:wŠL8眜ºv Šôkô¹Ú,aïóŒ¹à‚6“¡Çí3¿ÇÜÓO·æâüüØì—¿4^Ž;¢6qA0«^k#•GYŽ»eˆK Íö—ôlÆs…)Faø'ó}µN _®^ÓÒWlÞÑ®W¸Œ¹Ï÷4éºë;¼&ÖÏzÑ^òœD™Ïk Trü\¼Kç§ŽÒ¯Ée¼þº_‰rÈi§Ù¨|P^Mgí÷vÿ)ä·°8•µ¨VÐ^°ù“Ÿ†çý¿ÿg›Ïœ™o¼Å}™ý®wÙ¤ßüÆj¼§“hÁyçYÃí9@%ð*9îô˜‰ãÚŸ¾¿?2è™AlL»ò{´ÑJ‰\uM¹÷ÞüïÂâ_\fëŸ}Ö'Ê>ãPµªYØ1pôÑ~ÔŽ<òH{ï{ßÛÎÔ ÿªkKXÍ-":¯¸â £×O1ºð íKQ¹ÀÑ'>ñ û0+›:ãOÎñß–vØ¡ÙW|Èc=6^í*#E E E E E E ^ŽvwGÀ xàK å4æé•zð d„{HOu±ž‹åÁä¨C¢ ¿_>€]ˆ‡¬p¨×O2؆¯Ô³AGíPîP,—h˜ ã4гäIåŽ!ƒ]Ù&9í€d$¯:Éë£P„,rlȱ ðƒ ¤zøøƒ©øž å8•Œä©ëÒP5 Tçž °–>§ÃCÔtBþ0»ùÍ·X¿Ñ£­Ñ¿–ÎÿQ5çœnW€£Áþb&à†ð¥7Þ` ÿùÕŒoƒvÛՆ콭Ÿ5Ë^>åä¶ŽzO‚IéÅèÕßÞb«|^‹Ûncã>±qIâÂÜâœØ mccðž{Y}ëÃt dö3n„|gñô䩺§P«d™—¢Õ—]–¯ù¡‡ zN-òyFšýKò°á‡újz­4û¨÷µßëQç+ôª@„—¿÷ÝkkŸzÊø×ðaœuz,dÒ“#žVe<îYâ@ôdëç= †¿¬|à~æ½ä¸. Å—ÿÂV_ziÈW¤wÅå¶šž "6óž ïšýËÿŠ{þ®z?ôÐ0±oGÀ‘L‘vv^W|üüÒ•ó3ö-ä;»Z²^Y¯wšésâ ~@ð€ëa™ƒ.«ø³_Vë«KŽþЇCÅ£2÷¯Òöbà(8Xâ~óÁj3L¹®Û|“Þûo²ÿFÐ[šû©OÙ†Gÿž¯.–Éü{TÌXÞ€Ãße¾ô¥v5¯üæZ[é@LwR Ñkgˆ÷˜…{ÁG½çêž{î鿆Þ­·Þjßd¥·Vã?…9X Ýë€×}÷ÝgÛx/Ã|à­Rfïvàúeÿ*FY€£õë×[­ÿÖ@/ù/¾ôHyÂÆAtå(rØÓ——'¾ì)-(Ö#›dÐW/$íG—#ÉBñ„®sŽ?ÞšŸ}¦™®G¼gCÌ zu´øŠ=íÈ_r ç<Ú̇éåzþW¾b¼;¯ÖoÚ4Ûü¼o„rá$­Å^$ƒ /ýˆÕyñgœtc€$«Ÿy§Z3ÆÓƒ‘çžëgDߴ¼ €œBZqÛmÖôàƒ]³ûî¾’ÐB~÷(Zô)ÐüÅQ4À£ Þ;*\¥­þÄm쌓Bݺ瞵…þu:ÒVjΓ¬z¡¡Ö?•GYŽ{Ö¸Äü}ù'ÛZ?÷Âùè/ÌS|.•*?Va òŽb•è±BÕ|?ïE#¾öµü°@†C-9ûì0DNõã.¹Ô:hQpTÎy]ññs $ëù©}ˆÓN¯‡Vá¬×_Öë½z§mòZoñž3sŽ:ªý¤ÐÜ—|8£½Ò¶I–ý«´½j·'·ö8¢½ù~Þ4úýaªƒç¢pûu=ÜÏ¡áï8ÔVÿß#¶øŒ3BuÅǽT#&^}õo]~Ñ¥—ØšË/— ½šþøÇ¸;±ê‘¿Ù’³>ã÷z º‡bàHã½½.ºè"¿ý6ÙxÍ7Þz-óaª‡:ø 1ÿ|†©A »öÚÜQ”ßï‹QœuÖYdíw¿û}£HSê²GèA?ùÉOì׿þu˜Séðýr5fo÷áέ½(ÅKiŠ@Š@Š@Š@Š@ŠÀÆô2pÄG<Ѐ4nô*ŸË(ÜƒÇ "zÂJÐçå“”z¸„ÊÈSF}ð êHU–MÉ"‘²!+Ò3AŸöõ€†>$ý\©Ä_ —¨næQQlQá‹ä+×\m+ý_T 8Êä§Œ*-3ž'­d™Í|bÚZŸ ší=ÃZ|œBŠ—Ùïq n‘u>Lpêïo¢ e›íó^hØ•ô‹GYõd´5-8Êxܳƥ¢GÚòo»÷c.ü‘ ò¹ˆÂ\*ï8$ÔU¢‡‚–¯Új+›òË_á¼õÇBÀ´\ਬóºŽ_%çgرøO×CÖë/ëõ¾¹¿¨÷ršsòI¾´ü“±çåå+Ø¿JÛ‹£0Tò}¹¡QSÿü—¼o³}RèïWïsçŒ=õcaθşt0¹Ž{h¤ï€;óÒA›¢7O0Ü…?ÕoÙÁ&~ç;aÞ<™ÙààÍüÏ|&7”˜]H #z í`]ü°eŽ®ó9Ö¾ÿýïçMdÝ|óÍÁ/˜GqDè5•H™6èeàhOw,°G›€ðåÁ‘BðÐQY¶à£G"!+RÕÃWž” p ùNO>¡/LyÙ#_v<Û9¡\.Å ;!p†Æ!d!•s¥œ³´ÉΡ¯ žvRvI!ê°G™€Ó<ɳ6€?­¤ºXŸ<|Rñ±‰ ÙÆ§¿ùÖãÔfBW^ŠKLªÜà(îÄ6Ì›k‹üKÓ_+óîôŨúÍo¶É?ûy¨âû¥vC|9æ+®È«êÅFü"I[  ½£~€O"Ú²n}›ù@²ø™w 5Sn´kªÏ 5¯[ks|˜g*{ÿ2´GsÏ<Ã6<òHpqê_r½×úäØ/Ϙxu>ŸÍøÓÏÈGÝqÜÍ—·÷óŸÙÀíÞÚ`²÷}uhSÏUåÃçF½ßç‹hÉU¿¶U_q²ecàhÞ¼ya!†‚Åô¯Þö¶·ÖÞ{ï¢iӦ凭 çsPÅó½ï}/Ïbâìb«­eŽþé=ROóaâ…öþŸ÷H}ç;s÷³~þ<™0Í{œ2)))))ÿÝèeàh7.8XÁ¦~Ÿ°êà‘‡‰OŠ)=(CäEÔ‰$O~`WX|ÚÓ€Ô&yøè@ä!ÚYʪóli’Ri‰¶5ñŽªF;KÇq:v’6àáyÙ ¯ °ãr\;žlÇ>c‹:RñÑ‹e©ëï>I^~²©>÷VàŒž¢xB×—¾{­gå¨TpÔßWåšèÃ) 6óùCýŸÄ:^µKÍ,»ãv[îó÷´ø 1Å«÷ÄüRùYûOË÷‰_$ç|ìc¥Wk+4–ÁÏØD%ñŒõÊ~1zã,÷a{¯úð½bÄŠMS®üu¨zé‚ïØzÿÂŒbùòvªÅ€£¬z…ÆËŽ2wï¡Þi•Æ%€f{ó ]·¡Ÿý¬|O®·G1਽Í}%(Í£4ëíûú‡[O[*8*÷¼îŽãWöùÙv7ÚLßÙý%¨f¼þ²\ïL¾>å–ÜË|åºe¦VJ•\ïYÚ‹£Y>Á¿ÀoGs„ÜððCÁíBà¨ËÇÝÑÞSeðn»û¯\­üAnˆl¥qê)ùÿ0é?l×Ü|ŸÇªñÎ;Úñ+aÄÀÑÉ'Ÿlÿú׿ک MŸ>=ð}Öïï{_(n§P„qˆ¯ìÇÛ…”8bònæ6*¤Ïþóöž÷¼'°gÌHÀQa|R9E E E E E Ž@/Gô8Gà…Pk€§ žê£ÜùDð‘§Ž<õ€BÂ#ÄS[^•—S{’¡‚φ ôâŒ$¶/YRˆöÑ)›0^ ©94 €bHuð•'EW¢¬:ì¨,ð‡2 u ø)6¨—/ž =€ÚPŠ,y]òØ„T/÷Ø=ð'šÐµÉ¿„Îó!Mz1)ÖúHŸ{AsñÄ M,OVÚ8jªš2ÅFùWáÁ{í«…ü’«¯Ê}ngcØç>g#Þ{¸eòו<ÐN'Ïhi¶¥çžëgBîTˆ_$gûŽ–çŸË‹–“©Äϼ½ ã™×óLÙ/æC‡ÙÔ;r/@L⻬àëµlVMžbSZWèYxÑÂRÞñðµRý¶HÞ¶Ÿá-–UO¾(-8ÊtÜ Î—8šu„ñÕ† )î%T 8*[/êÕQ/—ñ¾êCƒ:šã¨Üóº;Ž_Ùçg¸.\•^Y®÷ªÍ7·)¿¹.xœ©WJ…û—¥½6ÀÑ>âs 8êêqöÅ/ÙˆÖaS}º§QtÎ 8zÕ‡ ®ñ…Ћê¡̑JÙÙ8bÕ´^x¡îÌ™3í€ÖžkŽ˜³hÿVÀ~ƒÿ>=þøãíôÄ`Ø ÎÚÂ] ptàÚêÕ«e>Ÿ^ì=°vÞyçP..å…S&E E E E Eàu>ñ‚ f@Ê(ClA8u¤q^¸…x™\,ÈÅöc|»`è]Hß³¡mÊ`%ø",ų°‘RWèåB›Î*M(”K8MÃÚ¤G°p[Ú)ÙE–|\öb°žêƒ(ÇÁ§E^öH‘ƒ$=RtØ RxòAm¢'YφúªÖfÂÚ“fX³¯¬ÕÅ/ô³:Ðg^^ÓN¼Î‡„ÿä§¿¸*}^:ŸÆ€5€oƒsÐèÆ@D=²è £2<òÈ’B²A*’2j¬‚2²ØÑ.õè‰eòЇÇ&yÏB>zÈQ†Èÿ5äzàO~BWokÖ¡ï0ŸT¡ÓVû|°Müê¹AnÑe—Úš_ü¢­ŽQŸò»Û‹Øê¿ÿÝŸžëyd>),s43'D4Au^9ú¿Ö'é|ÙW7 ä³N½ï> ¡aÕ£“}Õ£‡|›?þBV5vl +Ó‹dV?Ý™,ñŒ÷!zŠ ñ‹e~øÃy mɯ¯´U¾ÔrLU¾,ô”ssU…UÀÞy˜_l°¯J7úƒ ¢‹|ÂÛ5>*Ç7´ ¿ F4«8ʪ™ Ùr#&äÍrܳƥ§£±ÿÄêwÜ1ÄdžOVÞôÀý¹Pùù7ÎDMD 3^År–óº;Ž_%ç'~fººpýe‰ ~öùÏFä8²zw98^ ˆ ÑŸLûçú•¶×à(ëq¯šº±ÇÛ«·ÜlË}Âç>IÞC²Îçj[ÇŠd­ :ä£ë~c«½7™OÉûäá]¡¬ÀQ½ƒÁwß}·ÿŒå~Ǿè×ü}ü®Pmm­½Áç¦{ªDÏßîŽvÚi'»ð ^GÐ ¾ÀÓO?]àI*¦¤¤¤¤¤Äè#À‘\Š##0=È€=@’,!ðÁ+ òÒ¡NyÕ¦Ô#dž ôÉ 0’]xeòè!+܃¼Ê±Žò^]šd¼´ÄÆÉÒ0 ªñåD\pƒô ôÈÇvxŠ‚®6Ï€‡2¶± èÉ•6Ú¢BèI–¶¨ƒ‘—=ʹ§¸ZU-žÐuá…?´uþU¶ª=Ú¦xoÑŸ»¡áwú)ãç̈6ÁW@0qb¨žëÝó7<ú÷Áœ./ûÜ >h-¾TrX ~ð`é¼~@-¤âÞ' •˜ïl7>öh&Wåîj}›±¾j ÃÝ4÷ †²¼Hfõ3k<÷þéï¢NüÊWC‰^& ?}¦µ,]jæ/µ>?ƨ|Ðs´Ÿ‰~*ú ödïuTíuÐ+þ’´òW¿ ñ¬ÙfÛÜWèQ–¹Ÿþ´mø[¬ÞþM6ù’K‚^þ‰‹çž·±§Ÿnµ>÷T! 8ʪWhOÀËŠ¿õpkñžO…½×2÷Œqéià¨ÿ!ïðcý•|xøù5½,ëüXH%?: vPÎ a2îuÄÏçA0e²Ïÿuj¨Š‡#f9¯1ÒåãWîùé½*²Þ_2]­ÁË— îû¶ÙWXíÔ-Z­O„òÞc2ëþå­VÐ^©2G´—å¸÷›6Í6?ïyw;ËÄ÷¥Îd»³>Ž:²;­éÞ{;)«®+À ç‹4œæ9:£Î€£Býùóç·„bà¨PVå›}•ÍoûÛ*¦4E E E E E E ƒôàˆ—u°ˆ|ì Ú¯ˆ¼óp›ð¥/yÔÙ•ˆüKj:õÓÜò|Ëõ›¶¿8ê}6h§ÜJ.ùºÖÌrÿjý*K=û„ÁÅý;à=2Zçö‘ ½˜–\y¥­õ¹#Z-Ûªü+î”›nå²ç.ÉàgwÄ3ï´÷~é=ªÔûJü–Æ[ìÃÊb0.Ôyo­1þÕxÐλH4¤ôšXð?ç† ±ÛTPð6Fxo—a˜¯jô¹ª^rùæþÓ͘acNô‰Ò½§Áæ6eÕ“¾§®»>ß#-bÛjïA¶ø“ŸŒYù|¥Ç=(V—ê7¾Ñ&_zYP-5lsOâ>æ£Ç·:–U/4äÓŽýÞ÷­~‡òûÊdç‹Îýªµx³¸×G¼b[¦óšºáøa£³ó³K×C†ëOÁË0Àû0µ0”³à¾¶~öl[åKÞ¯¼è"«óeÕÇŸùé •õþ”Ël¯Ú{ÕL¾üŠv×£æóŠïmZÑrÅ}÷ÚÒ³ÏÎíY†ã^³û6éûßWd:LÛÝ':”îþJVª«›6ÍWvràoj¾Ó/²—¿ýkzÄ¿Å4òLÔuÚwß}ó` =wl élû»|%º5¾p+šÑ»(¦£>ÚNñ{É Aƒbv˜œú6rwë­·†Bm*[ Ô3¦]Õü¾qüñÇçùGóæÍ³Ÿúïó=÷Üã£Æy$J”""""""ÐYz8ÚÍý–Á8”y!§,ul†ô£Ë GB6¶:p lB±¼ôå6È« äñEõ¤´!IEèvJ±BgÂjTN)r‚T; £,^Ï£+}Ï’]äȳ³v€I޼@!l‘WŠ ~IŸ²ôÔ»ÈY¡^íÉWä69pÄWÚ†“ùÃtÓƒâK6âåÊç&ªžº…Uy÷ú Ï=ë]\|hUÁƒr;ã>ñªÕÕY•¿Ü›ç¦æa¸[gD›>akàQCƒµ0/“¶vÖfgv‹Õ—ég·Å3öýôaxU>­…U€–/ïx½ ÇÂ6ø©Çä¯Ä¤3ªõc0z”µÐ» ûåRV½rí“ËzܳĥXû›’W?ȪF ·ŽAAO»MÒlw¿ÎÏn»ʼþº=F´ë÷&îOáÚã½`wÛþÉñNÚ“X—Óî8î]vbÓÈÏqô›km¥ë‹ÔÏÁ<†§5ÊÖ­[gK–,1†›uGGy¤ÿ¼®õŸÚ¿½¬òQåeüÎöÅ %ŸRRRRRz1½ 1TM` ?äo ™ßÀÀ$À"HÚP–ŽRêÉ“BÈ¢“dÑg«²äá‘Á%lªœƒ²ôI!êÉ“Bò#W*ñWÂ%ªÛ°q¬Ð¨Å)ò eò¤"µ… øÚì*¤€>ÚÙ]òðe ”lÀ— Rd©“žgƒ ‹á"äc|?…µx6ì©\2•c%¢Šb²j\αCäEä‘!U0ì #=ò²/}R¢ëÉS/Û?…ò”ÕõØBO²…yäö-QŠ@Š@Š@Š@Š@Š@Š@ÅHÀQÅ!K )))))F G` l`¤Xà )v”zÊòª‡‡;¯âi‹@IG²ƒ¼*Ø¡Œž|!¬ìy6ïêà‘g“¿è É·y0¥¤¤¤¤¤¤¤¤¤¤¤¤ôvúp$Ð̬‚|¼ÁÓDô<›×fƒ?Ê£>-ä(«Møea'ÑúG¾ +Yå±%=ÄÁCÔ†Úƒß!Éñ…Z+‘Õ¦†å©v€`‡ðÁ18Êc YÙðl¾<ùF[ØQÛØà ¢ž2z¤€AØ&EO6_íÊri¨š!QŠ@Š@Š@Š@Š@Š@Š@Š@Š@Š@Š@Š@Š@oG —£=[÷ ƒžF`ä!ð>Ø‚êÁT‡|̧l‚” ,BÀ|ûÂ0(“—Má´'9á.¤Â8TG>zj7¶ _ûãÙÒ„R¥$í€@"ìÀS½œV9n‡:× C™DªÀªW2‘´“šÛ]첑—\̃¯:Ïæ‡«!%à(‡ô7E E E E E E E E E E E E E W#ÐËÀÑn¾óàÂ%1ñ5<ˆ” 9@0 6å%'‡TõžÍë©ôÀ'HÑ%<D6ÐQròó!OJ½ì cŸ´,¢¡rI@ B8À@#)Çr’§ŠeàË©üŸ2yˆ”©u´ pÉ/ùC*BGõð(£K )MCÕrñHSRRRRRRRRRRRRz5}8G;´ ,à>¤^EðüP'=° À&aØ@’ Êȱ)]a#²GY:ÂPÔ&©ðR6dÑ‘Mx´O )Í•JüÅ©rIC=ð´CÔáòrHΪ¬ÔEò°#¤Ø•íØ?B²ëbyÐ ž€"’2yQ\–,z´! )õ8R´Rš"""""""""""""Ћèeàhwßuá^ÔÛH¸X€ed‘cƒà) DB eé˦d¤ë"A=òMø |x”±…Œêd‹zá8JÕ1a¬B>Ö¡Q9ÛÁvžTõrX:…;’Žv~<zéR§²|#% ªS½Ê^ê±ËÁOÀQH”"""""""""""""ÐËèeàhÖÝË`|W ¥¬<„φ:ÊðØ‘œl!ƒHm€aL‚O½¶¸LX^ø úÂTÄ#¥Ý¸M/vN(”K8ƒ³86„|á„Cä©G(v–2vµ³ÒU™zñòÀƒä)À’€*Ê yéÓ«~¬'ßÙ/ì?ì[¢^Ž@/GLŽ ®!G)Ãzß„;€_€GÜANuž  2"BÈÀȤzôiƒ:mÔa» ç“°xè@²©:Êò›zÙ%Uü ÁrI ø±rˆvc›^,N4R.©1É „ÁQ/{rH²ÔI>–…‡³²¼dQî KY<ô”˜$à“gC‚G›”UO{â§¡jŒD)))))))))))))½^Žª~ÀV w€aC†T˜òÀø²ÛÃ2`È@ا ‘G]µAYv=›×—.õ²¥¶áAj+Wê䯔: ÕȲÉIRÊz´S*˶R òi(‹Ôã]H ;'yêdKr”ŒHåeèÿ³w`²&eùð_AI‚ ä¼ | (AaÙ•$,‚€ A²äQÀ„¢ˆòW`%#A‚$ÉIA² *,®(A$IPAÉIa÷«ß;çî­ééžéî™szΞ繮šzêIUuw˜é{ê};ñòSO»œØs¥"Ž(%…@!P…@!P…@!P…ÀºX3q”KÕÀ∎ûp*_AÂ…èCÐÄÆzœDNá1’“z!›ä†ç‡ÃÐĉ!ëË=~ãˆùÕÇôöø·ôŠ,*Š[`_8ŧVÌ¿„N€hê„pŠ®OœÙHê¨%_£§çD‘¼Ä±õ뚎 á$'zÝã¨QR…@!P…@!P…@!P¬}@…0 ‚ð _‹`‚’‡/­©#—ÑûÂyˆ ‘¤¶º„] ¢>žƒd l$óe=©•º©“Ø1i§’—‘LfQt›0qžžoºvž¾…Lò76('þÌÁ>mÏ‘BÖÁ ’ŸšÆ¤·'/ý»6Bêg!P…@!P…@!P…@!P¬5G.U Og@î np !‹Øø~B¼±F'ì."‘üHj¦®ÜÄÓÙI_'þ>7ómDo¬AnÖbþä%–oGÉv l!o› „ôÉÄ“ÉÙø#ÇßÏ]¾¹ÄÒÙõò¢7uÔs’¨_O|lüÉÉX--õ3G]ªÖ@)) B ( B ( B (ÖÀ> Žp¸‰=¸ºÆG1ôBô<¸ˆèréê‰ÑØôê…K‰O1–§–ÆnL’OW#ü‹-5é}l..— í7HÏbÔ2C²0¶Ll}K\H®š6—qúÑÏò(ûáŸöœ*b‹_ÏÛ;›^R…@!P…@!P…@!PkF`ÍÄÑÕÛöñHœ†9ÓÛŒ ?þBLHšäÅ×çµ° ù#ŽO1&òp±g xŒØÄ†÷àOëóÔfOßÔ1'óo+kŠ›Ôbéú,ÂâŨ›ÚúØÅe‰I||-d’/Q$Vœ&NsCkB(~vë§½©ã¸ï“x¾ºT %…@!P…@!P…@!P…ÀšX3qt¶}œƒ†?8µ5C8‘ôx’qú;òérÃgˆ1fרõæÑ“Ì%o‘ud¾>.µÒË—qtc’qjlXçüLÒ÷³xd3Æ6`#l™¼©=öäésúHn*N½´~ÜÌcœx"?õ¢Ç×çÑÙÕ$‰A:ÉÏ)¤ì£ˆ#(•…@!P…@!P…@!PkF`ÍÄ‘{…àqZ$:.cº‰aCòà•ÈÁGèÃ4uä%2§’ˆ¯‘qj&V<ÑkbSƒ.g"ßüjù$ù£9?<ǽÉlÒH_¼_pˆ™lN\§Ï&bO¾è™'›ä3‡žôþŒ³†œ>R 8$Ä’>µb—§%¶ˆ#È”…@!P…@!P…@!PkF`ÍÄÑqmû¸d Ž#-Ä~!:^!d‘ž°ÉÉ8µØå…¡±ÉÑÇÏ]¯ÉÁcdMx¶¬I~8ñ©GgO¦î,’•~rúE„œ19K2Þm,Öœ6'?-›L]=áSÏàæb HM/[Ó“øú|:»>v5ÕHmkª›c7J B ( B ( B ( u#°fâ蘶<®€à#4œþ‚]/÷ÁÇFg'±ëåé5'ÂMäS3>=IŒø¬CÝp!ìæ ÿ’9›i´Ë!Y¿ùbk_SçK’æGlöô'›5¶p‹6yjëÙ,ŒžtñÄÆ³ðlºƒ-µSWžZ|úØåõ±|N"YSâ³½ÿÉM/) B ( B ( B ( 5#°fâȉ#<®)Dp lilñ‰ãÇ;¸R„]<_x¤PøˆØ2WsMâ2_bø»¦†¼¾Ç‘ôõ«'Ö'gaQ|É!rLÌf¤'€âc®—€ŒãS'ã?ÆH>;Ñ«ÁŸµ4u¢ÑË^ƒñtÍfš/‹6qZò€eÑjeS©+–ÞÛp¬ò‡O¡÷à°e£âSO/Ž‹$O/G#z¶¬A¯ÉKlSG[]ª‰’B ( B ( B ( B`ÍìâÇìÁA„÷È8DŸ{S'䎜CxˆŒõ„ ùÓ C¯®üHìÆ} u̯Ï$SÈ©fO#ñO·Ä¨q’À’B ( B ( B ( B X/û€8BÆàðtÎ!-—„ñ~±rÄdÌFÑÔI q‘ĉͼ¸ cM¬óòË‹]É:Ùˆ¶¬¯Ÿ›¦¤ðLç”QabByÆÑ-4‹kê–˜,XŒøÌˉžúÙ»FØÔ@ø˜ŸÝ˜H>›–ø¦Ž"†]ž8cB¯oU¡¨…@!P…@!P…@!P…ÀzØ'ÄQ@艣\"¢G î!Rbñ ìø BO_ôø¦{~qšòé!ŒR—Óå‰ ïAϸωÞÜó%ÅçGœîI¬‰M˜ bÏ"úqˆUäyô¾ŽDìrÓš:<Æj«EBôlŒN¯eÜBòk.>6BO=㜆ªKÕ QR…@!P…@!P…@!P¬5GǶíã0!‚ðZŒõ$„ C›÷„“x1©£06Wx\Fækêè׳…S1–—ãˆùÅ‘¬qVÜFÄœŸ›0‘‘L†œaËBù29c±giM0ÔJ®>âϺ2GˆŸ~£ÉEú0}Ÿ+&5ÎÚôÌ›5‘WEmàQ? B ( B ( B ( µ"°ˆ#cÜD c„~‚/—¸¥ž<ºØp+©›±^.áKnøcÂ×s,j'—ŽsI>ûŽ’Â;¶€9cB¢F_¥Ïâ³Éô|bB&¥VìúÄ"w€¢¨©£ð'_P„_Ër“ßÔQRWÝfCì`#‰£‡R‹ž^Œu%ß8y9]ÔL£?óe­âŠ8‚NI!P…@!P…@!P…@!°fÖL¹T-d "&äMH!èœÚn'‹Ð‡´1NNz~ºžˆ•ßKbåk¸Šäˆ¥³Ñ{rIÍøñÆÉ×~ºžd£9?<ǽɦ7fR‹¢ czH™¦nZ{6 n€Ð#}²‘Ôݦèì©Í–SHdO ½X9Ékê/_|ÖS—ª50J B ( B ( B ( u#°ˆ#¼~B£ã9i£Ç9Љ8¢G?NB< ùÄÖ×Q_ {j%§™6ñ,Y >ƒnžHrÔàÏ:Äh'®™æK_x~Ô†§ß¤ârوŤVâŒÙ£[°qâÙ-˜è›¼¾7oòC‰'‰ëk‰7Öè!Šš:ÚR+SG)) B ( B ( B (ÖŒÀ>!ŽBò@rÇ ±‘GÑñ D|8œP W¢nƒ¤Éþ"ö¨5ÈU'¼½çW¬3\KSGI½ŒçöYØÜ€Î„:Ï@IDAT1+6“gq6DÐÅèfˆ1É£§~òõÉf{??µ?ÓñÆ™‹_-y‰ÖÅŸÔZI!P…@!P…@!P…@!P¬}Bá4œ†žà"3z6½’AdŒwHn8‹>¾¹'\ {âS?zzuH8–ÑÆO1ü!±«vt1æI½¦n/‚—ñ&ËzD ÀôžÅ4u_<Ñ#u²xz6í$ÃïÞDtskê:»x¹âÄÇŸuèµÔ®cn9rëÄQ¡¤( B ( B ( B X7û€8ÂàÒŒ¿Õaš8Ê8üŽA ×A/—§… ÛH¦Ž\FêÈÅmÈ—ºMÅØúˆ8ñiìêèù¦s›i¾¤èüˆÓ=‰ÍFõ6™ÍgÃúÄdQ‹µH}Èš¦NNÑ“#ŽÈ‰_ßÏ•z‰åSCŸœ¦Ž6}O$ÉS7džLI!P…@!P…@!P…@!°föq¾#¤ Ò&:‚‡„„Kôiø qø¶äÒùÒš:Ê,?‡xÜFH¥?ꪑž=5åd^vcÂFÄ-$IX$8±Y”q&¢Çß/”ߘDŸMŽÍgC@%'±!ƒšk¬c,/k¡‹M½¦NÖ]Ëzå ²¶"Ž%…@!P…@!P…@!P…ÀºØÄQHœ®B£÷ §"‰/yMä…3éÉŸèòñj‰3ΜìÄ8ÜÉh8ð#k‘›Øèj%O8>$sd>öm% ß6è€SlZ&΢ôÙ€±€>'ºZbS£©“SHlY›¹ÔÉÜêö@üÆòôÈ µõòRC|ì™75ÄÕ¥j „’B ( B ( B ( B`ݬ™8:îÀþqNá èßAØq ñãâßÛùpz ∨Øžšá3Ì—¸ð.úpñ³Ë˼}möì§©óEÒ²’œl $‘:lñgÑ÷óðYx6 ÆXÏFô6§‚Ä„DÊ&so#¹êjôÄõ6öøš:¹\M )âh‡úY…@!P…@!P…@!P¬5GÇ´ÍãÂK„$rãk6¢×Ä!apZôÄ…ÀÑÇßÔI^摇ŸÐË¥‹'lxÔŸ¸¬£©£®çO½p,±ë-*!jLJ,@CÐdÆ}\bز©>†=5ôYOìÆt¢PNñ™qD²®¬G‘?›±\=I_—ªmàQ? B ( B ( B ( µ"°ˆ#<îiƒ0qÃNrªˆ=Ä_òpȦpjˆ#©a,N‹®n¸‘Ô3NN8”Ì©ߢ×ÄÊIM6óëIúÑœŸµ¨0ñòL°eC|'> Êb3NßBFAò؈^ÝÔî×B(u[Ø„´b Q é‘~œXyæ‘T'Ž‚Võ…@!P…@!P…@!P…ÀX3qtõ¶õð!^rÚ(¼®ad,VœFØ¢‹!ÆxˆBÆÉOÍÄ$·…Œyx yD¬-ü ;›±ZbâK-þð8é›i{Qlßç˜4 îëX€ÍëãÏ‚“3½v’œl’½¿?¹|gmz€ÆÆÍ5úÕïãØ‹8‚BI!P…@!P…@!P…@!°fÖL{`û¸ ù‚WÐGÇA„Ðiêè3fa>$q©%F’9p!“ØùÓú1]NþE~8•ØôæíçlÃE¢b1ka½¨æËB4bAt~9bH¿Xcu³ÙäfÌ[H6’õèK!ªŒ=ùN±÷yY»}©Rk%…@!P…@!P…@!P…@!°fÖL¹96^#$Oz÷Z ÀG„Ü_SGRIL$¤öLñË7_ŸúwáäS¸69$5ã3κùSWû¶"pQÉâ‘/Ä&² :á³°6Ù¨Íõ1m8nÜø€§'j…Oy|©KÏz2OâùÄ#Šø®qbB"©¯Ž±Ú'·VR…@!P…@!P…@!PkF`ÍÄ‘›c¼Ž!äP¯ó#~ÂaàB ÑÅʧã#ŒÅæžGÆìüâ´ÔKÔOR÷AÄdŽÔ§nÄX¾º}Í6œ-&YT2YâC¨a2þÔ˂˗ø>–ÍbSC|bCñäŽ\ãØäE™”5°Ó5¹„ÍœÆñ›/öºT­QR…@!P…@!P…@!P¬5G.UÃhÓ¤QxŒ&FÎB< ùÞZ}=uÄà$Äuðú]¬ÜÌaœºMä'—?µ27É\£~&i‡°Ñ-VË"õÆ!z²©ŒS;} ãCÒGrâG. ©cs‰çK­Ä#ŒôY1I¼üÔÓÇ.'öÜC©ˆ£JI!P…@!P…@!P…@!°nÖLåR50„8¢ã>œ ÂWp!ú4±ÅŸ±'‘SExŒä¤^È&¹á9äá04qbHÆúIJGß8b~õð!½=þ-½"‹ŠâØÎDñ©•³Å/¡ š:!œ¢ë§F6’:jÉ×èiÆ9Q$/qlýº¦cB8ɉ^÷8j`”ì+\á Ã¥.u©áÔSOÞô¦7í¾`U8¤ì÷Ç﻾뻆Ë_þòc»â¯8|ñ‹_žô¤'RŒ·ÉÎw¾ó Ççwÿ0¼ùÍo¾ùÍoÔ-{ì±ÃùÏþ-sœrÊ)Ã{Þóž-ö2nt£ w¸Ã†·¿ýíóŸýìC3é³\ö²—îw¿û ï{ßû†~ðƒãsäßøÆ™R…@!Pœ±ØÄQ£ð Ÿð¸v1á!è!yøÒš:r½/œ‡˜Ij«KØÅð êã9øHÖÀF2_Ö“Z©›:‰“vú!yÉdE· gáéù¦kgáé[È$qcƒrâÏìÓ6ñü)dì"ù©iLz{òÒ¿k#d±Ÿ>\žp ›‚¿ýíoÿõ_ÿ5|ö³ŸÞýîwúЇ6ùkpd ð‡™Øb29$ãøûù£Ë7—X:»±^^ô¦ŽúÆ_€§ƒ’ù²^þäÈ7VKKý̱ԥjý‘Vk¦|âŸîsŸû _þò—gú˸·<ìa?¬}øÃèë’ýJ<¬ ÃmÞýøøéLg~çw~g¸ño¼ Î~ô£Ãë_ÿúñæ&Gì—×ÃôºÖ1î߯q„p*,r¡ ]hTψÄÑáò&[ß’«¦Íeœ>@ôs†¾2É—‡(+N§¹¡µ)'ˆâ³º>yMÇ}Ÿ˜Äó-u©ZÿAäw÷w7ÝËÆÿøÿxü  ðSŸúÔá/xµä "°_>Àì7âá B~†,½ß?—¡ýüÏÿüë<àÃßþíßNÆó”ýòz˜·¾Ciï߯‹8Ú[äëy¶·xªvŽsœcxÑ‹^4䤚Kào}ë[ÿû¿þ‘YR…@!P9¬™8ºFCç áNm ÇN$=~‚dœ>Ä|ºÜðbŒÙ5v½yô$s‰Å[d™¯K­ôòÅeݘdœÖ9?“4ǽÅ,ÞÙŒ± Ø[&oêD=yúœ>’›…ŠS/­7ó'žÈO½èñõytv5IbNòs )ûØ3âÈd×¾öµ‡G?úÑÔá[ßúÖxZ`»{ݸÉi§YÖòâ¿”ÛÕ^¾âÖ ë#Ók\tîEã¶ÎÜÀ9sOÇîŘݬ3ë9TÄÃ*k]öy6ûæ^5/7÷¢5—yüvÂaÑ9çÅ]ð‚^ùÊWN^>L:i´ˆìÅëa‘yæÅ¬‚Í*9óæïíÛG«<_–]ç^œ8ZvÎ~ÿSß‹ç™Ç`úwË¢k>Ô¸ªù.~ñ‹/}éKó÷@‡+‘ƒÐ‡iêÈKd,! '$Ÿ?2NÍĆ@ÐkbSƒ.g"ßüjù$ù£9?<ǽɼñ—ˆ©/Þ/8ÄL6'.‹Óg±§_ôÌ“Mò™COzÆYCN©bIŸZ±ËÓ»§Ä‘IÿøÇO¾Égú~ î·pË[ÞrôÿÈüÈx:ÉýJŸwCL—¸m'ÇsÌð“?ù“Ãþè¾ð…Ç{5¼÷½ï>ð êüÃ?üÃH&ñ;šXzâŸ8¸éæ´¸”îb»Øx£P¨uÔQÃmns›ñò:RÍãþ þ°ìc;žvðGíoýÖo Öî+ö:ý-0Öu—»Üepƒhß0ä²÷øpãØ÷¿ÿý›–႟ù™ŸmOúÓÇØë^÷ºã}orÝŸBîõŒn.¶€ÔÔñ²5=‰¯Ï§³ëcWSÔ¶¦•oŽ=}©Z«5 ²È mÉ 'œ0üË¿ü˨ÿÀüÀàáå.w¹q<ý¹ó{¿÷{Ã[Þò–i×ø‡©ûøZáíä'~â'Æ´÷º×½†{Üãc¨?BgÝåOþäOÆÁŸüä'Çû1!šN<ñÄ1ç OxÂð+¿ò+“©| ñ‡øsŸûÜÉMF9‘=l/rcLÏ_þ’—¼dâêÿ@viÎÍo~óáV·ºÕÄÅW›ß> w¼ã'sÜûÞ÷Ã|x}Æ3ž‘”Iïk°§¿ {Ùu¦Áæ>Ln'{Eù@²ÌãnMË>ÏV}Ü}3ÑnŸ/NÖ …ÎyÎsÎ…q”çÄ*Ï—¾ð*ß¹Ï}îáå/ù¶kt—”ì…˜ï oxÃX ébï_ùÊWæ–^õõà9Š&0dÊ,qб„(tÉ ‚èž÷¼çä~b·»Ýí†ßþíß®t¥+mI÷a×ëvúCõª¯¿-ì`XôùòÕ¯~uüÖµÏþó›*îv‹G÷½ï}w…ç¦EïÑ ë/ÿò/ðñ˜ïæ}wÙ÷%ÛX—ÿñùÈGnAÂëÉûÝN²ÌûîqÇ7<ô¡/1ó»aÖïÚæ›å÷7„¿%ÈôïÍéx¿sû¿+¼Õ½‘¦Qªq!P…Àá„€¿ÁüÃçP]®íïŸN9å09 €GÀ|„æ.þ‚]/÷ÁÇFg'±ëåé5'òA9'˜šiôéIbÄgêâ12§A2']¡óÅ&Ö8¾¦Î—$ÍØìé7O6klCÝ/ÒlFO ºxbãYx6݃Á–ÚýšÕâÓÇ.¯åûžÖ¬)ñY‡^‹ÿä¦/,ý‘yÄÑþàÏyÎsÆš‰ñdtªÇB‰(ÆþȼÎu®3žÎíÇ 'œN6±ùàæG_ÉM8îurÒI'/&ßâãLìîq4N4ç‡ùýç˜üÍßüÍð¿ñ£îƒ%RÈ‹Žüõ_ÿõð¶·½m¼i¸ÓM‘[ÜâÃþçŽÃOX䯠ŸûÜçÆSXþ(÷AšøðêÃÀyÎsžá/þâ/FÛ"?^õªW zÔ£&¡«¬S²=?å)O®|å+µàघ“^¸ÀÆ"Ùû^G«<î«<ÏzâhÒ eúqw,ÄÑŒð‰i:/ÏÏW'ÉB¹‘üÉ'Ÿ<¾aûÏ9 ‘"óˆ£EŸ/YȪ_ÿù¦7½i<ƒôò\ЬÑ=töB¼>…d‘KÔV}=x®85’û© `ß÷¾÷mÙ‚ýýÑýÑh÷úö^Ô wIîYÏzÖѯí«\å*“:H·Ç=îq“ñª¯¿I%”eÞ_ÜxüxĤú^¬sâhY<' Þ#Åó‰J„Q^›ˆEÿHXõ}w•÷%ÛYõyæ¥S¯ÿpñ~ºq´ìû®ßQytÂ×kv/$ï^ŸÄi[ò¼Ëú¼;Aò9©XR…@!P®¬™8r‫@ \[[|âøñî‡aÏGçG …ˆ-s5×$.ó%†°kjÈë{Ðûú‰ÕóËYX_F2Aˆ³Ùé  øØ£ëå ãøÔÉ8ä1R‡ÏƒÁNôjðg-MO s¤K'réj’øØÞ9ZüÑ )4Šrš‡ œ‚éO!ýë¿þëxÓÛþ¯þF¸¾Ì%ù#ÑøýG“øÃÔI"È÷âö%/yÉáãÿø˜·ÛGj»$åÏÿüχ׽îu“©°°þ ?ñÄÇKµ>øÁŽëñǶ?róAÃ%*ˆ±ˆKšîÿûÃ×¾öµƒ¯­&=ž£¡ýpÉžKåœÀò¡V]<œp¹ímo;dyÌc&'Ž\–™õ9>è#Ȫ딋ðp™!Ã/þâ/nº¼e™{äŒEvø±Êã¾ÊóìøãŸ|ð±¤Ew<"‹æyþz~$ñ:¾¤ËsÊ~d{!ˆ™àâÐôk}zܪ¯‡ž4ôAÓÎiqÏ6DÉI†þ}âŸö´§§Ç¼G¹´Æ)¤rÜŒvóúK­eúeÞ_\nzãßx,¿Wë\…8ÊþÁ3±{Ñ{OFê"ñLó ¢«>ÏVy_òûoÕçÙ4¿'!Ž–}ßýþïÿþÁë¯{ÝëN¦Ý+Éï>¹ÙÍn6þÃi2I§ •¬øÝä 5”…@!P‡+û€8ò‡=Î@¯á 2tÜB"¿pùô½Þ"¶L-lŒëë÷ü†º8 y¸‹ä7uœÛWb-ür#Ñõ|Ók0ž®™Ü™½„EÅ¢Mœ–<`Y´ZÙTꊥ÷ã6kÈ‹O1îÁaËFŧž^Iž^ŽFôlYC攗ئŽþ=¿T yògögêù—9ü¿ÿ÷ÿ§^œP ³þ8ýA¥ÿ€Úÿ7ÑI„·¿ýící~ì–8r²â§ú§Ç)úorr¯ò‘@È \âboöH|°ö7ÄÛyÏ{Þ å´•?‚Éô;§”ô mÊE¾9Å5ïþüqp»Óî4Ö÷cÕu^â— -uZ7¹ÉM¶\’°×ÄÑ*û*Ï3Dg ewÿU_%ÏsÃéÝ‹x> ä¦ïõq´èóe7‚̽•øyÏ{ÞAýPÖU!\ÆÉü±ÌëÁ),¤ª×'9á„ͧ]^ä^fĽÔò-oÓèåy<Åz?ssï¼çy/A@¬úúSsYæýEýÜWj¯Ö¹*q´(ž«`2³a„Ät¯ªY¤è2ϳUÞ—üYõy6½Çeˆ£UÞwÍç}Ôz]&Ù-䔈Üýîwï§–ÚÓ½×°ûÅ9ñ×ÿq!P…@!p8 °ˆ#²Þ#ãI|ZìM;rBá"2Ö6äO/|x ½ºò#±÷5Ô1øŽäÊ'±Ó“G_H$/#Y$ÂÅL˜¤¿Íé [ã,>vcµ‰:ôŒùR;óªÅ&_»žM|H¦SÍ4žFâŸn‰Qã$‹JÿAdÞ‰£þCªS7þhuIqÏÿŸ%ý üà7mué“›·—0\ïz×[èÂÝG{ØÃÆÓF>¾ãïçϽ ò}ˆ#ÿmÍeˆ€ßÿýßsòC÷wŠäR®Oÿ)EüØg/þËóæƒ]ï_æ̪ëtJ¦Äã™›5÷ëØKâh•ÇÝ©³Užg>d„ZæqeòGæË½Fæ‘¡;GË<_vóøõ§å<Þ¾"ÛÍ”½.·»÷PÿÜXT÷!þïþîïÆp¯Ï×ee™×ƒÚýižâëOBþê¯þêxy,{^ÿt—·¹‰þô ‡þ¿R«¾þ̳Š,ûþâ†÷^{µÎUˆ£eð\“äÌ#Œ<¿CM?¦ÉÕ/ú<[õ}ÉëkÕçY¿Nú¢ÄÑ*ï»ÓsùýoÝHßȪ’ûƒ¹d<ð/ýNÍê B ( 32û€8BÆàðtÎ! éƒcà#übåˆÉ˜.VORCIœ˜Ì‹«0ÖĪ1/¿¼ØÅ±‘ÌÁFİe}ýÜü3%…g:§Œ Ê3Žnl¡Y\S·ÄdÁbÄgî,XNôÔφØ5¦ÂÇüìÆô@òÙ´Ä7u1ìòÄúž«ÚýÐM¾¹Ådßt–Ó8îáòK¿ôKãäÓ?\žâv’Kܼh|H%ï|ç;'—{†m~ì–8ò_ÓSO=u<9âÈåe.3#ùƒ>đˌòíhÛ,kâòM5n¸Ú°³æþçžÄDABüñã0ìâÓ/úFìªëtéœoý".—sÙÜ´ì%q´ÊãÞŸ’Xæy椖ç)YæqeòG.Ìý¬<¶ÿó?ÿ3 çxÿ££ž©Ú2Ï—Ý<~.s¹V.ëŠäñ F¹~ï[Ewb ' ‘R;Ý Ö˼äû`ï’Ô<¯k§‘ÜÓ†ÝZœ² ‘׿|„·{MKià 'lœdZõõ7]{Ññªï/{µÎUˆ£eð\‡>na䤊ß?Nñåqîó¦õEŸg«¾/9Ý·êólz­‹G«¼ïNÏ•±KÇ­?—y²/K õ÷;û½ßû½ñFS¿úB ( BàŒŒÀ>!ŽqOå±=bp!‹g`ÇWzrø¢Ç7Ýó‹ÓÔOa”ºlĘ.OlxzÆ}Nôæž/)>?âtObMlÂL{Ñ4ìD‘Gïë8AÄ.7­©#Ác¬†Z$DÏÆèôZÆbÔMIJñ±zꛟìù¥j.Ëð•õÄei>týéŸþé8vÙWîE0º¾âÞ}bˆÓø‡8~S‘o.#>Ü9Ù±ˆ,BYÓÅ.v±!'‰úûäDPâȱùw½kƒgËô!ŽJNC—5L‹Y¿fÿÑ÷¡Yаssa÷hš¤FN§ì–8Zu.=Ìý›¬ÅesÓÒ\Áo:fѱo¨Zöq÷X®òÀŒþìcÚßW]g»WÕd g9ËY¶¬¡}8šøáüú¹—ÑWyÜW}ž­ú¸¯š×H‘ VíäÑ,áÔnd;‰i¤Ï$fÕçË^>~½èEOóüoÏdóvYÉdË<ÖÓ±í„á¤î¬×îtüôx™×Cr=&ä˜Ì{™Ë\æ´öMj“qûƦM{Ëëß¾Ûå=›|j¶“S›ê©Ï¾êë/ë\¶_õù²Wë ¦xÛ‚Q¿—Uñìk,£{Ì)qZ»´uò{,Û ëO»ô¥/½íZ3ϢϳUߗ̳W¸4âhÜç[Þò–m÷¶Êûnðè{{n_"±å=Âó ‘HÛ®¡¯Ó.ÿž<>í¸…óú¥/þ÷]aUXÕs žõØ?ÏFv¾óï´öíÚ‡¤ùÌîóÜçÀÕ[ïRÿÕ®Ôš¯ÕÆ'\þ@ó_!¼ÃÑÚ%[ŸÀKà*ðj ŸqÁ=ÇßÀmÐñøs¶†ÑŸ½µð'éñ&šÃ/áUÂÁè5Æ8ÜM¤ÄÄ>&l÷cáÀVDñ°Uòz¶*c ¡š®Ïâ³Ðô‰U;±l±ë Ì›>¶¦ŽzòõÑEÄjêó©É{SDZ~O/U;þøã'÷÷ñ-B.áqÂÆýÜ?¸Ç‘{M‹›Ëºi0q²ÁýFœ<ð_k—ôöq°Í;ÞñŽã·~ qÜÝeßã4GÖ³'Žúo¢YædTû`7¸Ä‹,s‚${Ñ»ßPû°3Þ¬Z½ídÕuæÝjçôDæi_Sî’ŒHNàd¼l¿êã¾Êó¬½)ÒG.…r/¦œxp?™þ¾V¾ÁÌÍ]"FϲÆô‹ž8Zõ}7óœãç/eö§VÄ)>—N#cü³úöŠñÆä~÷û‰Þþ}rVNÙ B ( 3 k>qäpI¸\NCÃ-ãŒñ$t6>ÍÉ¢ø›º)†]Û×`ãÃ]¨¡'}|ò³65èY‡xk‰_oެG‘»§b²L@D×gSz A¢' ™1= ‹æS¡>'°k9u„}Ó0q9q„¡ÓÎ×öΧôœ8Ò_²µ£[»tkÿ_k—i͉£´0…XëhØD -ÜÚ‘Éé}îQGuZûyâ÷ÇøÛe^{»iìiÓ'g.ùËOüþ+Ú>”NrÛ×ZO|N_`_Sw^ß>hOrÚóI¼ºí£‰Ï™sRgÕ$ÖÐþ`>­Ý iR·f“9û5¶?¦O³×Øz~ž/ö9ë”™}ô'Ú奓}ôϹeõþ4 Ó‹Ëæ/ózèk;%Ñ {CûFÃ-kØî$ÈU®r•MuúüU_ý:—ÑW}¾ìÕ:÷âÄÑvx.ƒÅv±^w³N =îq;íè£Þòø«µÌól•÷%s¬ú<›Þë¢'Žä­ò¾ëùÒÈ¢MÏ{¯eOõën÷Fš¼Û%ê3ƒÄû› ]Z»ñ´öO‹mc“Sýâ÷V…U=ê9PÏCÿØ''ŽpWiÍI#í[»ü†sÀ7èqGµv±Ö.q ¿pë5WLixŒžÛ8W‡ûHÑp$x½ÓE¸=®%:Žëc3¦§§‡¿¡k É­š üÒ‹|ÌUV˘®d.5Øå©™†£ÛhذÔ].=µÙ€Æf.5_òš:ÆËãW'ëùû¦/,íƒÈä„Ì>ðñ?³Nš´K;ƯŒO!'Šn}ë[ßüæ7G“ÿì¾îu¯›œ¢ps`7Õuj÷Uð Dù¯{¥ˆÈm—„÷#bsg=ëYã l¿öµ¯_ìÔ†û¡8ÍdÎ+_ùÊã½Äýë_oîë?˜nÌÝ.;ažÈ^œ8R¬ÿ/½ÿú«ÿ\» 6Œ¬Ñé ÿ‰Í·£õx®zâÈ7±ùÏ4q:Å·Ï|æ3ŸÚÑÆñæÂ0qb%7|]eNŽ=ò‘çðãe/{Ùà¦Òî×s¡ á17ËnO©¶Êã¾Êó¬#‡üÄÑï|çá>÷¹Ï4÷sò<½÷½ï=œóœ8äÍ’{ý¬ú|Yõñk¤Çx£zÿé÷ ‰n\í9æþ\Nj¹_ÙÕ®æwÊ0~ý¼›hïVœ0pϤ¼N§ßvª¿ì롯×ßS…ÝMà½f½ž{éO‚ø¶D')œ¼ó¼÷¾ñšyõ«_áØ¯òúÛT`‰ÁªÏSìÅ:sâè‹_üâ¦ûÓ}ùË_ÞôÕê»Ás 8v õþ1ëÒk^óšÉ·f¦È2ϳUޗ̳W¸äÄ‘š¾0â=üsŸû\†c¿ìû®ßýîåÆö‘UN%Wïµÿò—¿|rÃz'Õœ'àÿÇãßxð+) B (WÖ|âÈ-müñ‹ŸÐèxBw"HŸ q7Á—S@ü8 6b,­¯£¾öÔJN3mâY²|]½HrÔàÏ:Äh'®™æK_x~Ô†§ß¤ârوŤVâŒÙ£[°qâÙ-˜è›¼¾7oòC‰'‰ëk‰7Öè!Šš:ÚR+ÓÊÄ‘‚³QÒNmºh<øÁž‰ñÍFÏ{Þó&7hŽ}ºÏe#æpyŽ?jg‰?¤ÝÄ»˜öŠ82}ûjîd/‰#ØØk»÷ÌÜi¯u­kmºqñ²ël'Ä7ƒvIÜ,q£X—üñ£{/ˆ#…–}Üå,û<[q„ÄDŽÚß,qÙ¦çfHˆ›ÝìfÃç?ÿù•/U[õñ q4k½Íe©¾mﳟýlo^Y¿ùÍo>  mªŸøÄ'ª·Êë!…‘vo|ã'—ƺŒô oxCÜ“¾ÿ@?1N)¯|å+GÂzÊ<—}ýͪ±ˆm7Ä‘ú»]gˆ£éµ~úÓŸž<·ùv‹çtýÝŽ§ ¤| B_wÙçÙ²ïKæÚ+\zâ¨ßƒonó»cZ–yßµ¯OÈ©Mï9ö@á6ØHú<á/b߈ÚXƒ\uÄDǃd.ëŒÞÔQR/ã¹½ÄEeVl&Ïâlˆ¡‹ÑL‹$Ï8õ“¯H6Ûûéü©ø™Ž7Î\üjÉKì´.þ¤Ö_­;}ªàÛßþöpÊ)§ŒÍ÷sðAr–8â¿ð—»Üå6¹"zâŸ8øÏîNdFèÿú¯ÿšá̾ÝÌ}p_CâÄ1ÂtÖëvfr B ( }ˆÀ>!Žp NCOpˆ=›^ã:‚ȘžÜp}|sO¸öħôôêp,£ŸbøCB$VíèbÌ“zMÝ^/#âM– ôˆ€é-<‹iê(:¿x¢GêdñôlÚI""†ß5|tskê:»x¹âÄÇŸuèµÔ®cn9r—:qÔâ÷Dœ¸¸ÈE.2 6\nö•¯|eáºnÊëÃbHÒÈ%oóı÷K^ò’ƒË%\µ݇¹y5–µ;ñäò´óž÷¼ã²N‹8:°ç6¯¯öcØüÇüLjñ¼õ¯²N'‹Ìa?ˆ®ƒ½§¬}ÙÇ]Þnžg™÷`öPÏeÏÏÏåYÄà^®aÕÇÏ¥X^KòÏ|æ3_úÒ—Æç‚å`ˆWí¾I“Šï{ßû†v¯¦-'·›{Ù×Cú æ&峤ÿ@ïtB &.7ôž¶¨¬òú[´ö^Æìuîž{¹çej-û<[ô}i?à²Êûî2Ø%†n‚nÏ7 wZj'Aøº<ÝeàïÿûGÒw§œò…@!Pû}@áð iÆßj 0MeþÇ …ë Ç‡Ø¡‡ ÛH¦Ž\FêˆÇmÈ—ºMÅØúˆ8ñiìêèù¦s›i¾¤èüˆÓ=‰51]o“Ù|6¬OL•±X‹Ô‡¬iêXGO’#ŽÈ‰_ßÏ•z‰åSCŸœ¦Ž6}O$Éó.Ž’B (öNc=ó™ÏœÜ ©æþZ.Mqºa/I+ä]»iùHŽ9µp£Ýhî·8õèsyì~ÃîpZOá9ûÑ:£ã‚lE»Ïá 'œ0\àî¹!¾©Ó)¦ƒM¤g¾ê B ( ý„À> ŽÂw„”AÚDÏICˆ%ú4|†8|[ré|iMe–ŸC£ Ÿ»ñƒä(u؈^ÝÔî×Ç××mà i¥ÞÆ'—uˆ3¶¦H?N¬/k·/õOj­¤( B ( B ( B (ÖŒÀš‰#7ÇÆk„äIcpSÂðø |DÈqñ5u$•ÄDB ‰aÉ¿|sð¥ñ©OpN>…ë`“CR3>㬛?uõñ±o+•L ùBl" ¢> i“Ú\Ó†ãÆ­xz¢&PøÔ‘Ç—ºô¬'ó$žO<¢ˆ/à'&$’úê«}rk%…@!P…@!P…@!P…@!°fÖL¹96ÁàBõ:?â'^!Ä]¬|:>ÂXlîydÌÎ/NK½ÔIm±ñ$µqDLæHíp*áFŒå«Û×lÃÙb’E%“%>$Œ&ãO½,(±|‰ïcÙ,65Ä'6?AîÈ5ŽM^ôIY;]“KØÌi¿ùb¯KÕ%…@!P…@!P…@!P…ÀºX3qäR5ü6M…w@Àhbôá,Ä“?ì©Õ×SG NB QO¡ÐÅÊÍÆ©ÛÔI~rùS+s³‘̵1Úág’vÝbµ,Ro¢'›Ê8µÓ·Ð1>$q$'~ä’:6—x¾ÔJœ1ÂHŸõ“ÄËO=}ìrbÏ=”Š8j ”…@!P…@!P…@!PëF`ÍÄQ.UCˆ#:îé | ¢A[üëq9U„ÇHNê…l’žCC'†d¬O,{ôø#æWÒÛãßÒ+²¨(n}áLŸZY0[ür:¢©Â)º>qjd#©£–|žfœEòÇÖ¯k:&„“œèu£FI!P…@!P…@!P…@!°nöqÂ(<Â'|.‚]LxzH¾´¦Ž\Fï ç!&D’Úêv1$<ˆúx>’5°‘Ì—õ¤Vê¦NbǤ~H^F2™EÑmÂÄYxz¾éÚYxú2ÉCÜØ œø3û´M<@D Y{€H~j“Þž¼ôïÚ©Ÿ…@!P…@!P…@!P…@!°NÖL¹T-<ž¹ƒ¸Á1„,bã#ø ñưk¸ˆDò#©™ºrOg'}øûÜÌ·½±¹Y‹ù“—X¾% Ø1°„¼Il6Ò'[L&gãd?tùæKg7ÖË‹ÞÔQÏI¢~=ñ±ñ''cµ´ÔÏu©Z¥¤( B ( B ( B X7û€8Â!à&BôàèAÆÐC ÑCòà"¢Ë¥«'FcÓ«.%>qÄXžZ»1I>]ð/b´Ô¤÷±m¸¸H\V,´ß =‹QËX ÉÂØB0±õ-q ¹jÚ\ÆéD?gÈ£ì‡O|rØsªˆ-~=_lïlzI!P…@!P…@!P…@!P¬5GWoÛÇ ipzäLo3&üø 1!i’_Ÿ×Â&ä8>yĘÈÃUÄž5à1bÞƒ?­ÏS›=}SÇœÌc¼­H\TL¬)nR‹¥ë³‹£njëc— $&ññµI¾ö¦Žã¾OLâùêR5(”…@!P…@!P…@!PkF`ÍÄÑ5ÚöqþàÔÖp áDÒã!HÆéCîÈ§Ë Ÿ!Ƙ]c×›GO2—X¼EÖ‘ùú¸ÔJ/_\ÆÑIÆ©±aó3IsÜ[ÌâMÍÛ€°eò¦NôØ“§Ïé#¹Y¨8õÒúq3qâ‰üÔ‹_ŸGgW“$é$?§²"Ž TR…@!P…@!P…@!P¬5Gîq‚Çeh‘踌é&† ɃgW"¡ÒÔ‘—ÈX<žBNH">¼FÆ©™XñD¯‰M º|œ‰|ó«Mä“äoŒæüLð÷&³I#}ñ~Á!f²9qYœ>›ˆ=uø¢gžl’ÏzÒû3ÎrúH-àKúÔŠ]ž–Ø"Ž SR…@!P…@!P…@!P¬5Gǵíã-8Ž´;ø…èx…EzÂ&'ãÔb—„NÄ&G?{t½&‘5áIز&ùáTħ=ušº³H^TúÈérÆäD,Éxc´±XsÚœü4¶l2uõ„O=c€›‹- 5u¼lMOâëóéìúØÕT#µ­©nŽÝ@() B ( B ( B (ÖÀš‰£cÚþñ¸‚Ðp ø v½ÜÄ®—§×œ< 7‘LÍ4úô$1â³uÃ…°›/üKæl¦Ñ.‡dýæ‹M¬q|M/Iš±ÙÓo4žlÖØÂ-Úä©­g³0zjÐÅϳé ¶ÔN]yjñéc—×Çò9‰dM‰Ï:ôZü'7½¤( B ( B ( B (ÖŒÀš‰#'Žð¸ ¤Á5°¥±Å'Žïà~Hvñ|á=Bá#bË\Í5‰Ë|‰á#ìšòúGÒ×O¬žXŸœ…Eñe$„È11›MžŠ=º^n2ŽOŒCþ#uø<ìD¯ÖÒÔñä2Gz±t"—®&‰­¾Um„¤~…@!P…@!P…@!PëE`G¸œ^ÃA e踅Dx>}¯‡·ˆ-$S ãúú=¿¡.ŽBî"ùMç6Æ•ô§’øˆ:D/wz ÆÓ5›i¾HXT,ÚÄiÉ–E«•M¥®Xz?nñFÈ>q„ރÖŠO=½8,’<½èÙ²½&/±Mmu©$J B ( B ( B ( 5#°ˆ#²Þ#ãI|ZìM;rBá!2Ö6äO/|x ½ºò#±÷5Ô1¿>ód]Í4±Ó“G_H\F²H„‹E˜0 K-6~›Ó¶<ÆrIìÆjuèó¥væU‹M¾8v=›øL!§ši<Ä?Ý£ÆIK B ( B ( B ( B`½ìâƒkÀoÐq8‡´\ÆGøÅÊ“1==„Qê²cº<±á=è÷9Ñ›{¾¤øüˆÓ=‰5± 3AìYD?q£Š<"Þ×q‚ˆ]nZSG‚ÇXmµHˆžÑ鵌{BH^bÍÅÇFè©gœÓP _ªvýë_8ñÄå.,þð‡‡¼à•7…Xá2ÈaáR¸ÌF`¶µž/…Ëlf[ëùräâr·»ÝmöæËZ…@!PìCÖLÛ Á! aBá-´:ëI†6=î 'ñbRGal®ð ¸ŒÌ×Ôѯg §b,/9Æó‹#Y㬸ˆ9?%,*6a" "™ 9Ö…òerÆbÏÒš>`¨•\}6ÄŸueŽ?ýF“‹ô `ú>WLjœµé™36k"!®&ŽÎþó×¹Îu6²üù™Ï|føèG?ZySx.S€.…Ëlf[ëùR¸ÌF`¶µž/G..ïxÇ;fo¾¬…@!P…À>D`GxŠðt\‚1r‡°õ1|Ƹ‰@Æ#ü_.qK=yt±áVR7c½\—ܵ’Dc@IDATð/Æ„¯çXÔN.ç’|ö%…w l)rÆ„D,8¾,JŸ9Äg“éùÄ„LJ­Øõ‰Eî9DQSGáO¾>: ¿–yÔ䋽©ãX¿ð¥j‚K B ( B ( B ( Bàà °fâ蘶«p ¸œ††[)d†O 1ÄOúœ„1Û×`ãÃk¨¡'}|ò³65è™C¼µÄ¯7‡¼ôMEîŽ"qQɤYTÈ"ôÙ@6gÂGnò›:JꊣÛlˆl$qôBjÑÓ‹±®ä'/§‹šiôg¾¬UÜÂÄÑe/{Ùá^÷º—Z ËG>ò‘ámo{[åM!V¸Lr`X¸.³˜m­çKá2ÙÖz¾¹¸<ó™Ïœ½ù²…@!Pû5G.U ƒˆ yRb§¶†[ÀIà"ô!mŒ““žŸ®'bå÷’Xù®"9bélôž\R3~<‡qòõ„Ÿ®'YÇÆhÎÏÏqo2‡€é™Ô¢èȘR¦©›Æž ¨ ôHŸl$õc·):{j³åÙSC/VNòš:ÆËãŸõ,|©ZÝ㨡6%uÏŒ)@ —Âe6³­õ|)\f#0ÛZÏ—Âe6³­ž/u£ÙØ”µ( B`"°ˆ#¼~B£ã9i£Ç9Љ8¢G?NB< ùÄÖ×Q_ {j%§™6ñ,Y >ƒnžHrÔàÏ:Äh'®™æK_x~Ô†§ß¤ârوŤVâŒÙ£[°qâÙ-˜è›¼¾7oòC‰'‰ëk‰7Öè!Šš:ÚR+ÓÂÄÑE/zÑáf7»™: Ë¿ÿû¿ï}ï{+o ±Âe ÃÂ¥p™Àlk=_ —Ù̶ÖóåÈÅÅ·»–…@!P‡ û„8 É6üEȃÆFBEÇ7ñá@rB)\‰>P¸ 6’>$Oø‹Ø7¢6Ö Wð"ôž_±Îp-M%õ2žÛgas:ǬØLžÅÙ=B£˜!vÄ$žúÉ×$›íýtþÔFüLÇg.~µä%vZRk%…@!P…@!P…@!P…@!°fö q„KÐpz‚‹@ÎèÙôZH‘1Þ!¹á,úøæžp%ì‰O=þèéÕ!áX6F?Åð‡ÄJ¬ÚÑŘ'õšº½^FÄ›,è1Ó[xÓÔ Pt~ñDÔÉâéÙ´“DD ¿{ÑÍ­©CèìâåŠÖ¡×RwºŽ¹åÈ]øÄQ‹-) B ( B ( B ( ƒ„À> Žðø†4ãoµ†G˜&Ž2cÐÂuÐãËåiáBÄðã&R§©#—‘:rqrÅ¥nSG1¶>"N|»:z¾éÜfš/):?âtOb³Q½MfóÙ°>1YTÆb-R²¦©““Aôäˆ#rÅFâ×÷s¥^bùÔÐ'§©£MßIrÄ,|slJ B ( B ( B ( Bàà °ˆ£ð!e6Ñ<$$ŒX¢OÃgˆÃ7°%—ΗÖÔQfù9Äã6B*…üQWôì©)'ó²6"n!IÂ"Á‰Í¢Œ3=þ~¡üÆ$zülrl>Bà*9‰ Ô\ccyY ]lê5u²žøØèZÖ+”µqÔÀ() B ( B ( B (ÖÀ> ŽBúà p½ol8I|Ékê$/œIOþD—ŸPKœqæd'ÆáNFÃY‹ÜÄFW+yÂñ!™#ó±o+Yø¶AœbÓ2q¥Ïôˆ„ð±°8ÑÕ›MœBbËÚÌ¥NæV‡°‡â7–§G©­——âcϼ©!®.Uk ”…@!P…@!P…@!PëF`ÍÄÑqöÃpÒg@'øÂŽ[ˆßŸøÞ·›Ðk¸ˆGìDýpÆôÔ Ÿa¾Ä…wчãˆÏ˜]^æík³g?M/’–•äd!‰Ôa‹?‹Î¸Ÿ‡Ï³1Æz6¢°9$&$R6™{ÉUW£'®·±Ç×ÔÉåjbHG8ÔÏB ( B ( B ( B`­¬™8:¦m^"$‘_³½& ƒ³Ð¢'.Ž>þ¦Nò2<ü„^.]ǤYp_Çl^œœé°“äd“ìýåhüÉåË8kÓ4¾ø3n®Ñ¯~Ç^ÄJ B ( B ( B ( 5#°fâèØÛÇehȼ‚Þ8:"„NSGŸ1{›ð!‰K-1êÌÙÄΟÖérúøð/òéĦ7o?gî,‹±X ëE0_¢ ¢óËCúÅ«›Í&7cþØBò°‘¬GX Qe èÉwªˆ½ÏËÚíKý“Z+) B ( B ( B ( 5#°fâÈͱñ!yÒã¾ÕZxü>"䎸øš:’Jb"!…݇dŠ_¾9øÒøÔ'¸ 'ŸÂu°É!©ŸqÖÍŸºúøØ·‹J&|!6‘Ñ Ÿ……´ÉFm®iÃqãÖÀ<=Q(|êÈãK]zÖ“yÏ'QÄpI}uŒÕ>¹µ’B ( B ( B ( B X3k&ŽÜ›à p !‡zñ¯bˆ.V>a,6÷<2fç§¥^ꤶ؈x’Ú¸"&s¤v8•p#ÆòÕík¶ál1É¢’ÉF “ñ§^”X¾Ä÷±l›âˆŸ wäÇ&/zȤ¬®É%læ4Žß|±×¥j Œ’B ( B ( B ( B`ݬ™8r©þ@›&Â; `41úpâIÈöÔêë©#'!†¨ƒ§ÐGèbåfãÔmê$?¹ü©•¹ÙHæÚíð3I;„n±Z©7Ñ“MeœÚé[è’Æ8’?rIH›K<_j%Îa¤ÏzŒIâå§ž>v9±çJE5PJ B ( B ( B ( u#°fâ(—ª!Ä÷áT¾‚„ ч ‰-þŒõ8‰œ*Âc$'õB6É Ï!‡¡‰C2Ö'–=züÆó«‡éíñoéYT·À¾p&ŠO­,˜-~9ÑÔ á]Ÿ85²‘ÔQK¾FO3Ή"y‰cë×5ÂINôºÇQ£¤( B ( B ( B X7û€8 aá¾Á.&<=$_ZSG.£÷…ó"Imu »D}<ÉØHæËzR+uS'±cÒN?$/#™Ì¢è6aâ,<=ßtí,<} ™ä!nlPNü™ƒ}Ú&ž? "…¬ƒ=@$?5IoO^úwm„ÔÏB ( B ( B ( B X'k&Ž\ªžÏ€ÜAÜàB±ñü„xcNØ5\D"ù‘ÔL]¹‰§³“¾Nü}næÛˆÞXƒÜ¬ÅüÉK,ߎ’ìØBÞ$6铉-&“³ñG2Ž¿Ÿ?º|s‰¥³ëåEoê¨ç$Q¿žøØø““±ZZêgŽºT­RR…@!P…@!P…@!P¬}@áp!zpt cè!…è!ypÑåÒÕ£±éÕ —Ÿ8b,O-ݘ$Ÿ®Fø1ZjÒûØ6\\$.+ÚožÅ¨e,†dal!˜Øú–¸\5m.ãô¢Ÿ3äQöÃ'>9ì9UÄ¿ž/¶w6½¤( B ( B ( B (ÖŒÀš‰£«·íã 48 =r¦·~ü…˜4É‹¯ÏkaòGŸ±‹¡Rw…Àš‰£k´Åã4üÁ©­á‰¤ÇCŒÓ‡Ü‘O—>CŒ1»Æ®7žd.±x‹¬#óõq©•^¾¸Œ£“ŒScÃ:çg’渷˜Å› ›1¶aËäMè±'OŸÓGr³Pqê¥õãfãÄù©=¾>ή&I ÒI~N!e‡„8zñ‹_<}ôÑÖ³IÞýîw÷»ßý6Ùæ Î|æ3õW5|Ï÷l¶ºímo;|êSŸš~ÄÛò‡ 7¼á G®yÍkÿ÷yÚm@Sxžþ¼ÈÏþìÏÿøÇOwm/^aYGtÉz=ÑmþC ^ï§?à‡Ëï¿ú½yúcVÚÎìÅó¥Þ'vƹ"k&ŽÜã(ËÐ"Ñ}¨œnbØqd Z»<ج™8:¦í€+ ø §€¿`׋Á}ð±ÑÙIìzyzÍÉ£p9ÁÔL£OO#>ëP7\»ù¿dÎfírHÖo¾ØÄÇ×Ôù’¤ù›=ýFãÉf-Ü¢MžÚz6 £§]<±ñ,<›îÁ`KíÔ•§Ÿ>vy},Ÿ“HÖ”ø¬C¯ÅrÓ¹¬ú~ÈzO¸qtooO—~¸þá¼§ T±B ( m(âh[xVr®ú÷àªy+-²’{êùrØ?„GÜÖL9q„GÀU …®-->qüx÷Cа‹ç ï [æj®I\æK a×Ô×÷8’¾~bõÄúä,,Š/#™ DމÙl‚ôP|ìÑõrq|êdòÇ©ÃçÁ`'z5ø³–¦Ž'9Ò‹¥¹t5I|lkùVµÃí üLg:Ó–{Hî㇚8:1Ê÷qô]ßååÒ^x§yé-&Ëb±¯‡eç\l'µ,õ8ÌF¬p™KY/Gâh§×Þáúûo/~o^Ͼ3îjWù;kY4·çËN¯Ûe÷_ñ‡û€8Âuø¤×|BÊÐq !ˆð|ú^—»>$SSǸ¾>?^B¯.]-ÜEj6uœÛWÒŸJâ#ò‰^îôŒ§k6Ó|‘°¨d&ÏBäË¢ÕʦRW½·á˜ò‡/õè=8b³Qñ©§G€E’§—£=[Ö ×ä%¶©£måKÕܤú7¸ÁpÕ«^u8ꨣ†ïþîïï9ô…/|aøÖ·¾5|æ3Ÿ^øÂšg‹,ú~ñ‹_|¸ýío¿%ÿK_úÒð¬g=k‹}–a•uºÒÏüÌÏ Çü` ç8Ç9†o|ãƒû|èC>ð ö°Ä/—k_ûÚÃ5®qñÞ g;ÛÙ†/ùËÃ1Ç8e¸!¹9önðô­xw»ÛÝÆ‚W¯9î|ç;W¼âó~þóŸ>øÁ|à+¢múçO|âÃu¯{Ýq¿Þ¸¿þõ¯ß¶öüç?øìg?{ÕÍÝ…/|áá.w¹ËkX}ík_/©tƒÆ÷¿ÿý›ƒ§F‹¾àm]ßùÎw†'>ñ‰#™å5è1¸ô¥/=Îéò|ÿýßÿ=5K §Xõõ°Êãkxõ«_=|ä#™^Îdüc?öcõ®u­qüÔ§>u|ï™8÷±’=.óüLŽmQqÙÇÙ¾YÚ¼ßcþžð^æ›.Ÿüä'ïy?ú£?:^bí ¼×}ûÛýߢ[òí«»ØÅÆ/ÐxéK_:W}½÷ ÄѲ¿.¿ÿúǾèïÍé¼ïU_.ùòw5yúÓŸ>þ½Òÿåo&÷ô7Ó¼¿³Vù{>»]ôù²ï«¬sÙ×möUý}@á8ü‚ÅA„÷È8DŸ{S'䎜CxˆŒõ„ ùÓ C¯®üHìÆ} u̯Ï$SÈ©fO#ñO·Ä¨q’Àe傼àH óœçœ›ŠX¸Õ­n5Ó¿èøÿø|ä#·Ôð‡¤{óì$«¬óÜç>÷ðò—¿|ØnoŸþô§‡[ßúÖ;MÐý~Ѽà/ÛIˆ£Ýàyå+_yø£?ú£qš=èA#Aâå³$óÍòíG[ÿ‡ó]ïz×áæ7¿ùÌçîW¿úÕñ[×d½xsîsŸ;øð3Kžô¤' /yÉKf¹FÛ¢¯‡ûÞ÷¾“ûjÜîv·~û·{¸Ò•®´¥®[W„WÉ|V}=¬ò8xM<ö±ƒ€ ;½:ÿyE  ÿ÷ÿw$çñ‡ƒ.‡Ã£´ÿÖ¸ìï±{Ýë^Ã=îqq#Þ»}àœ–?ù“?ÿéóÉO~r@"‘U^ïþ!vÇ;ÞqòÞ~ï{ß{¬åê3žñŒQï¼ç=ï´uÉ*¿.¿ÿ¦1]ô÷æt^w‡Àª¯¿EŸg¾ôÆß/Ó¯ëUþžïwºèóe•÷‰~žUÖ¹Êë¶Ÿ³ô3&û€8BÆø`ƒß ã pi¹$Œð‹•#&c6zxŒ¦Njˆ‹$NlæÅUkbÕˆ˜—_^ìâØHÖÉFİe}ýÜü3%…g:§Œ Ê3Žnl¡Y\S·ÄdÁbÄgî,XNôÔφØ5¦ÂÇüìÆô@òÙ´Ä7u1ìòÄúÒߪæd‰ÿ„¬ðŸÀ“O>y8å”SÆ›Fú€ä­½ ŽœdrŠ#r£ÝhðÁjâhÕuþÎïüÎBÄ/°7½éMãé¿ 'š½ùÚöuŠo{xÊSž2®Ç:ü—ß Ã?úѸÀ§üANBäìÏž8‹ø#¿¯r•« —¹ÌeFkæëãö³ÞÿAã•së\ãr?÷¹Ï˜wÜqƒ_ìä5¯yÍðˆGzOˆ£ÔL(qLv'âh7ëD ˆÓ'Ó—” ®ÔGÔ¬S~ã7~c¸å-o9.i÷‹¿ø‹›Ž÷.r£Eñ4Iÿ6û~ík_;ž¢8õÔS‡þCãáLeo/{ÙËÆK#\áþýw—ÞøÆ7Ã<ØC¢ºl"—E¸Ímn3Üÿþ÷caåëagÉ¢Ðô§ÎÓžö´ñôŸË6nzӛާâ»Îu®3žZɸúíXôõ°êãà”ä‰'ž8.Âå®÷¼ç=·,èÑ~ôxé)‡SN7.R¸.ÔþYç*¿ÇVýà:½ëE^ï>>æ1™œ8ò÷GdÖ7»úG“½ë’Uþ~é?ÐgÝûñ÷_Ö–~Ñß›‰¯~oXõõ·ÌóìSŸúÔpÛÛÞv\ðnþžïw¼êóe‘÷ óìf«¼nû½•~ÆD`ÍÄѱ U‚»!‚ðZŒõ$„ C›÷„“x1©£06Wx\Fækêè׳…S1–—ãˆùÅ‘¬qVÜFÄœŸ›0‘‘L†œaËBù29c±giM0ÔJ®>âϺ2GˆŸ~£ÉEú0}Ÿ+&5GÈœ±Y qµqääÅãÿø±€ËuÓ÷ØÄÑnÖù¢½hüšz›týyÏ{Þ¾»_Ï%.q‰Ávþ7¹ÉM¶ë=ØÄÑÞð†á¡}èä¦Ñý‡ÆÃ8rjÈåxý ±Ÿóœç ÿ?{ï.KQÝí9\D@¼üAô# * à= šˆ@"Q1ÄK¾ñú™pTHÐDcŒâ-((bTðŽÆ€ÁHä& AA;_½µyçÔî=³wOÏœ=³k=OïZµj­UU¿žžžþíêîG?úÑsVs!¬Bã?Ì#·Õ1›l²I:õÔSK;ÇËÞ{ï]ô柶?hjŒÉÁíÔ!²>ÿùÏ—qQßgŸ}«¦BÚ!Ðöb×ýÀê .*ù\ |pºð {ƒ{à˜N;í´R?ï¼óÒ«^õª^ÛRP—¥°—¦gŒ]Ïc]/\›3o{¼×qÓþŒ£.¿_šôÓzþ«÷zÛóf3.ê£!ÐõøæsVÿsx”ßóõL»~^Ú~OŒ2Î.Çm=·ÐWO¦€8‚§‡@‡K ¹ƒ`«}h£7!DÂ~mÔÉe>âÐñ•[1¯uJbÚŒ•¡ŽÐVs,ä6ÎÅxì Š‰tÌ&—œ¡C„Ø6Eiø;IKÚð!·¾æ³®/ä KeµíÆSªB;›ýŸ6íY-uÊïñ§­¬X±¢÷l¡7¾ñ鬳Κ: ÄÑ(ã¬W‰0¹k¯½6ñPäo~ó›éÆoœ3ßIx¾'o„“Z¿‡¯JâLÀ‰•F +’8qr d[M è3­eýƒ†Õ[x`™G=^þëì‹y9óÛc=z·­A ½óï¬C ‘óÞ÷¾·gD¨µýAS_˜s›Ó¡‡:‡Ô¬—<|ðlb¢7Pú"Ðöâ(ûÛ`ÙGH“b…£dÑë_ÿútÎ9A×wn‹a \åÕ§®ç±®®MäÚïuÜ´G]~¿,•ó_½ÐÛž7›qQ ®Çß°Ÿ3ÿA7Êïùz¦]?/m¿'Fg—ã¶ž[è«'&ŽxÃ’\\œÜu¸ê’0Øhc“¢©}ˆ£Žà[çÀF¼9(‘ÚßxÇFtûÀŸ±ØNIŽ—R!vA©r¶S%‚Ò 89êÚ |ˆ5>«EÌ‹:“•ØÁ¢º¤¹Ð-ña\ÆS7ÎÕEÙTÚíϱâ7qÄ2lŸÿ²×^{¥ßüæ7äž%Ó@2N–¦s»oûiÊw¿ûÝtüñÇÏZ%ÐôYŒ:o—ámvËyYÖÛ”UIñPfN«‹Ô?høQtþùçÏ™¤ŸyDâˆÛ4}KÈœ€>†g>ó™³nëÔ¥íšúœÏól£¦Ô·~qÔDgþzÛˆ£ìžÿöå/¹÷=Ê*5–Íj$ž¥Â÷+5«¹ýp)IಔöÖäÇÚõ<Öõµ9ã¶Ç{7íÄQ—ß/KåüWïô¶çÍf\ÔGC ëñ×õs6Êïùz¦]?/m¿'Fg—ã¶ž[è«'&ޏUM2"FòFRÐY=·'ÁVJIêÆXÒŽN‰àK|-úÏWa ¾èØÐkr‰œ¶ÃsP7ž¡q3µuÐ<ËÌÀšIí”A¡ utJžÈÝ W (!}œˆùµ‹ŽÝÜØ\…€ØÍA‰/1ÆeµøG;þާõ­jÜ¿ q‚°ª„Õý䳟ýly î8ŽÝÌßæ |ãä–ŸvØ¡ü÷ŸWÍ7… <ˆ„I=çè”SNé=W‡ç¦°¬·)5Ñ1h¥K<Í[?ãèu¯{]úÞ÷†âM3•eýƒ†‡ž_vÙesƹbÅÊÕvG<³hÏ=÷,¾|æ{›+” uú‘­mÐÔæO{ÚÓú¾¦×·óÙE‘K¥1þÌA íñ0ê~`¿¼á o(ýŸ{î¹é°Ã+«õ¼í j–®/5 \–Ú›ìx»žÇÚ\¸ú;¤~«Zs¶m÷:nÚ‰#Æ:ìï—¥rþ«÷zÛóf3.ê£!Ðõøëò9ã³×3ûkÒ×£}j"º S@ÁÀO°¡Ãs è6”pè~ˆD¥ípø#’OØê<äÇ»¹ŒÉ¦Y<‹cÏ@§ÅrÐî8ða³®_6 –:ñ`¯™–z’$'Â`Ì¥uìÔÕ0uý±3`쥾ÆÕ%ý/Y„?¢_ êlèEY-6sI0µ&Žxƒ÷¾#¼yŠ“@Sxp$?ÆIGã'óxÈCRÞˆÅRRßb…½ù dl‹%ßøÆ7Òúë¯_ºãÕ¡õ-cúЇ&~8+A‰Dÿ²Ëˆ HΫš‘QVaµý¼Ð…ùZk­•¾öµ¯¥u×]·Œ rw©¼Ê½ xÂÚ^HŽºøáG ûéÅ/~qyÉä,2heÚ„áY°ûÀeAˆÂ¡B ëy¬¾Åð‚ZûØÇ¦ãŽ;®˜Vq4_Þz,“ÖÛü~Y*ç¿&–mϛ͸¨†@×ã¯Ëçlíµ×ŽëŽü{A™äu‡cˆrñ˜âH’‡‰Ã_HîÀ1°aC$Ôáüå@\¡$WBI;"· ±”ä‘¿Ð>ã53bɃzͯ0Nìu¬ù²y~q`ó{Í´öóµsÇ„Ðt|(SbãÐÍo<%“r²u;:íæ†øiúS·/ÚÉEœ¾Mÿ¡¢ñï|'q«Ò¼(åMd<¬—%—¯çäý¤ë ¿í…ݸÆYyñü‘ý÷ß¿˜y¨íÁ\»,š^?¨¹¹ª„‹Q^ã̓›• ŽD¢Ùå ÄÑÓŸþôò€p²rûÑGÑ¿ƒ¬m‡….Ì8à€ôÚ×¾¶ôÆ1À §ö´ý~Ç~`e+šÂÅôÛßþö¦yIÔ—%±›¦f]Ïcõö¿1ø­¡<à(ÿ4Yo½õŠi>‚§íñnnJž'øÈG>rà?ÏjßiÒçûý²TÎM<Ûž7›qQ ®Ç_×ÏÙ¸~Ïwý¼´ýž×8ë½3ßq[û…¾z"0%Ä\œ%!„:›$uxcå,jÿÜÜãJ°ëo>ÚÕ-ɃȱÌÔfþâC»$–¾äVLJ~Ì—Õùça:³J˜£dà&«= ÐiÇ¡„ÔqðèNš•D>´ól"túf›ajVNbñÃßvÇAÉfÞfú&†ØÖ+޲oyH4d„‡³ÔáGÿáÛf›mJ?õ[zÆû”UýÎ컌sùòå‰òqh®âaè[mµU!ÇÐò“ŸôV›P_LáÂÒWŸ|òÉé=ïyOéž'ì—wæÖÔ•ÄÑJ,úi]Ððyá¡é,§F?üðtæ™gÎé‚&xÄ#ÒE]4§ CÛãa¾ óÇ?þñéýïâ3€¼ìe/K_|qÑãO;Úþ@Ç~€àeu˜ûË.åý¸¸£lƒ@×ód/ç@Þô¦7õV$p‘uì±Ç¦G=êQ½îÇMq;)/@š¤U¯Ó )]¿,•ó_Ö¶çÍf\ÔGC ëñ×õsÖõ÷|s–]?/mtg×ã¶9¿¨¯~LqÄÅ |ƒõÛóÐ$ެË_À1°Éu Ûæíir!øÐ7až¬.Ã<ÄÂm‹Ÿy³Z„úÌÅØŒþnØÉCIŽfl6 “öXÙ¢¯¥d’NÞ Sêã ¬ãË )%k²ZòP"Æà‡‹¯b;eÝ—ùô¥”ÆdµØ(k"‰|†zP ·T¼úÕ¯&W‘øÃéæ›oN¯|å+{·NÙFé³`jº_à×_ý¬U,9Ÿïb×/prðÖ!åÒK/õÚñ®ãäuëü”Û{N<ñÄòÖ8þ›Ési6ÝtÓò\’wܱtËkÏyˆö$„ÛÓŽ<òÈ^×'tR–o¾ùæ=»ÊBÄ~óáI{<ãhî3ŽÀ¥^uÄ*$ÈÞ†uÓM7•U_¬Ìc•ä‘o !®–¶ÇC}aÎÛÚ.¹ä’r»ûwß}÷í¥ä³ÁkßC†C í÷˸öÃË_þòY«yÈ=Ÿ>GKQ—¥¸×&7æ®ç±ú\ÄïÎÃÜÒòš×¼&m¸á†³&Ô†8"`¡óŸIyã&Ÿs„ßü³€Ûò7Úh£ô¬g=+=÷¹Ï-„Ò$lßõ÷K× z0XÌóýÕÒö¼YÇ„>:]¿®Ÿ³®¿ç›3íúyiû» ë8»·ÍùE}õC` ˆ#ùIHuD_„Ò³øÁ7`36·¬é×Nþp’Jpú’ƒü”ØÍIŒýb§Ž`Cðk%´qÖ×AQ·#tÛëÒNQ·1LÞ Aà0ycô• ÊM%uâ :¾æËjo<¶aCgs¼Ä@ 9¶¡ˆ#~œzꩽ7å<³„ÿƱ*Njؽ÷Þ;ñšò¦øÞ´_uÕU½Øfõú ¼n?æ˜c ᣭë8ý7Ï òÎ;ï,o3»æšk¹¬R;+\>ùÉO–%óý:âž,Ùßk¯½Jsâ¨ÎÓÄ“¶úÇB<ûI³.î_ò’—¤C=´†°¯¾qÔ jõ…yÓ×ú$ MǰT˶ß/ãÚ<§ŒUG<ÐáMˆ_ýêW—*|傚 ëù¤ÍçsuÃe><~›ÛºžÇ¸]žðÔ+‹j9Ž÷ÙgŸò[¤-qTÇ÷;ÿÙÎ[é›g ’]wÝu"/Îèúû¥ë½ó_¬óŸýYvýi|”Ýèzüuýœuý=ßœ]×ÏKÛß]ÇÙõ¸mÎ/ê«S@IúÀÀU°¡×68 |ÚŒ£nœœ møðÃWxêøâGê3?gêr'ÙÜÇRûª“‹vâøû°¿Ò0ß>ŸmøºÙ± t”; áÃÀ$pÔÉ…¯9²Ú[…„ͱÑyì›<vI Ú©G DnJâÌ¿vû5~Cݪ–ýËÊ¢úGÛW\QVÀðV©úAƒÞPõÅ/~1ñL¤¦ðZj^O=HŽ:ꨴûî»Ïiî÷À8V ;Nnaü¼ {Ûm·ÓÞ&F^æ¹ñ¬§óÎ;¯×ïRWvÛm·Þê1HO›¦¼ùÍoN¡Ü‚ žÍU!ûí·_zÅ+^QnÛ¬cY½ÙÊgžBý¤íñ0aqå•W¦}èCéŒ3Î軯ûõ¶Ù´=Ƶ Œx…/Çò-·ÜRn?…”^ª¸,Õ=7¹q/tƒLå‚©ÿ²ñÆ—ÕÞ’Nûµ×^[~‡œ}öÙéøãOÛo¿}ºà‚ +ûúIÛã½Ë­%ĺúØö»îº«|Ï}ôÑsÎú¬Ê²ëï—¥rþkb×ö¼ÙŒ‹úèt9þFùœuù=ßœe×ÏË0ß]ÆÙõ¸mÎ/ê«&ŽfîÉž!Zøa gŸÀw Øál‡o° ÿÚNÜ%\„ÄvDRÇ:9Ì)ŸAúÉ»PÊqØF;qöK>scw>Y, +Æ8Hƒ`³ÝA[/÷ý¡Á:|¨SbC(ÖUAøH"9IŸmD,yÙÐõ«mØmËjïv5|¡‰#‚øO!Ä`fE+ošÒøMZF'Ï aÙ;+w–-[–n¸á†tã7ö}ú$çÉøx‹·ùAxô#ˆ&9¾ß¦¾ùOÇÄ&›l’n»í¶rl°_ƵOê óç=ïy…ä³Éí\´„,ãÚO}êSÓŠ+Ê !£y¹ÀR–Àe)ï½ÉŽ}Ðylqäh9Go½õÖ‰ÛÞ¹el\ߵ柯äûžs/«ømpõÕWOÍ÷ð$~¿¬êóß|û"Ú&ƒÀb£üž_LtFç$ŽÛÅÄ&ú G¬J€?—$âÁרJ6ü aà,ØÔõ“À¡´=«½8û!~‚’Xtülð æ Æ6üGV‹NI»ùäX´S¶:j+5tŠ06H½öÓ›“ª}°›ƒÒñh§ŽŽP«ˆh£_ˆ#Äq9J…Û±Q'–±êVµ™Ðø“@ ¾0gÕ«TBqìF¦ÉoHFžÂê´¥,ËRÞ{Ó9ö…ˆ£éuŒ*@  ) Žàà mà#¨KÜ`G\U„]â‡6ãà. ›ä4Èbêø±©“WnÄ|Ô‘C±OJùJ6|‰1'6ú§D,gjþ2¨¶"`øG›¢ÁáuËìR’‡‰P’×Üõø$„Ì›Ýz¤6‰"¤Ž®Ôu}‰£‰¤N+Žì Ê@ X<Æqa¾x£]}{겸½åî»ï.«5·Ûn»rk$¤²bÅŠtÚi§-yÀ—%¿ §nAMÝ.‰@ ,&Žž§(?!ñâj#y¸#êøâdž`SÇ¡!)DÝxsêclv)qðÄ!äplò/رQ'>¶™‹vyËlš_H6Œà_ÇЩ®ó0&Oi»6¦9ìˆ1N{};íÆÒfݱQ¨m¶[ÏM¥üµö Ž@!$Xt¹0_ÓZrCì²xËÝf›m6g®< ›‹ãÕA—Õa/Nׂ8š®ý£ @ X&Lí|ß,á2Ø _à(©«ÃAHèdµ´QÇ.a#¢Ÿ¹ð!bp’IØiw«ëèÄÔþò/ÄË©h£¤ßºÏ\]Xh+ †Á2°ZÈ!óÅ@Ø„N;1ø õ`©“×Ékvm’<ØÇC ±$QE] ÐgUö:α3/òŸ“·@ XrÈ!é ƒ*#ÝsÏ=Ë-NK`Ø«Ý»ì‡æƒ9Y}tì±Ç¦N8ajž‹2êŽ \FE0⛼õ­oMÏyÎsÊj=^Œ±˜Ï0jŽ%ê@ Àb!0a∇cÃkHòXÂ1ð\yø øÉülËj!•ðQ$…ðÁ.Éd;ñôA›mäGà.Xù$×Äœ¶QwÜ´›—Ò6ìó ŽmÅð‡|A˜„BGhc`’6N”ÉÕ>¹Z&Îh’Hä'ur?o!@ °à¡‹<€YÊoßZPÏ;Ä.û·P²âˆ‡™ó_dÏW' \V§½9sá̓3¼|#^0û$F@ °ê˜0qä+»á à$‡j ~ä0à$†Ðñ%>‚:¾>óˆ:vÚñc3Ÿy̯‚?bn¸û0·œŠÜuâÉ[çÌÕþB'mÅÎô—„!Ñn>¤/mú׾ج9ð×Wˆvr‡XêÚˆS—Lr ØÑÙˆE°Ñ'uÛéO{ܪ–Á @ @ @ &À„‰#nUƒ?`k’Fò0løPÊYàHþ`7W<øÀIàƒž‚RAÇ—Xû nÞ¬öâ¥Ý\ö ±¯™Ú ZÀ­4ãËæ )©Kô8)ëæ¶Ì®Å_’†ºâŠbI&§?mæÒ:„¥ã™Y‚°’È"Þ|”æ!F»ÏP â(ƒ@ @ @ L GÞª Gèp¬ ‚¯@äB(%h´ÙnNÂUEðƘO²‰Xyâà0ØðñN©/vuÛ©+ôO>øÚnûœ’$m…ä °NlG¶‘Ëc³ÈjpR§ÔNÄ<ä"ž ݺ+ŠˆÓ[=®¦„1êñŒ£ FH @ @ @ 0i¦€8’0’𑯀‹ÀŽ<º$mnY-\FÝ&çD¹É‹`Ç‘!?<mˆcÀ†ØŸã1—yÍ£o ZèÁÈ1(t&AÇÜ’¶fnn™]zq7LÛí{Ó†?í‚)Ä8° „ñ椎Ôvã,¿7ã@ @ @ @`’L˜8âV5y xȈ8É"l´!ðøSgCG°³ÁEH¯˜Ó¼ÄꎩóØ^ÇÚߌ÷̈u,ôoœ¾´-(`AÇì y£¯ô±ccçØhW¬Û^÷¯N<}ዎ:%qêY-º+‰êñ؆vc¬“‹Íüö·ªePB@ @ @ I#0ÄÜ„DÜ:md º¤º$\„:±èäÇ %ùäRláN¹Ø°SGŒG'‡ü >læD¯}sµ½8¬0Ðz‚è†\ÔñA6 &lõ¦ŸKN&gÝR ê>%œmøƒÝUEØl§¤MÛÙY @ @ @  #0aâè yúð4p”3µ:B;ü>’4ÆÙVÇe·ùƒmÄ!Ôâà*´;x møÊ{ÐîVÇ‘»eVKŒýPŸWl+tÌFr:e°è”‚ÁãC^sSjÇÏ è£¿mÙ¥OD¾ø±áÇÆ­ÙIB¶cg<øQjÏj©×¥>úÓ·ªBH @ @ @ 0a&L=)OÎ þàŽ¼Á1ȉXÂC Ö-%wˆG'V>êØÙ°SÒ%b_øÂ[8û«ýÌeI<~ÖÕ©#ÖÍ1cð× ÍsÌøÓ“¡Î˜6;ÏjO×n¥«ˆu ø‘Ï­®gsñÃ!Þ|ê¶ÕqèØÉ‰èéD¼«œGG @ @ @ L G<ãH‚‡ÛÐu¸Œæ†6Hx¹bà#(å@²Zx ëøÃS#ID¼†usê‹?BɆ¯9Љ‡3!žþÉ?SðWçͳÌtªÔÉëKÌ89ü¥“ÐnÚÔíÇIÒF”HÝnÝ1¸úˆ\€ƒH,QšK;qlúN qô˜Ç<&=âHwÜqGúú׿ÎxC&€@›ýð¬g=+ýñÿq:묳ÒG>ò‘ Œr¼]nºé¦i—]xódJßüæ7Óm·ÝÖ©ƒÕ —N ,¡ wÞ9=èAš3â+¯¼2ýð‡?œcŸfC›ãvšÇÿÛ4¶øžømÚÛÓ?×qÿ¦¦1Â@ –&ޏ(‚[€lãp“Ø_P‡W,¢D°cÝ\؉“AGð5†Òvìê”lÄÀc8&xlމx9ü͇ŽÝB›}妞ŸýéC‚ÄÕ%I__J„ñÓZH>ŒØDccHMÙ†]’X¢ny¬KþP‡Ô¡¡$íŽ%«eå؇%¾è±èäDlÃ65oU â¨ì›‰ÿi³¸¥ð©O}jâû?øA«1îsŸK[n¹eºôÒKÓØ*f±œÆõù .]ç8ÍxvÓ¤ã¾õ­o¥u×]·|®cÅѤ÷ÆêÛ|OL~߯÷çÊ}0®óßÊŒ«^[*ûo©ŒsÕï±è!†E` ˆ#¸8J68Ht¸ "xÚ(k]ÞB›$Sv+~uþšß /qpÆgµôM®¤^•DB„’Øæ¨7sfÓ`! ­0h:v3°4¹œ”yñE¯ë¹ZrHþІ‚^ƒƒÍ‰âo>JüÀBŒ£$† ¡Äæ(ÙˆÓ7«Å·ªDH6ÄQÏyeš@Åç!väjìÄÑj¼sK§6Íß»“Ü%ËJôãü·‹qkñ97¢‘/øíA` ˆ#8È8yëI´±iÏjÜ!FrÂ:%‚ ò§Úà1(ÉK¼¢zƒ<ôOi?Ž+›zvtãÐ[ ‡ á èЙ íLŽÁ&ÁCXD;ur#äA·N›¹í—\؈Ç;%6ü%™$§²©¬F¢½¹éCŽsp‡¬¹æš#=—hUã˜ÛRÉ1ê>`ž«j?Ló¨ù~8¯±‡O>Èîõ.ÕUòg˜ý7<™Û¨ófÌ«´1&•8ž]§³ªŽÛ®ãiÆ-æçd”¾F‰eΣÆ7qToÛÏ8¾'aUÙÛέÙÿ0qK—æ|ÛÖÂeÜç¿q|.4æqí¿…úi‹ñ ¿qŒsUqÐØÃ“E` ˆ#ȸø t8.†Ü¼%Œ6„v|‰ÁÇ:6t.¨(sP*úác¿pÔÙð¹(ËJú¥8íøaCì‚6ÇW÷M{_1q߯†‘ÄG]:upYãã€ñÁß¾01êæwBØÙlä€ð¡ìÔÑ%ˆÇƦV‹àƒ8ü¨#è#½Um§vJÏ|æ3ÓïýÞï¥-¶Ø¢<ëæ¼óÎK\pAúïÿþïôÿñsÈ$N‚»í¶[zÒ“žTžysÿûß?ýêW¿JäRâáØ"±p î/yÉKÊÅyCÔ¯ýër« þýÑ~40Á0ûa«­¶JpÀœ\7ÜpC:î¸ãæØûÆñª_ÞqØš?œò“Ÿ¤=öØ#½ô¥/M|qß|óÍåmkŸøÄ'Ò5×\ÓërT\xNÔ ^ð‚´×^{%r-_¾<ÝrË-‰ç@]|ñÅå8‚Ìè']ðäù*ø‡XÞ ·Ã;”Û²8N¹Ýð¤“Nš57ûä3È¿üË¿¤ŸýìgåX}ñ‹_œ~çw~'qüþâ¿H]tQ:üðÃÓïÿþïìðÿ¾.¹äÔ¾òÄ'>1íºë®¥íƒü`™{_ÇE2KuÁÓ©ðpù§?ýé¯m¶Ù&Ýï~÷+ߟ¿üå/Óí·ß^pï¦ sÜ6cWuïpŽ›»ï¾;½ï}ï+¤$sä³òÈG>²|7ñ`}Ún½õÖ2c¨tý¼ŒrÑï0ç±a^òÐõ{Bl†Á“ùÔÒå{¢Ž_ }”ý×õü·péŠý°xv=ÿ9¾.߃]Ž#û£eÿµ=Þù]Ë-ùÃ|gÝu—×F+GÂÛeúЇ–„œxâ‰+îÓºŒsØý7§Ó0ÀjÀ”GbÉ ¼ƒ·ˆIôàƒÝ/I}á°‡ C›ºmÍ’vüØìüðæÅ†PG'޾ä=Э×1ê¹y°˜|°ÇÊ}é˜í@»ƒ¨ëL;BBz‡D؉uËj‚:9È…HôÌÔV梎yÍ¡/6Ú°#èæ£îj¨N·ªq±øÆ7¾1ñZáùäiO{Ú¬‹ANˆŸüä' É4_\Gó¡³²/•}ìcå¿Ü+­+µ÷¿ÿýéÓŸþôJÃ}Ú°ûá)OyJ:òÈ#çäáâVý„ á?ù“?é핯|eqc…Ë1Ç3'„ןOêèõç?ýÓ?MÏ{ÞóÒýÑÍãM7ÝTÞºQ‚tÁŤm´Q:ùä“Óú믯iNyÕUW¥}÷Ý·ØGÅó|`zï{ß›¶Ûn»9ý`àGñ;ÞñŽtúé§ÏjÿÝßýÝô¡}¨ØÞüæ7‚’¢÷Ž[È £>º4C€I:5ýù¯4DwÞyg!Q L&)ÃG]ñd~~ðƒ 7ß¾‡ k~‡=nË?û³?ë=¿lÿý÷OoyË[Òã÷¸9Àƒˆ†å33ÊçeØã¨L—óذÇ]¿'ºà9ê÷DÏbè£ì¿aÎK —®ØwÁ³ëù1výö8‚0õ÷İÇû!‡’:è ²+ÀˆÊ5å3ŸùL!†/¿üò‰4êç¬ËþkŽ)ê@ °z 0aâhçŒ"$ŒD¼›„ŽuJD¯ºk@IDATƒ­Y¯ 'üñ1uú’˰¿¬–vJl´áPS ÷ý¡üÇØÏoÆcÀ_Ú “ #fg3ØhV{uÉ»vÞ(ƒ\ÆR:!Ú—}0QûÏji7ÒGÀ(ëX|̱NÖíScB$®†&Ž¸àƒØqÇK"þúoÿöoéœsÎ)€¼¥ˆ KNÒ5qÄ¥þéŸþ)ñƒ!Ž•¬xØl³ÍÊ'FHG†yÿpÁ )$fßþö·Ó™gž™¶ÝvÛòÆàç?ÿùéç?ÿ¹ÕÔe?°"‚UM „!Ÿƒùˆ£7Þ8}å+_1dÁò_ÿõ_ÓQGµ ßªp¨8óãpƒ 6(Ý\wÝuå3ºË.»$~Ð!_üâÓ»ßýî¢wÁ¥æ?o}ë[ËqB·Ò}ýë_/«s 8FØ ^ô¢•QðäG7ÿ eåñE¹î¾ûîåx+ ùÏÁœ.¼ðB«eG=cV3DËãÿøô¨G=ª4qÜ"ü—Õ7”Aþ×ýW±×ê >Ç|§LZÚG£àÉ÷"«Y)ÜvÛméûßÿ~ºòÊ+ËÃãÁ‹&qÔå¸]lâÐñ•[1¯uJbڌŮmô…þä6nÅxì µ“Ó±$ݺmª<t’–ÄáCn}Íg]_È@–(ÊjÚ§T(„v6û!?mÚ³Ú[Å4ô­jÜzð7ó7ä(Kxù + jáÂgë­·N—]vYï*õWUn“Á‹¤?ÿó?ŸukÌ´?££žß¤u~q±åÅ'˨ëeÒ/|á Ó_üÅ_”a~éK_J¼v\Ç~€d÷|ÄÈ÷w×[q„¿ÒïMl'¬@™„Ô?œíŸ[·>ð”•8 àÍen«|ö³Ÿ­Û¬² .@@qñƒ°Ê©yKû˜þ X‘Qð„Èå)ò?ÿó?éU¯zÕ¬•€ô á­wÜZä³úýÀç3Å*‘;î¸#Õ·¾¬B[±bEÉÇm«/ùË‹^ÿùÛ¿ýÛrË*6VU±ºjÒÒ–8êŠ'ûóþáz·år+é_þå_&V²)JøÃçGã8nícU•õgÁ>þùŸÿ¹¬®‚@yîsŸ[V!ÙiÉj³Q>/ÃGöÝõ<Öåx°O˶ß]ð„`\*ß»àÑeÿu9ÿòýé~[ e<»žÿº~‚ã°ÇÑÚk¯=ÒçºËñÞ…8õsÖeÿ-…ÏeŒ1†G`ÂÄÏ‘K€‹€Ó„¡." ƒ ÞMb¤ö!næÂfÆÛõ…× %RûïØè½ÎÁXl§¤ÇK©» Ô 9Û©ƒAéœum>ÄŸÕ"æÅÉJìà€ Ñv„\è–ø0.ã©çê¢l*íöçXñš8úÚ×¾–øïÂíjguVÑçûó°‡=¬þñÏyX¿>´ ƒ'Ÿ>3$dN-Í ±šÈjþÀÿêW¿Zˆc±©/n%ޏx…äsˆ|ðìULÜÞpÚi§•6ž‰&iU üÓ–8êŠ'+×þþïÿ¾ÌU_/]£.ç1æÐåx ®–¶ß]ñ¬ûæ{¢Ž[,½Ëþëzþ«ç4í¸ÔcFï‚g×ó_×ïAæ3êq4ìþër¼w!ŽšûjØqvÙÍ>£«&ޏUM2"FòFRïÈÜœ\¥¤ uc,iG§Dð%¾}‰gƒ«0_tlè5¹DNÛá9¨O‰ÐŽN‰8Ž™Ú€¿:hžef`ͤvʠЈ::¥b_äÀîÈ+”>NÄüÚ‰EÇnnl®B@ìæ Ä—ã²Zü‰£Ç3Ô­jÜõÍo~3‡§ò×=÷ÜsÖÅuièó‡UIœ„~4÷{èkG}€`Úc=z·KqúÎw¾s–'h<ÏFñ‚~\û¡í…ýSûªŽ]ÕzýÙÕp[Íçíð_|ä̃Ý%Nê± ƒK½*Œ×^{mâáÛ_7Þxc¶¯ÞOVÿ}ç;ß)9x¶ «>ú ÏÓyÃÞPšÞö¶·õŽóú>cdܬ4Rh‡/È/q©ÿÝ$‡êN¯ýëËm®æ›dÙ†8Ï+Vôž 6ˆtïGë¸]ÕØÖD·'zè¡sÈÐúVŒƒ^I(vý¼t9ŽºžÇÀ¯ëñPcßö{b<í¯í÷„þ‹]vÙ]Ïõܦ—z¬Ãè]ðìrþƒìÄyE,†Ù]÷IG]öŸ˜D«S@ÁÀO°¡Ãs è6”pè~ˆD¥ípø#’OØê<äÇ»¹ŒÉ¦Y<‹cÏ@§ÅrÐî8ða³®_6 –:ñ`¯™–z’$'Â`Ì¥uìÔÕ0uý±3`쥾ÆÕ%ý/Y„?¢_ êlèEY-6sI0 Eñ!æâ9ûì³{·CÃ<¸å‡·-!ûí·_yEÓ=ˆ£&"ƒëÜÞÂÛ¸Ú o¾ãv˜q퇶>õø†ù¡WÇ-†^ÿpæÇâùçŸ?§[ȹ½öÚ«ØÇA±¤ÛµxÃKS¾ûÝï¦ã?~Ö³†š>mñ¬ÿ;ϳt^óš×4S•:ãð9C¬®òæõ…2;†\i#,_þò—{Ï‹bU·´rÁÁ³¯xŽ«¹i¤ q4 žÜŽéó³ø,ýæ7¿™3í~ÄѸŽÛ9ÙP‘<§©)õ-w¼’8êúyéru=1—®ÇCCÛïÏQð´¿¶ßú/vÙeÿu=ÿÕs›v\ê±£wÁ³ËùçRºêy1Ï+b1Ìþëz¼O‚8ê²ÿÄ$Ê@ X½˜âH’pá/$wàذ!’Gêð þr ®P¢.!$·á¥$þÚÉ0bɃ:<:þúÔ±æËÍó‹›ßk¦µŸ¯;8&„® ãC)˜ ^1Žºù§dRN¶nG§ÝÜ?MêöE;¹ˆÓ·©ãNÞZ oÈùð‡?\ü¹(<âˆ#ZÅžrÊ)½çñð< _Å\×殩ÛC_‰Ï,bµÂ3pæ{+@¸Xã"u\û¡í…ÏÊ/G<Œšgs5¥^-2âˆü¬ Ûa‡Ê­Z¼Ú¾),>ç¨noûÙWö³Ÿ-¡<ÄÞUEu.tÆñÁ~°˜!/þñÿ±èõ…òë^÷ºô½ïµ¿»µ^Åtî¹ç¦Ã;¬¬Nòö%–æO‹´!ŽºâÉ1!ˆ°:‹Uý„}EõñÇuÜöëoœ¶šè¨_ŒP÷ÁgŒÏÒ$—º~^†=ŽºžÇó(ÇñHÛïÏQñ¤¯¶ßøNJ†Ý]Ïõü–.õx‡Ñ‡Å³&ŽÚžÿò‡Lì¼Ãì¿®Ç{âÈïkߪÖÜOÃŒÓØa÷ŸqQÀê…À”Gp lp”\„6êl’<DÔጕ³¨ýss+Á®¿ùhW·$"Ç2S›ù‹í’Xú’[ú1_V眇üéÌ(!bŒ’;˜¬ö€B§„RÇÁ£;iV!øÐγ‰Ðé›<:vü‰ÅÛ%›y›yè›b‡ZqÄ[™xnòÓŸþ´¬*•þ|ãßè½vœW×·ºZ_ŒQâ çÕ´È0«@ƵÚ^øÔ3ðÔ Xµïbë]~8{KV=Ö.¸ÏqÞ€ÇRußFE[óÁçú·ÅsÝu×í­ºâŠ+ÊkÐÍQ—¬J{Ç;ÞQL¼5އt"£\(3È/Æ€ðÐmV q%\©LÁŸ6ÄQWCHMë¸mö7îúBDÇZk­•ÀÁÏäY}Kè8>/mŽ£®ç1ðåxï¶ß£âIm¿'Û¤Ë6û¯ëù¯žÛRÃ¥û0z<»œÿXãêÓÅ>¯0ÿaö_×ã½¾E˜bðbŒZûØÇ¦ãŽ;®˜ý®fœunõ6ûOß(@`õB` ˆ#øø7ê·ç ¡IY—¿€c`“ë@·ÍÛÓäBð¡nÂùaÏÞ„ðP[ž_²Ônþ—™‹^ÇíÃ{ÉÄÑüˆÖofå׸öCÛ Ÿz<×ê‘|äÀ‹æÚw±õ.?œÇM9g~s\í¿ÿþÅtá…–‡KÛn9 ž<7‰ç< <ãˆg5…U@<«©ëQ/”ù6+šòö·¿½ižh½ qÄ»âÉ3A¸% i’&¼aRžýðúzˆDd\ÇmI¶ ÿ,DtpÀ鵯}mX°²)ãú¼Ìwu=1ÖQr´ýþžÃ|O0¶i‘ùö_×ó_=·¥ŠK=‡aôùðìzþëú=ȸG=ކÙ]wþ9I‰ð]Ìw²ò€< ¬¸ò¼:ˆ8fœæîWηÿúù‡-–>S@ÉwHÊ@Ú¨Cð ’0ø"”nðøÁ7`36·¬é×Nþp’Jú’ƒü”ØÍIŒýb§Ž`Cðk%´qÖ×AQ·#tÛëÒNQ·1LÞ qÁäÑW2(7•<Ô‰s,èøš/«½ñ؆ Íñ䨆"Žr\Yáâvù¯ø+^ñŠòæ.Ú ˆ¾Âüä“ONïyÏ{Š+ÿæ¡Ã;ïÌÃÛWJG+±è§-_¾¼\¼²¤9üðÃÓ™gž9Ç•L¼¹ë¢‹.*mãÚm/|êÕÄDóGXí7 ½ëçæXÛâÂþãÍl\<7Wß‘³þéO~ò“Þ겺¿aðä3‰(ÞfÆío5ñµýöÛ—g*‘ŸÛHyPñm·ÝVºõ>Ä0«L8ÖkyÙË^–.¾øâÚ4q½-qÔOžÇ„ï=nAC¸øà?×Ûl³M©ó‡ýÀm½È¸ŽÛ’lþ™èxüã_ž¡åç`Ðþæó2ÊqÄJÍaÏc@7êñ@޶ßãÀs˜ï ƶ˜Òuÿu=ÿÕs›f\êq£wųëù¯ë÷ sõ8vÿu9ÞùÂK 7½éM½£8Ç{lzÔ£UÚø3ˆ8fœ]÷_o¡Àj…ÀG’>pplèõ† NC"‰6ã²Ú‹›¹`MþH?A.ü¨Û'v„ºÜI1Ü÷DZ«¯:¹ŒÃ>Ä>ìû¼âÀçuº¯_7;vP”N€b$|t`’7øš#«½UHØ}‘Ǿ‰E°›‡vêÄQrUFnJâÌ¿vû5~Cݪ–ýËEÎÇ>ö±r{u.p¸èáÁ·Ü‚ÁE/ÿMç90ü0ç”ÛÓŽ<òHÜ‹œtÒIåµì,Þ|óÍ5÷Ê ŽzP TêÿºBð`ãsÎ9§<›Õ[ìÈÈ#_?®ýà…ƒã­XÊ¥—^Z^¯m½.ySBÿ¹ƒì‚xÜh£Ò³žõ¬òYá‡Ú$”Üõ‡s=?ô¶¸<úÑ.«H¸MçÄOLguVÁ„çPmºé¦å9D;î¸cIÏëËyˆvS†ÁbâÔSOí­fá!Í<|ÒŠç?ð¦+WÂ@*ᫌúŸ›5ye“,%Ž®¿þúYÏoã…šäêŠ'·ê½úÕ¯îM‘çÅÝ|óÍ镯|eïVÞ^cV|–Ö¸ŽÛ:÷ªÐk¢ƒ·:^rÉ%å¶4¾Ï÷Ýwß^—œ ¾ð…/ôêM¥íçe”ãˆ}8ìyŒqŽãxhû=1<‡ùžhî‡U]eÿu9ÿÕó™f\êq£wųëù¯ë÷ sõ8vÿu9Þë1ò=Íyxíµ×./˜Øpà gíšAÄÑ0ãìºÿf $*@ °Ú 0aâhæ„¢…•Fppˆ$v¸Ûálÿ¶Ó7AÉ!q„‘Ô±NsÊgП~ò.”ØÛ¨c'Î~ÉgnìÎ'«ƒ… aÅ' IDl¶;hëu?´1X'€uJl¥ÀBÑÎ&‰ä$}¶±äeCׯ¶a·-«½ÛÕðA†&ŽâÍ@¼z{‹-¶ :P|@*+c>ùÉO–[•ú9s8K÷Úk¯ÒÄQ?”æÚ^ò’—”W^Ïm™m‘8×~¨/|êžx·Õô>3´qÏþ áu÷ý=È\ö®?œ›ý·ÅÅˆÍøfýÎ;ï,Ï»æškšMåOnCc•‹Ñœ„Ùqò¶·½myWÿxöáØö±þúë—UGÞâÊ¿úÕ¯Ú<5¥ÄQs@W]uÕ,âƒö.xrÑ)ç›ÕšýðßiˆwI–½÷Þ»¬æ×qÛìoÜõšè”{Zû·ý¼Œz {cŒã8Ú~OŒÏiþÞuÿ {þ«?cÓŒK=Îaô®xŽrþëò=ÈœF=Žºì¿awΕœcë•Eõþà8ÞgŸ}Êwö âh˜qvÝõ˜BÕ G¼òþ@^B’ˆ_cC(Ùðƒ„‘·P×O‡ ãì;ü%±èø#ØàAÌAŒmø9ެ’vóɱh§l%tÔV$jèal4zí§6'Uû`7¥ãÑN¡ WÑF¿Gˆãr<” 1¶c£N,%b9ô­j3ᩬáÕÞÜ‚æ-S¶q¡Ë³X½ÀÅ/Â|xènýúqžµÂÅÒé§Ÿ^–³â„UN“Xyâø—R¹ß~û•ÛÁ·V³p‘ÊCŽùÏ¿2ŽýpÔQG¥ÝwßÝ”½rЃœu`)6±®¦Ñ~×]w•q}ôÑY…²Ûn»õVõpÑQД7¿ù͉ yVØñùì·Z¦-.ܮó¾xý¶ÛnÛìªÔy{Ç$ÃâÉê>V{l·Ýv³R2'öÄ®ë³õ³jŸ…t#V9ñÙ»å–[Êw†ß Å.f;óçYCMa5åK_úÒ¦¹¬–OV”±_ë‹ö1yx;býf±ú Gã8nçL`̆ùˆŽ+¯¼2}èCJgœqÆ‚ßím?/ã8ŽXñ8ÌylÇCÛï‰qá9ì÷Ę?Ócÿ {þ«3­¸ÔcFïŠç¨ç¿IWºì¿a÷7Þ¸¬æöcöǵ×^[¾¯Ï>ûì²r—[½/¸à‚²²¶ßþj;ήû¯_Ÿa ¥ÀGðp6ðÔ%n°#®*Â.ñC›qpMräÀ1uüØÔÉK \‰ù¨#‡bŸ”ØŒ1Žº9±Ñ?%b9Sð—Aµß8:pØmLDvë–ÙT’‡‰P’×Üõø$„Ì›Ýz¤6‰"ú¡?êèJ]×—8úHê´âÈ(¹ß›‹-N°7Þxc‚4ê÷ÜcXYÄ[Ô¸ýƒ ô ˆD¦{ÉƸ=m“M6)·þâ¿(øÎ‡í$÷ãå3ÀãøÌ\}õÕ òè·Qx¦ ËÞÙË–-K7ÜpCÁ‚¥­ ‹'+_¶ÜrË‚9·—²V¥ð¬ž+V”. M|3ãªìs1s‹'D;ß™³«|gö#"ûÍa’Çm¿ñÔ¶šèxÞóžWþ ÀgšÛ<†9¾»|^F=ކ=Õó^Uú¸ðt|Ã~O·å(û¯Ëù¯žÓ4ãRs}<‡é§öö{°ŽEï²ÿ†=Þ9Go½õÖ‰Û˜¹Å~¾ßVƒæ2Ì8'±ÿ;ì@ 0&L=!ÏZ~BâÅÕFòp\¼QÇ?6›ºxÔá!$…¨oN}ŒÍ.=¾df%ÊJn…>å_ð³N.ú±Í\´Ã“P·ÌêüB²aÿ:†N e- €ÉSÚî€iNÄÆ8Iìõíh´K›uÇF 1d›íÖsSi'í‡}dâˆ$!@ ôCr„‡BóÛçñ<«úìýb¶4¨‰oSv&ñyY‰Ø8ð\™-´@ @ X*L˜8ò­Upl-ð ”ÔÕá $t²ZÚ¨c—°‘ÑÏ\ø±8 É$ì´»ÕutbjùâåT´QÒoÝg®.,´Ã`X-䀀†0 tÚ‰Á©K¼NÖXë´k“äÁ†8Jˆ%‰*ên<«Š°×qŽy‘ÿœ¼…@ 0X–ÏsªXUÃ-qÌ-§B?uâåT°Í+tÒVìL:ub ˆvó9 }iÓ¿öÅÆ`‰ÃŽè+D;¹ƒumÄ©K&9ìèlÄ"Øè“ºíô§=nUË`„Àxh>`šÕGÇ{l:ᄆzÎÍxFYC9$tÐA¥Ë=÷ܳܚ¸PÿñyŒP<g‹–@ @ X*L˜8âV5ø¶&i$ï#)D)g?"ùƒÝ\u>òà'!'Bx J_b탺y³Ú‹7–vsÙ76ľfj ü5h·ÒŒ/›ƒ¤¤.Ñ㤬›Û2»IêŠ+~ˆEð!ŽÉéO›¹ô£aDéx¨#úo>JíÄh÷JAePB@`<ð¶0VñPd"ÊðySbÈê·'òX¤í[óâó2øsÑÏÁÙ¢%@ – &޼U ¸$ŽÐá>X_È…PJÐh³Ý:%œ„«Šà1Œ1Ÿd±òÄÁa°á‡bR_ìê¶SWèŸ|ð!µÝö9%IÚ É`ØŽl#—Æf;1ü‚ˆ¬ö'uJýÈáDÌC.âÙÐݨ»¢ˆ8ý°ÕãjúH8£Ï8Ê`„@ @ @ “F` ˆ# #yù ¸ìøÈC KòÐæ–ÕÂeÔmrøH$‘›¼v|yòÃsІ8lˆý9s™×<ú– …þ<ŒØƒBgtìÀ-ikævà–Ù¥qɱÝ>°7møÓ.ˆBŒ»@oNêHm7Îò{3.ñ7@ @ @ &‰À„‰#nU“§€g€Ü¸c,ÂF??u6t;\„ñŠ9ÍK¬þèØ‘:íu¬ýÍxÏŒXÇBÿÆéKÛ‚âtÌ’7ú:I;f0vŽvźíuÿêÄÓ¾èØ©S§žÕ¢»’¨mØh7Æ:¹ØÌoq«Z%$@ @ @ ˜4S@Á!ÀMHôÀ ³Ñ†@Æ K ¡KòÀE¨‹N>|ذQ’O.Å6üêÄ‘‹ ;uÄxtrÈ¿àÃfNôÚ7WÛ Ã ­'ˆî`ÈEÄa“`ÂVoú €±ädrÖ-¢îSòÈùІ¿1Ø]U„ÍvJÚ´õ@ @ @ @ ˜0&Žž§_I§A 9SÛ¨#´Ã_à#Icœmu\vë‘?øÑFB!®B»c€ÇІ¯¼ínu¹±[fµÄØõy…À¶BÇl$§S‹Né <>ä57¥vüœ€>úÛ–]zñÄAá‹~l<К!d;vƃ¥ö¬–z]ê£?mq«(„@ @ @ F`ÂÄÑ“òôáØàîȃœˆ%<bÝRr‡xtbå3𡎠;%ýP"ö…/¼…ã°¿ÚÏ\–Äãg]:bÝ3Ö Ð<ÇŒ?8êL€‰`³ó¬ötíÆQºúˆXŠùÜêz6?üâͧn[‡Žœˆ>NÄ» ÉyqJ!@ @ @ À„˜0qÄ3Ž$x¸ MQ‡Ëhnø`ƒäg+!>‚R$«…—°Ž?<1’D´ÁkX7§¾ø#”løšx8âéŸÜñˆñ3µuÐ<ËL§J¼°ÄŒ“ÃÏÁQ: íæ¡MÝ~œ$môA‰ÔíÖƒ«È8ˆÄ¥¹´ǦoG @ @ @ L G»äéÃ-@¶Àq¸IìÀ/¨Ã+HQ"؈±n.ìÄÉ #øCi;vuJ6bà1< 6ÇD¼œ þæCÇnž¬.,·•zÄÔƒœ¡s_ÄúLmf°ôÉäˆwÃæ$ÍK‰ÐF>êN_Ø)«å¶5JĶ:;¥vr’ÃÜŒ)ŽA @ @ @ &À„‰£òüáà ø68ø ì”øÀ}І ;¢’8J6VÉM¸‚)›J%¢þŽƒ¼r!ØéOþÅ>³©Ø‰A?ýi׺mY, ö˜ÝROÔ'K3h:77%6†ntü&îÀt 6s›—8rÑF©¸Ú—6V"1&ý%›íßÏzH @ @ @ L G¬8‚G€«€Bà°¹a³ ?Úáx’‚Úä= …ä#´ÙWnêùÙŸ>´!ØÙÈA\]‘Ôùõ¥D1­…äÈHäÐ16&ÔmØÕ)‰ 궑ǺäuHÚØØJrÐîX²ZV€}X⋎‹NNÄ6lñVµIü @ @ @ &‹ÀGpp”lp2èp Dð´QÖº¼…6I¦ìVüêü5¿A^8 âà.ŒÏjé›:\I½*‰6„<%±Í1PoæÌ¦ÁB@[aÐtìf`1hr9)óâ‹^×sµäü¡ ?½›Åß|”ø!€…GI B‰Í1P²§oV‹-nU‰@ @ @ @`ÂLqÇÙ!ïa]"‰66íYí‘;ÄHÁCX§D°AþÔB<%y‰W´S¯s‡þ)íÇqeSÏŽnz+!á0â !\:0sa£ÉQ"Ø$x¨‹h§Nn„<èÖi3·ý’ ñøa§Ä†¿$“äT6•ÕH´77}ÈqŽ!@ @ @ @ 0Y¦€8‚Œk€ß@‡ƒ€spó–0ÚÚñ%ëØÐå1²ÚËŸ¢¾ö WA _r(ôK;qÚñÆ8Nl>Ø_Ý7í}ÅÄ}F#tHuuê ÔÁeuŽÆûvÀĨ›ß agC°‘‡þ±SG—"›þY-‚vâ𣎠Ç[Õ ñ'@ @ @ ˜,SB BMy‹˜D>pHúÂ3`‡¯@СMݶfI;~lä ]ÂȼØêèÄá+ïn½ŽQÏ̓Åäƒ=V¶èKÇthÚD]—¸! qqèuVa'Ö-«…à¡Nnr!=3µ•¹¨×„qúÒmØtóQw5Tܪ!@ @ @ À„˜0q´sž>$ŒD¼›„ŽuJDƒ­Y¯ 'üñ1uú’˰¿¬–vJlr*Ô‰3†ºBÿø!ޱŸßŒÇ€¿´&AG ±3Èl”6ë’3 ví¼Q ¹Œ¥tB´;.ûø©'j,¤€QÖ±ø˜c¬Û§6Æ„H\q4ƒGü @ @ @ &ŠÀGðòèp Ô!wlµmÔá&$€¨CÁO`£Í[ÜÌG:¾r+æµNI,B›±ò/ÔÚjŽ…ÜÆ¢Ã¹}A1ñ‚ŽÙÁä’3tˆÃÛæ (í'iI>’IæÒN©/ä KeµíÆSªB;›ý“6íY-uʸU B@ @ @  #0aâh§<}¹¸8 6¸I!ê’0Øhc“¢©}à$¨#øÖ9°Ñ¯AJ¤ö7Þ±‘Ý>ðg,¶SÒq–Y-Bì‚B`[±S%‚Ò 89êÚ |ˆ5>«EÌ‹:“•ØÁ¢º¤¹Ð-ña\ÆS7ÎÕEÙTÚíϱâÄè„@ @ @ F`ÂÄ·ªIÆ@ÄHÞH Îyƒ[€“€‹ ”´¡nŒ%í蔾Ä×¢/ñlpÆà‹Ž ½&—Èi;<uã)ÚÑ)Ç1SðWçͳÌ0µÑNºQG—”Éê¬awäJH'b~íL »¹±¹ ±›ƒ_bŒËjñ'ŽvüOܪ–Á @ @ @ &ÀGððlèð:¤ %œ:‚"ÑCi;œþˆä¶:ùñÁn.c²iÏâXà3ÐéG1†´;|جë—Mƒ¥N<Øk¦¥ž$ɉņ0séG;uuL]ì ;B©¯quI¿ÆKáèWçŸ:ºDQV‹Í\LALH @ @ @ 0a¦„8’ä ø É86lˆä‘:|‚¿ˆ+”äJ(%€ä6°!–’<òÚg¼fÆ@,yäEÐk~…qʵdµˆù¬,Ø@‡ª¡Ÿ¯;8&„® ãC)˜;ø‡n~ã)ÄÉÖíè´›â§éOݾh'qú6uüÏÉ[H @ @ @ L)!ŽàØà4(¸ÈJl”l’<DÔጕ³¨ýss+Á®¿ùhW·$"Ç2S›ù‹í’Xú’[ú1_V眇üéÌ(!bŒ’;˜¬ö€B§„RÇÁ£;iV!øÐγ‰Ðé›<:vü‰ÅÛ%›y›yè›bcÅQ!$@ @ @ ˜4S@ÁÀ7¸Q¿=oðMâȺü›\ºmÞž&‚ípæÉjá2ÌC,ܱø™7«E¨3>?üݰ“‡’¶fl6 “öXÙ¢¯¥d’NÞ Sêã ¬ãË )%k²Ú[„n ~±ø*¶SÖ}™O_ÚÈAiLV‹²&’ˆÁ'Ž 2!@ @ @ À„˜âH¾CRÒF‚‘„Á¡tƒÏÀ¾›±è´¹eµH¿vð‡ÛT’ü!/9,±›“ûÅNÁ†à×J h㬯ƒ¢nGè¶×¥:¢n;6b˜¼‚À(cô• ÊM%uâ :¾æËjo<¶aCgs¼Ä@ 9¶ Ž2!@ @ @ À¤˜âHÒή‚ ½Þ°ÁiH$Ñf\V{qr&5ù£N<ü¹ð£nŸØêr'ÅpßÇB¬¾êä2wøû°?ìóŠŸ×é¾F|ÝìØAQ:Jˆða`8êäÂ×Yí­BÂæØè‹<öM»$íÔ‰£„ "7%qæÀ_»ýš¿¸U-ƒ@ @ @ L G»Ü78 VÁ #ðv¸Ûálÿ¶Ó7AÉ!q„!¿utsÊgП~ò.”r¶QÇNœýÖ¹±;Ÿ¬‚†cœ€$y°Ùî ­×ýÐÆÀ>Ô)±!”ëª |$‘œ¤Ï6"–¼lèúÕ6ì¶eµw»>HG38Äß@ @ @ @`¢L˜8Ú)Oþ@^B’ˆ_cC(Ùðƒ„³`S×O‡Òö¬öâì‡8ø JbÑñG°Áƒ˜ƒÛðsY-:%íæ“cÑNÙJ設HÔÐ)ÂØ h õÚOlNªöÁnJÇ£::B @®"¢~!ŽÇåx(blÇFXJÄ2nU›Á#þ@ @ @ E` ˆ#x¸Høê7ØWa—ø¡Í8¸ È&9 rà‡˜ƒ:~lêä•1ucäPì“R¾…’ _b̉þ)˙ڀ¿ ª­þÄÑÀæ„hcpø; kÝ2»äa"”ä5w=> !óf·i…M¢H ©£+u]_âèC")V‰V”@ @ @ À˜0qô„uxI!êÆ›Sc³K‰ƒÇ !‡c“ÁŽ:¹ð±Í\´ËãXfÓüB²aÿ:†Np‡0yJÛ°1͉`GŒq’ØëÛÑh7–6ëŽ@m³Ýzn*íä¯ý°q !@ @ @ À„˜0q´ó}Ó‡Ë`ƒ|W ¤®!¡“ÕÒF»„|ˆ~æÂ‡<ˆ}ÀaH&a§Ý­®£SûË¿/§¢’~ë>sua! ­0ËÀj!‡ÌaC:íÄàƒÔƒ¥N^'k¬uÚµIò`C%Ä’Du@7žUEØë8ÇμÈNÞB@ @ @ @`ÂL˜8âáØð’<–p ·çMÞþ>Br?Û²ZH%|I!|°K2ÙN<}ÐæFù¸ V>Éu`#1§mÔ7íæ¥´ û¼‚c[±ü!_&á€ÐÚ˜¤erµO®–‰3Ú!' ÐFâh3/ºã±ýiâˆ6Á¥®$ùÉCÜßÏ[H @ @ @ L G<7€cªuÚ!~ä0à$†Ðñ%>‚:¾>óˆ:vÚñc3Ÿy̯‚?bn¸û0·œŠÜuâÉ[çÌÕþB'mÅÎô—„!Ñn>¤/mú׾ج9ð×Wˆvr‡XêÚˆS—Lr ØÑÙˆE°Ñ'uÛéO{ܪ–Á @ @ @ &À„‰#nUƒ?`k’Fò0løPÊYàHþ`7W<øÀIàƒž‚RAÇ—Xû nÞ¬öâ¥Ý\ö ±¯™Ú ZÀ­4ãËæ )©Kô8)ëæ¶Ì®Å_’†ºâŠbI&§?mæÒ:„¥ã¡ŽèO¼ù(µ£Ýg(q”A @ @ @ &À„‰#oU‰#t¸VÁW r!”4Úl·N 'áª"x cÌ'ÙD¬<qpløáƒX§Ô»ºíÔú'|Hm·}NI’¶BrX'¶#ÛÈ倱ÙN „Ž@dµG8©SêG'brφîFÝEÄ釭WÓG‰õxÆQ#$@ @ @ ˜4S@IɃ@øÈWÀE`ÇG]’‡6·¬.£n“óÀG"‰ÜäE°ãƒÈƒžƒ6Ä1`CìÏñ˜Ë¼æÑ·-ô‡àaÄÎ:“ cnI[3··Ì.½8ˆ&HŒíö½ißvA„bØÂxsRGj»q–ß›q‰¿@ @ @ @ 0I&Lq«š<<äÄ ƒd6Úø ü©³¡#ØÙà"$ˆˆWÌi^bõGÇŽÔyl¯cíoÆ{f Ä:ú7N_Ú° cv¼Ñ× HúØ1ƒ±sl´+Öm¯ûW'ž¾ðEÇN’8õ¬Ý•DõxlÃF»1ÖÉÅf~ûˆ[Õ2(!@ @ @ À¤˜ânB¢î62]R]’.BXtòáÆ’|r)¶á‡P'Ž\lØ©#Æ£“Cþ6s¢×¾¹Ú^Vh=AtC.êø  ›¶zÓOŒ%'“³n)uŸ’G·6üÁîª"l¶SÒ¦í쬇@ @ @ À„˜0qô„<}øH8 JÈ™ÚF¡þIãl«ã²[üÁ6âêqpÚ<†6|å=hw«ãÈÝ2«%Æ~¨Ï+¶:f#92XtJÁàñ!¯¹)µãçôÑß¶ìÒ‹'¢_üØðcãÖì$!Û±3ü(µgµÔëRýi‹[Õ@!$@ @ @ ˜0&Žž”§çÀpGÞàäD,á!ë–’;Ä£+ŸuìlØ)釱/|á-‡ýÕ~æ²$?ëêÔëæ˜±økЀæ9füéÀÉPgL›gµ§k7ŽÒÕGÄ:PüÈçV׳¹øáo>uÛê8tìäDôt"ÞUHÎ#ˆ#P @ @ @ &ŒÀ„‰#žq$ÁÃmhŠ:\FsÃ$<ƒ\ 1ð”r Y-¼„uüá)ˆ‘$¢ ^ú9õÅ¡dÃ×èÄÙOÿäFˆGŒŸ© ø«ó€æYf:Uêäõ€%fœ~ŽÒIh7mêöã$i£J¤n·î\}D.ÀA$–(Í¥86}ƒ8™@ @ @ @`ÂL˜8Ú%On²ŽÃMb~A^A²ˆÁFŒusa'NÁ×JÛ±«S²á˜àI°9&âåTð7:vódua!¸­Ô ¦„ä #ø"Ögj3ƒ¥O&G¼6'i^J„6òQpúÂ&HY-·­Q"¶ÕñèØ)µ““æfLñpì BH @ @ @ 0i&Lí”çW€ÀG°Á)À_`§Äîƒ6lèØí”ÄQ²±òHnÂLÙTÚ(}ðwä• ÁNò/ö™MÅN âøéO¾ÔmËê`1h°Çì–z¢¶8Yê œAÓ¹¹)±10ts ã0qî¤k0°™Û¼Ä‘‹6JíÄÕ¾´±‰1éï8(Ùlÿ~ÖC@ @ @ @`ÂL˜8bÅ<\¤×€Í ›møÑïÀóìøÓ&ï)$¡Í¾rSÏÏþô¡ ÁÎFâꎤί/%Âøˆi-$Fì@"‡Ž±1 ¤&€lîNI¬Q·<Ö%¨CêÐÆÎÀŽP’ƒvÇ’Õ²òìÃ_t„Xtr"¶a‹·ªHâO @ 0$m”ÖÞu·tçE¦{/»lÈàp@ @`.S@ÁuÀP²ÁA@Ê Ã-HÁCÐFYëòÚ$™²[ñ«ó×üyá(ˆƒ»0>«¥oêp%õª$Úò ”Ä6Ç@½™3› m…AÓ±›q€Å Éå¤Ì‹/z]ÏÕ’Cò‡6üôlNóQâ‡b%1l%6Ç@ÉFœ¾Y-¶‘nU[ówŸÖ¸ÿýÉUäîþgJ·ßnuør­µÒ|`‰»÷ç?>>"f#°ð\c‹-ÒšÛ:ýÜžîþÏüùi)ýâÖxä£Òš›n:'Ã=?»:Ýû¿ÿ;Ǿä ë®›–=îw˰ïþ¼Xðn¾s—¦ôÛ«|&Kàx ƒ%0¿Vû=Ï#åÏú÷_7Ýûë›RúÍoZÁ2öóQ‹^—í´SÚêÞ—~yÒ‰é×Uï²óÎ;§=èAs:ºòÊ+ÓøÃ9ö0@ Àt!0Äd„¼‡u‰$ÚØ´gµGî#9aÁùS mð”ä­/t´gsi§”ã uc‰G´£Û7z+!xq. €€¹°ÑÎä(l<Ô¼vêäFȃn6sÛ/¹°vJløK2INeSYD{sÓ‡çàØU¶>ë;ieN9¥ÿ=èeéžÿ¸kº´ÉQ›6Øu×to¾ ¾âiOMk?ýiùï푽;ýêÈ#óì™þlYãáOx`FáÞŸ»øì„€@Ït‡äðôà³Ák_›6Ùoÿ2 Ëw}rÙmF×/îÁÇ—Ö}Ìcæ„ßtÆéú·¾eŽ}©–í°CÚêÿT†}ù>{§ôË_.µ)ôÆÛoÿõW‘²އQ¦¾æ7h¿¯±ÙƒÓ:{î‘6Íßå÷{à&³`à|pý)Ÿ›!fúœt÷ùȼ}˵×Nkn³M9÷lôÔ§¥Ûój£ëŽ;6ÝýÓŸ¦{ñ‹”nʄטe—]vI/|á KÖóÎ;/}âŸèÛç>õ©ôð|^lʹ瞛;ì°¦9ê@ @ 0eLqÄ5\ü:ápn>p ^xÓŽ/1øXdžŽ/%bJE?|ì’:¾äPè—vâ´ã'1aØ|°9¾ºoÚûЉû66Œ$Fè8êêÔ¨ƒËêŒþö퀉Q7¿ÂΆ`#„ýc§Ž. D<66ý³ZìÄáGAé­júÀÒz;ü^IÆŸËŸó)ýêW½ú0Êšù‚ÿaù¹zÅŠtÇW¾œ6ÉdÑ»í^l—ïñ”ÌK6‰É<™=öH}ç»f|žñô”n¹¥è¿íúáYc²á_üEºÿvÛ¥[óÈÍü`Ý´¨ú  É…Ñ/n£7¼!-¯>ël½uIÄÑBh.~{¿ýWbÜŸÏ…Ž‡ºï¥¨/•ùõÝïyÕê6§kAØo»ä’tÍËÎg^N½seœç£¹ÙWZî—ÏI}÷»ó”Sh¹üÉOêß0‚õ ƒJ‡rHÉpþùç÷ôfÊw½ë]iûí·ï™7ß|ó¢qÔƒ$”@ @`ª˜âHŒj∋q8‰|à$ôåGvø ÝÚÔmk–´ãÇFâÑɯa^lutâð•÷@·^ǨçæÁbòÁ+[ô¥c:´í¢®KÜ…8„8ô:+ˆ°ë–Õur“ ‘虩­ÌEòšC_l´aGÐÍGþ‘‘nU[þ²—¥Í^>óC–d—ï¶[žûkHYsÍôÐ/|¡ü§ùŽ«®JWï¿_FàÞ Ž†„±ç>Ï^{V¶øä¿¤uñˆtëù?J×¾ò•uÓ¢ê}/$[Œ MÜþþ´æzë¥ ŽZºÈ. í¿±~>[‹<ýñv·„æ×w¿ßGÝyݵ麓îüϤ{ó? ÖØpôöŸ”6ÿ?ÿ§·²õ3qrÏ…ôÅol磾ÙgŒËž°sÚêïÿ¾¯Ç½wÞ‘ÖXkíÒ¶*ˆ£8 ½6¯ÐDÎ9çœôú׿¾è ýùÖ·¾•ïü[7q´RÑ@ L&ŽvÎ(À!pQ/oÁ†6ë”vø ·f½&œðÇÏpÔé‹Üèpö—Õb£Ä&§B_c¨+ôâûùÍx øK@[atÄ€;ƒœÁæ@i³.9Ã`ùI)ä2–Ò Ñî¸ìCâ§ž¨±>FYÇâcŽu²nŸÚf~ÕÎG´D-{âÓVG¿'§É‹ò3‰®ú£?,ú°ßoþº™ÁW¼øÀÞCFcÅѰHÎø³Î6Ö ó:ñzß É9ÚÄqÔÈ ¹,´ÿÆùùls “´¤ É$si§Ô‚%вZ„vã)Õ ¡Í~ÈI›ö¬–:åH·ª­±åCÒÖŸý,yÒ¯N;5ý*/‘Zò•·ùÊWKØßøzºáïè¥;qÄ­y%ÓÔË(ãœÏzÞã¼0¯ó«º\(O›¸ ŽBqrí í¿±}>[³åø›•h*]æ·ÃÔÅBû½_Ü{XÚúÓ'–¦k=&Ýú±õsKc9õÍ|Ÿ1¿Am›/¥TÊÊØý^˜óáØÏ|æ3Ó;î;?~éK_JÜ’ÖFF%ŽÖÌ+Úî™çÙRmÆ>@ @ Ð G;å‘Ê%ÀEÀiHÂH Q—„ÁÆ6›ÄíHí'AÁ·Î6x rP"µ¿ñŽèö?c±’>ˆ³ÌjbÛŠ:(p”NÀÉQ×áC¬ñY-b^üЙ¬ÄØýÐiGÈ…n‰ã2žºq®.ʦÒnŽ¿‘ˆ£´N~6E^\ŸGtÇ—¾Xôaþø@WËŸñŒ”n]ùŒ¢‘‰£ûÝ/­õ´§§ŸûœtÿGýiYþOðÝù¡¥·ä[!nýÑÒç_îùÑõnþÏñzûïŸ6zæ³ÒÚyHºë†Ò¯Ï:3Ýt§ҽW]97fùò´<û#·B¦Ý|sZ#ß ¶ñK_š6È·[¬™ÛïüÙÕé–ü`.Á6Ê8#™ÏÚµÓ…y¾ ¾ßî»§õó-‰äWP/ËÝrÞÓÍÿþïé7ùVCæNÄüÚ‰EÇnnl®B@ìæ Ä—ã²Zü‰£Ç3Ò­j9OÚú̳ÒùÂk¾gOà×Of=Ðõ¨£Ò_ÌäC%#Gùv‚­Nù|Z¶ñÆUÆÙêmùÂìš—¼x¶1×Öȯ2~ØçNé=W£épÕÿý¿éÎo~c–yü{ëO*¶ÿÍÏ Z¾Û®ù Assã0‹ aœ³+óâ™/äÖÊ$Ø™8@¶|Ë[J awuŸÿ\ß™Ÿ}4ëöù9$:úèYCŸIp_šü|Ÿ¾æ5éžÿêCÄåÏÇÿx¹8®cšú,\hìw_âa‰£.û=uÀ¥&€®Èéëfb³~V˜¸”Û?yÞ×}…ow×õ¿LWî—㚯1Ï„[}æ³ó·_þ“ô³½È!¤ÎŸk2 ³ÿ²ûHŸÏÞˆW*óºu<þFÂ%¿‰rƒCM›üñŸ8оååOÙ=ŸU8­ô—Vó#tˆþJÎþPéðÊ|+ÔCW¬èu~oË{ï¶:餴,¯tR®~ç;Ó_ž!TŠm˜ý¾À ÐçÒÿ^{æŸ œöúË(ç£þ+ëšËÒÖßþvï¼pÝñI·ÿ÷çe¹òVµÊ}Üêžð„YôÑ~43à–½f¿ÃGeRðä“ONëg}\•ŸC¸ï¾ûj{ @ ŒˆÀGððlèþ E‡´¡„s@GðCø‘F¥í\|bC𗫨ó|ÍeL6ÍâY |:ùcÈA»ãÀ‡Íº~Ù4Xêă½fZêI’œØ™«î™ ™K?ê ’º:¦ŽÆŽPêk\]Ò¯ñ’Eø#úÕ¹ð§Î†.Q”Õb3—ÓÈÄÑ~p~šÓÚéÞ«ó3îv?ÒÝÂ]?ÿ¯é~›nšî¼æštÕ óÑÆrøQˆ£õó…™Ä à×îätGþé²ožÖÛiÇ´Á“žœn¿üòôóW2{ ù¿«[eÒÈ £¾ð¯éæü\‹µ·Û6=øÏV¾ÊøòÆÜê œ:!}“cý]ž˜–?îq¥©&H:³î}!<óò6ß:£5°ÎEÑ-ùH¯}ó~¬¬PÁÀÊ©kóEË=ù¿î<õ©iãgÿAÏïŠ}_0ûù™°Úäȣʪ œ¸ø»éÌo§ß\tQZ{‹-ÒFOFëVªtŠëdÈG÷{\jˆ•l÷Ë+nùõwÎJåUrÈu=>ÝrÜqE*îcM·°DÉäÁùõݬlAî¹õÖüÐðo•c`íLznüìg—ûÎG™Šr¡ÏõÐû/‡Œòùüì ”]E™Ç«;dϲïévDñ¸1.ÓlÖ#‹ ¨ * à6À#2‡Å"a‰¶xÆ0‘u𜰠2(Šx–!éìBȾô|¿zï©w»ßë{ë½t2çÜïœÛUõÕ÷}UõÝ{_¿ú¿ª¯Â¾ù|OïCY!öý‹}ßiv¸ÅÄzÈ¡¾¼oè²þñ'ìÝØêúÛé’#?û9¿B±&p”q|yÛ #ßÁ*è7?@Û”ðÞ&Mx1ÙþG°ZZtÞynûžKª»ËDÿ?êÎX7¼~ÇëÆ_|q—šÕ÷ßçÖÝpC~#ûÛÿ[n¹Å›üñìfÏžÉ|àˆ¸IG}´·»fÍ÷›ßüƽôÒKnŒýŸ?à€üµÌâ7}6—3u¢*}º=!¥Gdè?·%&rÒô惺mO=åÙ}ì—îI3®óù ¶¢¨ã<Ðlâ(êgÀÑør Öô)mƒìéÑÓÎò¢›^yÙ-ÿêW+¶QU‹y«§>‘æYqsßcý@êorÙ¶Æ ›0·X,•&»Wþò3§z±J¾ã¶™nƒ­²ÙhÈW¾âÁ½°/o=û{·ê¢¯Ûg½þ퇵Éÿƒmñ5k–7võÕW»_°•5åŽæÍ›çFÙŠ[èŒ3Îð QØD“=›Ä<Ú^cÕW(_ä (þ 7±¸µÝ~‘ßaA³û{œoAt¡%—¯2è²ç:×p«W6™$Y£¨û^‡_BȯÞ8óÌŠg…ìe1¨ö:ª´eDÏY¬ÞP›Ì?áDï—j`Eà(ës݈û—ùùLîöÛ™<ïCìûõ¾ÛÖ®V‹ íØ´ÑµÛ6ÏÊ<¾ˆöBàhÑ_sÛŸ}Öw±õÉÒêÃ{Å´iž7À{þ×à¨÷ÝÙñöcn½Å Üç=¾ ‚½¿yíµ1nÚù:¶"ðïlû܈O[<±€VÝ=Û½uÓM§1Ùq¶µW«Œ.¶ÿMóçÏÏd8pôi•/ºè¢Än‡ýð3ÛâúÈ#¸µk×&ü"Sx ð@áÂ… ì<ìÀ“X0 2€6Êð@a4á%Õ….rà ð¤Kž:]–õÔ]=ȃmTë,6°O _6ÑQ»ð)Cð ä2‘²KV¢¬†È«>ì(õ”!åU¯à0xéHV`Uy;”ÑS_È#+{–Mú£:xä¹Ô_tÔ·^ŽÂ€®Ë®½Æm®±Ü> pÔ×Nåš`¿¾Bq‡ìKý0 bžÚå…ìÏ?äÖZüžN‹ÕRxzOȯ–_pp[²Ý'œH¶ŸsNõÓÚÒÆ"úšÈãÏP/óÄnŸ8|äTRà(ës݈û—ùù¬FE€øž>_¼jäûó¾|½åÒoØÉuo˜š—ò¼ï1í…ÀÑ ð/ð[ÀÑ"!·?ý;ßí4pT÷}70väŒnÈ?äí¯þù·îºÒÙ¼~ê-ù>öcÀ¤ë®ïÒÜ‹cµõWÙVuQ®Â1b„{жC^x¡{úél;ÍóG,ØÿU¶úñƒ6®4=ùä“îŽ;îpY%—6V” (`L¸È‡<0 d!ê¤GYzàuÈ€G(>ed‘£Œ eøeø²‚Ÿ–•²ÒCu¼¯#øëºÇ0:w¸5Ó§Û#È­6§§ª-´-¯¾âùYÿäégb3§?=Ëdž˜ÝÓµ–·DÄ÷ ;A©;jšÜâZÊÛ(–ßø#”w¸}­Z ß €äcµ'·ÓÅê¥û•8Šºïƒ‡Dû%Žw¬íÅYîº W ueÖ VFÕZå2Ö¶¤°5¨VŒ£¬Ïu#î_æç3ô\ïCÞ÷/æ}oš8ѵÜ?Ç÷8jUJÎñÅ´W}ôíÏLGá3Žê½ï{þËÅn˜ÚíÖ+|K½n[7¿ö¿~5é­ÌJtv¨mQüµŽÐ—íð‡çŸ>“­<À‰ctàºsìýöÛ¯Kl‘»ÂþqŽº¸¦`(ijmu-wßãóŽqÿ2?ŸÁ@êy3ß¿¨÷ÝŽ:oµ çPôAŠ.y}„"è¨9õò>OJ½ì cŸ4ÑPVPC£à Q)‡r’§A…2ðeƒTýŸ2yˆiu´ p©_ê©ÕãŒ.)¤´÷¶ªÙ„¬õá_ùÆ×=ñ„[ýíoù|­?ƒÏ:Ë:ó ^d¡ÅÖé\¾¼‹xÅv¶2ðÐE(dØ„ Mc¾l#Îø|xQÞQIßj•?5úé5"ü¶4ÚŽuôÞýª‚ ¡ì$»o}¬=hÁa‡:·qcXíóæOw{þIŸ_ø™S\ç¢ENža. –Õºu¾Þÿ±ñ›m'Ø•ƒn{™òý‹Õ{Ûx)—8нï±~éUàÈ\Ñ2¾kê[úØèàÙQç“ÿc^räùæöv·ìÔÏ$®Œy®qÿò<Ÿ¾³u¾É€•éáý‹ñ‹Ge÷âQZÍè=Q“5Ó˜ñE´Wp}ßÍ×t²@Ó¬ô\àR%]Mí‚Ê4p4þþû]¿‰“|OrˆcÓ.èUe“õG²Ä6V bq­ ¶«M›6MÕEZx ð@áÂ… 4лpŽvhAYÀ |H«Šà#'é]6 Ó 9H6(#Ç¥ÔñþÅø…~±É÷ÈÏŸNÖùÕ]ŽwÄzàOÔøL?o{õG±÷½©õío¯?0×­µÓÊvK²’,VÛ&â •tH€£9÷»õ¶šL‡'ø@òe¥‘c?~¼9r¤{ñÅ3oË  2ù ƒrÛí-[øžWI“l;öœ9s<óµ×^s§vZ¥@Q*P†àQÆ<ÕÉõÂq”«6a,!êШ:Ú¡ žTõê°tÒIGƒ„Ïò žzéR§²úFŠCU§z•­Ê×c?”ƒß+ÀQÐuù ×»Mö«lj²/È-¶B´Ôbmùµ­Z²_¥Ý°an¼€ÖÏ–ÒC‹,ŽÑö?<çó2ˆé²ÂN·ÙòÔS®ÓŽJöGÁâ†[€×¡‡êeÓ€T¸ú„­K,ÀöÖ?þÁo“k²mWý?ö17ÚâK°ÝM±g03‘Œíg¬?ý€Ëúq„›pÙw}‰U&Ë/¼Àu®YãœMú[@ë§žæ–žr²=UöÙ{²­:j¶:hµM’ÖÙI;ø³Ï»ßí&þð‡ÉŠ–E´uûïK Ùšÿq_7yæL¯ÃŸ7Û½xåU7úüó]‹=•&G±zi{Ž8V|Y°Â­ÓV>¥W¯EÝ÷H¿ô6pÔ÷ˆ)v¯/KܳÔâ“ìXÿ–÷­o'[ “JËÔû\7âþåy>c߇Ø÷_żïÞÇöÌŒµÀü:j nù5׸m/¿TúŒ;Î >ô·—ݳEl+46v|1í5ïkïìÍ?ñªás%ÆQì}Odܱ~½[nÛ ªQ§­DÚúÈU«Þ©ü¦½÷v-?µÏ=£M¯¾êÖZðkVY°ÏÀ4UýÑ!-˜£ÜÖÖæ®¼òJ¯ñ·¿ýÍÉiŒHÀÑû|ÿ¾í½aÿ±#ÚgŸ}ÜwÞi‹½6ûmh=ö˜[aŸm…)`Õ7-à>ð/>×? ˆvA… (õºÂ2ytByÚ@}òjeÚ Û´bÏ„BV¢34FÃ!aCÈá‚èyêÑA ;K»¬tU¦^<<ð õ‡`I@e9‚¼ôYU?ÔSßö³Ëb‚Ñd«s&Î}Àía_<}Œ•㎳ÑëYêÙj×£štÇm3ÝûÂ+Ò„Påjéû‚ÜNpâò¯Ç’ë;åH7á;ßQ±jN¬b&’Qý¬ÓŸÉ`ìT°±·ßá¼ó +¿ù]ïr“ Ò–§´,å5sáÞ´IpHÚrò”ï˜y«.’Ó­†±z²M*à(ä‘gÒ·üôϧÙ.æ¾Çø¥·#VÈMœ÷ËädµôÀ_z©ë×2Ùâí«Âíˆ1Ï5Fê¾YŸO[½ûùõþ•ë¯nc7k–ëßú޲µîPÞVLÆŽ/±š£=REG´sß÷hks¯øAÒÝž2áçRO²¬£Zv_z‰Û6~-‘¨:@£6ó•hÊ”)vPåZ«¦ŽÒK–,q'tRÂp”0ªd¶Ú'Ÿ|²[ÞÍÖñ**»ð@áÂ… Èáz€£ÎrŒH»ÈJÍ6Wl'i/^¼•ØÅd Ì@)Ãf»0,Œ<‚¼äTg,…8Š@!dà dB¢Ú N—e½mR° V>!#B’MÕQV¿©—]RÕÁ¯If%5€<à Ä Ô!òutŒT:Ô1¸PÆŠ~àô:œG ¡‡Ã©ÃŽôe9ÙV;’W;EÔÁ‡(KF ö±CÛÏÚµS)~ÚÏ>Û¶½»½~GãÆ_|±y'uûìÅð[§~Rú…<1lr{´ì†t¢ü¾v˜Yk¿Z¿N Q Ü¡?îÛ¶"£ÛG2¬bZu×]nãC¹Îޱ]Ó˜1®ås}9sì’ˆ~6ŸI§mõÃp[Q¥ÕWâwnÝâVÚ¶²Œóu¶Zk”e|àû%êSVM,ýÞt»¢‚‚µ1ÌV»ìyèaIÕV›p,3ùþ³ì&͘!ÏÔL»|NÔ”n|%'Õ hk³ÿ+ï3à¯Õ߀é[Wv¸W]í¶=k‹x XÙtÌ1ǸK ä…øbx£/‡µÚ›7ož5jT‘ÿ±÷êÔ© ¿¯Œ'œp‚;ꨣܻ»YE…à3Ï<ãfؽʘ;1^d (À'Ï….6)«žöÄ·o¹;—ø•¶ÛÉìËô¶§žŠoŒÉ•Å&jn}‡k²Ó]¶¿ò²-q±­UeTµªa ¼ê,°g“MîåÙ¦äcŠ°Ý­'¢M ØÚxd1:‰Ëd«”zl³'»ÝÕgìgÃüöqÚ6¼&ÛŠÖiŽýŒ]{Œ†Js/Üv{< þŠOz¢þvFŽp¬îÊð+yb.V/1‘‰½ï1~‰è^]*ƒ»¦a{¹NîAj¥]]v«)7âþÕx>ö>d|ÿª 3šO»öÙÄç“÷øŒ â²5l|ê`íI¬î´÷½îNì|IŒ£ûïsën¸a§6È¿,Vª_xá·žÏêD hjÿ÷†Ù–ð>ö̼n?°°ºig¶¹“†R˜-U·‚åOœ)×jwUüµ¥_É]kܵÊ]úè µâñÅ>¤*®ƒ?¾6øa'{tŠ%;ú••3/õëcÑÇ_ñhG:ڪ"Å Ä Ä Ä Ä Ä ”0¶ÜÒ6=÷\›ÿÖÛ¶òý÷JèÍcbbbbbb g`›m¶±9sæØŠ+ +•¬Y“‚Úµkg[mµ•U¨ÀWü„Ýĉí»ï¾3ølv+V´ZµjÙ¤I“°‡»„s¬t¼p dL(ì»®·EÆ%?ÉÕ§“À|B>ŠÇXZ„püqa‡ ¤>­l‘‹—ž¾ˆñ‰§ñ%ÏÙ$_"8 Ô@ÒKF&=>8:Hs1tšb)®âà«ðEÎ%! ƤàYkâjÑeÆÖÄÕ:“Äà†Iã#½Æ-öðJ" ó@®DÈV1éC¡\~j?K™Äǘ˜˜˜˜˜˜˜˜˜˜˜˜˜òÌ@qÀÑêÕ«mûí··æÍ›—hš&L°o¿ýÖŠBÊŽ:9p pÀ€0EÈÐAàØÓ炇sE Â_¤˜Š‹¯ìá‘CaéC_—²NÍ_Í…ñå'[tÅ’&P¬¡3 #[-@ f2z‘úÒ‡ã‹ÇŸ±°…GNŸ?ñŽõ¼*‰ÂùH‡ ½|Ô'—âkŒ¸UÍ%%RÌ@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@yg (àˆj£š5kÚÞ{ïuÛYQsÇwðàÁ¶xñâ4ß,ÀØ„€°x.t` ¼@!x<`âñ…'6\Èh‰',E:ì úø‹ 9}HþðÄþ‚ —b‡¶®›?áXRb¢áá5bÑÇÒÄ `B^²SäKL§¾Z%"Sà‘Öƒ{ù WU2éiÑIö©ã#Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä ”sŠލ6jÛ¶­µlÙ²T³7nœ5*­ê(8ÚÝ/¤Ó œ eô!ôàØ¤‘Ÿt¡Ÿ3KÀìÐáчð«\sÇ [áèu…~ÄF®Ö±ÞGãÐ/’pÌ—˜‹à Êdái5 & q›Vrì´ÙÈ^:g’øãP„-v\Øqq 5O€ôÈ™v´’;Ö÷ÃV6²G·ª‘…H1111111111111圢€£U«VÙ>ûìã³.Í4-Zdï¿ÿ¾Uª$¢ àhO'sà?Xî.0a"jÁ! õÕ ÜÁ_áØÐGÎ…œ–qh!…-“Ô<4^h§XjñÇN}ñô!õ#%Íñ(§êBbì@‹¡ÏX2 îØ„—\~´ª>ÂWÅŽxºÂ¾{;=£ø+žxéB?xäÄ„dè„¿ª´Ž‘¥H1111111111111åœ\À[ͨ8êÑ£GZÅPI¦‹ÿ믿îýuHvpÄGx؆&–‘yaƒ œAX >à´Â@ëq õ±§ÀG :p õS¶ØC´\Ø*<þ`&ø3>±!ü!ù§z9eœC&fPQ<œ°€-;MŽV‹\qЉ×8Z$:Æ …B½úšƒªˆEr K´Š%9~\²À™‰3333333333333PÎ(8êÙ³g™f8pàÀ¢€£=\p°À0]vÀă+,¢…᣾b!ÇO<„­|h¥G.ž– p Í œ™æ„¿0ì¹â8¶xÂ9_ '€O8 3 a ©Ÿê¥&˘,]È´HÅ¥…Ð> g,dJ’cý¶5ZHºÐ9­äÄ$†b3§x8¶KB¤˜˜˜˜˜˜˜˜˜˜˜˜˜òÎ@9GÝúÁÀ ð.0ð ä´Ø€} C’œ?Z.*„M¨‚ɉ¼Ž’ öšq…… g<á/Ó‰¼Hóg<ɰ¥/cs“œr[¤kÂ…J£ÅÒgâLšÁ›ƒW xì!®‰kÑa2)¶ââG,t´’ãÚ¢£‰9É^ó å’~˜ã#Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä ”sÊ8¢â¬Pk@¦ ™tØ¡wà<$rìÑ ÷!™ÆrªÄNãÉ„œ‹ø…-I_¶´óÃ'o"xIHÈa`d,  é‹§ÅW ¢/qÔøCPOrˆ–è5ÇúÊ# 1Ôb á OLH:dñ®j>%ñ!f f f f f f f f f f f f f |3°G``´\`€2ð` ˆÀ!Ðц¼p É293oÆñ â‚Qàv!Çú±éƒ•„UIè â@´øfÎ~fL'ÊM8äKLšuÉd1ibiQŠ‹-|Øw]Cà:ì ø09È´Pì;ˆdAò£Å‡ ¢E¦9Ðrá'[ÇzYܪF&"Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä ”s6àŒ° B¸‡ú’ÐqIîØÜÁGà8„ú´2ÀŸÐcБäôÃÄa|Z£y9Q"‡—|^DÀ’& àÂ$PS,dèY-„L}|!Éé"¼úè[ã þØ!§E†½@&SNä«‘Ðg^²!Æ #Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä ”o6à0¬| ÌA—¶„¡ƒÐc‹6ê#ƒŽáØ$v"Ùa«qÁ*èsaK ã¢ÇOrìAš'2dš_86ú¬¤ÀY•BC ˆ}ñô™¨&çØB6š06ØklMñН!ç‚À‡ñ‘Ó‡„?2.Ù;Ö6ÈñÃŽ>_滪q¿:uêXÆ ~òäɶtéR?ÀoáacXslܸ±Õ¬YÓ&MšdË—«z°øg`cX_¶U´k×ÎZµjå×úÖ[oe3‰²˜˜ , vjdÛœ¸Õج¦MxuŒ}þ§ l†q:1ÿ[Ø´a[kÝæ8«^½AÚÂ-šb?}ÿŒÍŸ;6M;1111ë>p¤…†À‘¶ˆ èÁìA’lÁƒW@ðòA'^ºÌ=v\ÄÀžøÂAëe´Ø(.¶Â=àÕ×\Ó©Š& ó%Ù20†ƒC“}7èñƒðƒãPA¤â¯øÃ+–c R,øÂO 6Š%;ú"UC•z«Z¥J•l¿ýö³sÎ9ǶØb Åõ-àÑСCí?ÿù­Y³ÆN?ýtkß¾½­ZµÊ®ºê*[±"³2ÍìÈ#´®]»zûk®¹Æ-Z”“`Aß¾}½|̘1vçw²A ñV¯^mùË_lÙ²e…ì:tè`'žxbÎñ´¾sÏ=×6ß|ó4ÀÖwà 7x”—\r‰5iÒ$Í.[çú믷™3g&ª£Ž:ʺtéâû¬{áÂ…‰®8¦V­Zvùå—{ÿ5j$æ£F²»îºË† Ë}öyI×—ß@˜¿ýíovàúÙìµ×^ÆsiãÎÀæ{5µ½nìfu¶ª“¶Õ«ÖØÊ¥+mÉÔE6~à8õÀH[:cIšMçïm­ŽÙÖªÔ_æÒL|gæÈYöÎÉ ù£l°s#Ûûöý¬n«º¶|Ñ ýØ(ûüÚÂÇÀ~ì}GÊN#¸—9[¹d¥‹»Ø¦1;»o„Í^ðÞüÐV¶û_÷´ZMjÙ‚ óíÃ?¾gÓ‡N•{Aë@àžèaMºmikV¯±ÉïO´A§¾nÿUöõ R~Ü>hÛön—Lྚ·úu&‚ÈÄ Ä ¬× ÝûCkÜ„sP Ó˜Ø[/ŸTX%1111ë4å ur‹W¼Ä-.:êë rð ]™ýprfÞN1øðŽc ËÐxŽõzZdÂTèã'ú"ÆÇÒ³Ù¥,r<â/±bBœQRO}}ka²UÝE«dK¾´ZzÅÑ~Â…ÊÐG £ }±QŒjŽ×˜’1'HÀU©€£jÕªÙ7Þh»í¶[*ZŽG}¡ÄØyç½Õ49øàƒ½ Ò´iÓ E½òÊ+í°Ãór©}÷Ý7+(Žw÷ÝwÛ#‚¼ôÒK¶Ùf›'Spê©§Ú?þ˜ˆÿú׿ÚAäû€HS§fù2™X0µk×¶ûï¿ßš7o^ Ìà.½ôRûè£2¤f¥Y_¡ å,ˆÀQ9?ë`øvg¶·.¸)ŽV/_e¯ôœMûô—Äô¤ûXí-7IúE1v}Ôæ|7»I—ÛöµvgíT wÀÍ›Þn«–ñR]@-jmô?´@ƒ›÷ã{aï§lùœeÖáêζ땼§hÂÀ1öæ1¯¨›´õÛ7´c‡œôaWNüáŒ2¯/-h9u"pTN‰ÃÆ äÈ@×ýnµíÚŸj•*W÷*ð12E8R&b333°~3°G|ø–@pBÚ £6!ˆ>€o,ÈÐÑ'–âá-xŠxäêÓâ a'_䲑ޱ4ÅÀÌEþøK ”/)8ƒi‚øÂ«/Nž j‘jñÆزU<õe ¸C’9Özùӊ׻=z.C|t’;6©L*ñV5¶6XPýQéqï½÷Û…¨(jÖ¬™üñ¾zˆ "ô!SZà¨bÅŠ6hÐ WNúpÃØ\pAÖªšp¼•+WZ= U¹€£lë»çž{ìí·ßNÖ׫W/_åC¥GO?ýtV@ »ÇÜ,Xë©4Às$[µ  &x oÞ¼yvÄGØÑGíå<ôîÝÛÆŽ›ôK»¾$ÀÂDàhy"Öâ42£%ÓÙòyË­FƒêVµ~AE†|bëûmÑäT…^&p4{ä [³’—àtšêÀ¦¡ýÔVÌOßÎY¡b;cƹV¹–°õ”ß+ °)NN ’ -›µÄ–Î^j57«aUê¼>á´pâ|{zÇGm—K;¦Gèjx‡­XÈûguþWWkÿÇt@>pTÒõŒP¾\ŽÊ7ÿqô˜â2Ð÷‚™VµZ]o£â²õ111ë&å ut«–Áj.¾ô ¢/:.CúÚ€IЇ° c C®A Z(´—¿æF x=s‘ž–1ðSëXOøK8æKT“R4 Z-@‹£/€¾òw¬'ÅŞŠØÁ$;xBÄ‚W‹ ó’?}ù…߀Ðk<Í»GTQšœyæ™öÝwßù~øP¹re´B §´ÀÑvÛmg=ôP8„½òÊ+öü#M–9ý'Ÿ|Òn»í¶4»\ÀQ¸>@¯¾}ûÚ÷ߟæK'\ý8R¥òâ¨4ÀQ›6mìá‡ö¡çÎk'œp‚‰®¸â ;üðÃ}wàÀÖ¯_?©|U—ž¿’¬/ °08Ú@žˆµ8Làh «*šüÁD?BõF5ìˆw·ºÛlšŒ8øÜ·íû‡¾õýLਤ[ ²Uúø»û†ÛGç¿—Œ “ ½}â«6î…Ÿ¼Í&[׳ã>ïm•jðòŸ¢O/ûÀªÕ«^8zç¤WÓÎø©PÉWÓxU;|éÎ^qTÒõi.åÝF਼Ÿ8~Ì@шÀQÑù‰Ú˜˜˜õ‘rŽ(‘×y7…H¿À‚-€I€EÐ ´¡/µèái!lñI¶øsUÈ[xdð!¸DLéÁ9èËŸBO i©^ŽGçP§‰À„B Ê¤à• úð´"ÅÄkÄU"hu´Å—_x䊌o%Š\1B€H~NííñC½æS¢­jTýPyÃ!ÌÐM7Ýd ð|Qk8â,¥ÓN;-máæœ%Âñp±ÅmÖ¬Y‰Y6à(s}œÑôüóÏ'>E1ë8ºúê«}óç9 ‰­hï¾û®±rX·xñbߟ¿’¬/Œ_Zžj'HUZŠ£yªŸO£|²´qÙ±’ú;4°c??%YÔOÏü`ïºó ²G»ýeãʤ•‹–ÛCîJ;‡§(àÿ-ln=^>* µjÉ ~Ë—¶ë[ÕPNýx²½¼_Áëçf6·#?8!ñ“­âh½GüëòUZg1åK8Ê7SÑ.f |2P~ÀQêõ¤Ð Mù¤!Ž333P®Ø€#>Üñ› œ‚´¡å…Ò—q=´ÒƒI`aY‡øØ W,ù8Q΢¹€gÀë ;ù½æ —ú²s¢ÜÎm•Ò„‹$8¾È &£X²£œ¾x&L_öÈ™0rˆV¶ò [Æ•¿À"ì!Ù…±°§Ï/ È±^¦X˜J…U?â|È!‡d=èšÁB œÒVQ]ÄÛ2éŒ3Î(TŽ'û×^{Í®»î:u-p®íd={öÌk}]_À•Nƒöw°c\€3@¡L +™þüç?Û|`eY_füâú-Z´ð9Ü»ýöÛý9N4hġ✽´å–[ÕQ»ì²‹QuóÍ7:P‰-{î¹§µlÙÒoU¤ÊªcGª(ST’ /ùÄvÃË@qÀQ書³~ú$ÿê_CmØ5Ÿø~Y£ÞcδZMk'±CæÙÝ·Ù# º.8Â÷à·­z´JÂŒ¸å ÛñÂô-h(iz·-›µÔÛu»çksÚö‰˜õ Q1Õ©ß^¶i»úî®gãlÖˆÖåÖ}­Jª¶bÞ2tÚ6×Ýt`ÿžÖ`—Æ~Š?=ý½ >o­tŠ‹•8¼ý»Zý¶õ­rÍÊ`ÊÜ ¸Þ0M0¶ÿÓ¨ZuëÜí¶íö½­r•ÔÿýêÕËmò„÷샷~o æOðù©P¡’íÞåk¾õ!¾?uò§6dð_lù²‚ _>ÂíÜék½ÝqVÑÙOóš ýèoz)ªP¡¢mÙò@Û¹ãEÖ Q{W±\Ãý¨³ÊÝt»æÙ²%³mäWw»ñßµN{_k6ßÍ~üöI›9}¸xø“V­ú¦¶tÉ,{u@O7·Ÿíà#X“-»ùà#¿¸Í>yÿ2«»éÖÖq¯k\ÛÚÆüð¼}õÙ¿ÍÞ¶oû¬Î&-¼íªUKí›/ïrk¸Ò¯ÏÝ©9ê±4ÀQ¾ùÔ´+V¶¶;õµ];ýÙjÕÞÜ}¦ác©ûàºf¥-\0ɦOf_»Ù·^bbbþ‡2°GyÈ:á’c1ôÄÁF<8<ö² }Ï©‹&‚äKÙl5¸&Ç‚àEðØÐ*™L^$?úŠ/Z¥Å†zxôŠÍ;l¦=}…žXøÉ6“Ç~ˆ»ò&¶¥õíÛ×Û¿ùæ›8‘…@Ni€£¦M›Ú³Ï>ë‡1b„}ûí·þŽh}ôQ¿.œG8wp«R%µmäØc5îøeŽJ»>â­/à¨^½zöúë©*‹/¾øÂþð‡?0|!êܹ³¯Cñßÿþ×ú÷ïï·–æù+<€V¿~ý¼å-·Üb^xaâÅYXl¯c»w†q–2ºÇ{¬Ð]û¤W#ebãn‹ŽÚöÝÁºÞ±²È7yÉ&¸»¬AeŽjoYÇù§^׈ÅÙJ56+ø»üâïCì‹ë ^*óŽZ÷jcÝéA8OÜ ®mßöê&íàsÜv»‡¿µŠU+YßYçY…*z OL²޽®—‡µ²¤¶¹Ì €[³b•?,舧¬Õ¶Ç$fsgÿ`O=¸£8RŸAwét™í±÷ß}æ¹@UªÖ±SÎãnJQ°Í61˜Ù³¾µ÷ß8ÛŽîýq Mg·V®X’œA$í;Osñ7±®ûlÉŸ:ùcÛ¼é^2Ikfž¸w[…ŸõS&%ŽJšOF©T©šzÞ÷CPƒ´yevfÍnÏ<Ü!Sû111¿ù l Àot\`©7½! Ø—@"úàòfÚ;u‚• —½â¡¯–80–T/õˆ z½±É–Øâ±aÅslÑ„qI{Ó´1$Œ–‰k2ŽM{ˆ–oš<¼]Åñ6è9àž±¹ˆÁ#Ç_ì°—^ó åRÜÌ8Œ¾%ª8êׯŸ¯pq~¾:ä™gž-–B §4À‡m_tÑE~ng?fÌ@7¶Ÿ± -Üþ¤ñ¨Šâi¶¹AT¹p§1(pTÚõo}GTòiÌþóŸžÏ|àP ây¢š§,ëËŒ_\?ŽŠ²DªT)õeùÃ?´Ë.»Ì›#»ãŽ;l§Rw¹ÂîË/¿ôw¤ãîuc"pTT†7]QÀQÍ-jÙI®Ú€Z1o©=Úâ~[µ$õcEY€£vg¹»¹Ý¶_’¨onûÒv8פ¿xŠ{iyÒÏ8Ê<3iÜó£­åÑÛ&1ÄÌùf¦ èð¸5Ûo+;d`Á¡öÒӮϊ£â€£p^!?þ¥í­^sš}óXÛ¢KÓµÛʶhÊ"[í@§Ú®ª+Ç"pT¦È­û T®RËN;wBðBÑ’%Ó¬eë#Ó&pÿ­õm…«ªX±Š~ƉèÛ¯ï¶ÁoŸo ïlÇ:Lb[¼hª=vw«¡bé„>í^ý6‰ 5óçŽõ}ªƒô2à( ’ÁŒq¿¯N £ÐdåÊŮʩf(²O_a_vCšŒNI€£Òä“1:uù›íÚùJXO+W,´I®ÚjÅò…¾ÚŠê)ª´¦NþÌ^xro™Å6f f fà&p~Þ ‹þ2wñA<8R_ø—°xévà‰£ØèÁ&DZËPìÁ6ðÅNqë‰>óƒ°Ã^râТËôu¢Ü¤ ¹- 4²e`xZ©ÅkÁ´²Ñ¤ÔÇ–IÒ ¬q¬C É;_lEÒÓ†c)žlуV>Žõ2ÚH›ŽÍÝÔ¶ß>µ•â÷¿ÿ½ÿ"OàâH@Žì-Z$6i 8òÈ#mÚ´i‰„ ˆCŸgÏžmï¼óNr‡5€¥‰'&öàP‰mj5jÔðzÎI=ztV਴ë#p}þùçYïªFÕÔ!• ø…[ÊŽ:ê(›:u*✴뮻z@æûÀdµmܸ±½øâ‹^'@¦,ëË:HÂLàèÎ;ïôÏ믾šxMš4ÉN=õTh±mÔ¨QÖ§O¯@"vçŸ~ZnâG>5¿©‡Là賫>´q/þd;5±nwF,úí^¯Ø¸—Æ$ëÏŽ–ÏuÛ¿2ÎÞYðórù‡ÉÛr>æÓ“mWÈžÜæ_1n«ê¿Ýƒ¶`ü|ï’pT§…ÛV÷}êo§)ƒ'Ù{7óþ«—¯J[ w‡Ûë¦nÖâˆÔk\¦>pT’õùAó|È ¿ñsÛé’ô_ýç™kóÇ͵fû·ð‘§6Å^ìö´_Çöçí’ŒÆ]å^Úç™äîwñŒ£$5‘)‡ ôêóµÕoú Ãðo¾x¼ýñ?“[f=Ž.8ÓðóO®³a_ëu5kma§÷³çõðÑ  ­ó>ÿt?|T—Ƚ«…-Z89épØãn [¯¤?ùçwmೇ¹;´òƒ¬¹ê¢­q“=<Ÿ 8úä½?Ùžû¦<³gŽ´9³¾·­Ûç}'ŽËÆÿôJZÅ‘W¸‡A¯÷±Ñß„-D«‹oìx1G&_xtºë)›ö`•*:^¶Ä >-rÅÄGã"§•øE)÷¢e«Iч‡à¥'ª‰c#^zdø°x-‡ÅcÉV`2âÐÇ^1°ïØd>Ø)<v\È«¸Ks+pô /Øæ›§~u ·}¹xE’€œ"e…[³‡‰ +¯¼ÒƒBð·Þz«=õÔS°ž4ÀNÇsŒýéOòº‘#GÚÙgŸ8 ×Ç-í§L)ø Ù:ÍÄÙ=O?ýtRéG~ ,TÐPRI£îÝ»Ûßÿþw¢¨ÃÉ7Ùdc;!Ä]á8 ª,ëóJðG}ºËU_"žž‹/¾Ø??üðƒ~úé¶ÕV[ùÜbÇù8K‹3§BŠÀQ˜ßŸ e[ÕšU«ííÚø—Ǥ©3£4eÐùôÒläm_%’ª›V³Ó§œ›ôçþ0ËžÙé1W´¯µ;+Uí†òã ß³oïîíòŽ8/‰s“DsFΰMÛ7òÝÅSYÍÍÓ·ÂívUêË#Ì¡^›‚-Ù€#ÅÍl3×—©/®Ÿ 1W@³¾sÏOs}~Ï'm·¯ýwóòŸOµAî ò¾;#±{¤Ù=¶b~êK2Š%é‰ÌzÎ[ÆÎüãìdÔ_~~ß^zú€¤OEÎYÍKúóçuÛ¹ *…¶ju°õ<æ•DŸÉ¼õò îl¡çqý†í¬×©× „ æ·Çï¡ê_)*8Z¸àgïsΟÐã÷lm[»3•:wû§—HýB!àèÓ÷/wçÝ”xžØ÷›´ê§ûo©çÎqLÿ!/_à¨,ù<ê¤Án]çd^ï½q–}?òᤙ˜˜˜ÿõ lÀ‘@Þ´À*¸àà ˜†€$tòslâ…àxüÁ'ˆ…})<ƒ¾°Ç&¤¹à+[ñÄ’à!Cã!/’4ñ"~Ub«KkR´Zmew‘Zˆ‰1AMLà ¶ŠáXo/™æÆXÄÑØøBÈ=}bч?ÅÀ^r«Ø•h«Ú#ûÌŸ1T–õ…±óáCàˆCÉ©úâ®jŸ|ò‰w?~|rNÏýÉ'Ÿlލ:âï ʶ!ÀYømQ>ÀÛÈ>¹tp¡…gG³GLw[£x, 9îPgªgfœ™[ÙÚxêФÿéeXúïW¶ù^MíðA©_óQÎvÀϳŸðvùG›´ª›¢Lûôkܹ‰÷ŸóÝLwøtCÏó@õPÕzU ßg[Ü2ÑgŽò]_$O&8têk6æ™ÑvöÒ‚3ʘÿ€]·ŽÛÓv¹|wàhÜK?Ùî×uIFÊbEà(IOdÖs8púØS†$£.]2Ã>ÿäïIß½AY—î7ôw×øHU@]ºßdíwKQÑþøÝ“Æ9C!eÚxtw›9­´Æ¶8à襧ö·_&~`ç^ʽ)š>e¨=÷ø^¶Ã®çY×ýnõÂlÀÑ„±íµçŽrú‚×ÁÌ-bl«[¸`⯑SM¾ÀQYò™YÝÅÈK—βOßÿ³ËeW‘UøóSÚ$c'f f fà7žrŽôk&xo@`ðx„lAzðé°åèx3¢å‹À™Þ¤ê¨O ÅÄžñd‡¿ðätôÑã§q‰§ØÈµÇæ&œJJòÑ4Iâ “^“V?“Õ°¡O‹ ¢%)Èù´BË%I‹¬êdØáK\.xÙ…2äÒ9ÖÇ¥Å*pô¯ýËöÞ{oﯭP^Pă€LJzÆÑ7Þè洞ï‰'žhPÕªUí½÷Þ󷘧ÏШl4ž€#d|°]sÍ5°ÐÄÓ€#Î êÖ­›—±5Š-g"ÎDÚgŸ}Œ­b¢\ÀQ(—m®¶¤À€—À3¶¾‘ŸlÔªU+{â‰Ô]*þýï[YÖ—mŒ¢d!pDN—/_žýãÿð`12#î¶Ûn©J†Ìmˆ3GÊÄo§Í޾ø«Ñ º5?¬u²HnmÿX‹ûlù¼‚*”™ÀQ¾gçôÜaÖ¼çÖIü§¶ØØ†U±jEë;çî̽TÜ-à¨áÎìè!½“¸ã¨ÒâˆÔ:f}5Í–Î^jM÷kžèÅÌ=Û¦™bmN-ØN“ 8Êw}Š›o› Ý¿ÉmFåP½×çMûñÉQ…€£eÓÖ5Æ ·÷iü)±]ßÈ<Ä:ŸñゥV²­ {ÎÝ9óÂ9…Î º÷¦Ú…ÀŽÓ?ÉjÔLÝyß»o¬îî,˜þ9µ8àH[ÉBàè•gvç *8zàÖî}7µ½–ñ¡]÷ø³uêz]ªãË•%ŸÜÙOÑßZn°U˜&MxÇÞ{ýìB VaË(‰ˆˆømf œ£Ž.«àÂ%ñáDË…on`\âe'G@‘Þå§qó¡›_xâAÈÀAé°Ó<ëyZôЇ¯æ#?lŠ%Ê—D‹À‡¸h4Aú¡liQ¡ rÅ Õ|$§Ñ’ @$ä\Œ p‘ Hó¡á#=2úøÒBjK´Uí‚ .HªD¨àáË>$ Û’GÕªUóà•*[³uIÄÏ"ª’t·1G¶üüóÏ+Cœ³¤s•…ë»ï¾ûìÁTø¤,ãü (ˆÂŠ£Pž8æ`J 5lØ0\tvQ¶ÐáYHäƒs¢Ê²¾lc% #å#¬8â.kTBA™ÀÏÓ[láuÄY¼x±çöëuïÞÝ‹?ÔG~ãË@&p4ð çlêÐ)vʘ¾Vµ~dAÜþý]w[øJUªîîd6ûî•2õúB<*’V¯ä¥Ò’jßqƒNqÕ7Ü!×G¹*¥þUJoŸøª{á'™ùv›ÞmmßJdß?8Ò¶ëÓÞ÷Ž>wwi;èù#½˜þ0Èí¾ù   £W|Ö~qg6eVU¯_Ýê´ª§¥Øƒõo·•‹ ^¯Qt´‡µ>¾`ûâ'N‘‰XG :‡*Zíª\æt3¸)“>tg à¶£ó10EÜv¾ÏÓ€~Ü2{ÿ­slÔðdæÚ ®J¨ÜæðçûnÙ4ЧØãNj 7ÛÅw²q¤Š§8zê¡mÎÌQÅG÷ÜXÃÒþÿ—y‡¸làR¾Gk#Ÿõêokûö˜5Ú¬à¹0IÇÞhŸ|p…ñq9RÌ@Ì@ÌÀÿN6àļ òfBŸ–äÔ傪"äÂLÐÉؼ Ó †ÞTƒ>v\⪄(}ùCј´Èä£9ÒWLdŒO ©Mõr<|CÈaˆ•0Dø1€&€L“CÇB°×„è£W_­yâS ¡%®b‡óCÆuÝ$"ž>¹0vôáEa_¶ø1†€¤UQB54}út_©ÞÍÌ+²<ÈAUàh÷Ýw÷[Û²„,$¢:ˆ*!Hã…Àrª¥¨”Ê$Gáú8S‡*¥Õ«Im•7p˜öþûïû ­X±Âƒ'!˜¦™† àç•e}Š›o[àèí·ß¶Úµkû¡T­ŽË–Æ$¢%©Ø¨™lÀÑä&º*–ævÈ«é¿L¿¸÷S6}èÔd½¥޶èÚÌ{ûØ$Fq̤·ÆÛk‡¿Xc}fý>­¢‰ñÝòëô÷.%pT·u½´-wªVR^ë4w†»;ã…£0‘_—h¿ÛÒ¶¢=÷øž6}ʰ¼‡¤ÚèØS‡$@O¦cÿ¶·¹³G{1¶çü©`»Õ’ÅÓìá;š¥¹pGµ“ΕÈÖ5pÄ7}Ο‘vG¹»opUP0ÆdòŽÊšÏdᎩZ­župÕPí;œïªÈõÛhÊ"<À<ô‰|Ì@Ì@ÌÀo9å qð /ª6îÁd#úØbÇ! DB }ù+¦läëL¼8~147á$È‘Ñ'6Ò)zpúj[4¬$„}èàšp‡ °xZé5aùd.9$-9ïœZ´Kªè+ðò§ªyè§¹³.âœé:ùwÂâ]мyó줓NJÎÊå_àhÆŒ6hÐ «^½º»ëGîÊš‡~ØÚ´Im}àÎiüqNàˆù…‡jk¾ª8¢®;§vÚii‡doÀÑ{ìaÚ¦ÇÒ$MŽD€-€zPX‰E¿´ëÃWÄv9ÆÜrË-ý9Jä<“Ê]}õÕÖ£G2<ljCÎÿóŸÿ„‡£0/_pT½Q ;eì™V¡ /g)úÙ×ÿùÜwJZqTÁmO;cƹV¹Vê×mÀÞikVòÒX@G}t‚5ê°y"x­çóVe“ª9#ªnŽr’U®]ð«ùË?°ªu«Û®W¦GÕÜÖ®&ÝšÙ&-6±E¿,²Énû×’)‹l]Gä°Ë-ûZ“½›¹3––Ø'`“ýœ¬ &óŒ#;ùlU›õÍLÛîô’x#ÿû¥}zÙ`«X¥¢üÂáiçÉHñÕmÌÀºÊ@ÅŠ•íw—p1}D2{öÑN6cÚ—iCR-T·þ6¶bÙ"[´p’×5n²{ ³xÑT{üÞmíðãßL»;ÛÛ^쿟óYcGœðŽ5Ù²[{às‡ÛÏc_w5•í˜S>)T¹´lÙè’umR¯•Ðgxõè]-Üú&'ó“/pTÚ|â׬ù~¶jõ ãÎvk֤ĮV}SW5]Ó±i¿ ±çŸèšô#333ð¿rŽ8\ƒ‹Æjy¥œV¸/ààØÈN:'ò`Qø"/Pä™°…‡1Ðér¬M vAåSÁyÊbJG_óF¯¸´Ò!/’0Ì—4öú¶Â"4!x£•:Ú¸®_8s@Gòh!üH8:âÈ_q±Sl#{P„9D_6ðÄæ"}b§—ª8AqDU‡-ׯ_ß›N›6Í:=bÄ÷`qxªŠØæH¬4ÀѦ›njTž@o¼ñ†¿‹–ïd<sÌ1`±‹mY/s«6Tqv‘*Z…ÀQæú8 ƒ;ÈÍŸ?ß®j—°Ï8âîe€NÙèwÞ±I“&%ªðŒ£·ÞzËÆ—èB櫯¾²áÇ{ç5‘mçjQu4þ§W]õO kÓîdk·óYÉv)*~8×èô?LN]t¨tåÊ5í § +‡† ¾Ê¾úìßÖz»^vÀa§-kìèçl«V= ®-£çŸØk­G_|ÚÏÌŸh[¶Øß¶nSp‡HÆzuÀ!6q|á×;t!pÄÎ>ÿø:ĶÌ´=yü»i`SiòY«vS;õÜñ>æêÕËí»¯ï³Ñ£ž´ùsÇ»¼Ô°}{ÜkM·*xÿìÃÿ³/‡|ðŽñ!f f fà7žrŽ87àôÀ¡Gð®Þ® `[üáÑÓÇàGX‡ü„Q(žâ(6¾"âAŠM,AKìp×õ}ü‰ÆD—•$_Ò`²gZƒ¡Wï¼ó `+¤²Gä—;Çm½õÖaÈ„'‹Ý»w÷²ðyHŒ"³Ñe 8à¨B¥ vÜ—'[½6ÿ—S\•Î+=k'îcµ·Ü$¯5sÈu½6õm·¿ðƒJŠ^;äy›ôîÏê&m­¦µ­÷˜3“>w{·Ï¶ÿã=Y.f¹»ËØÓ»<櫈:\Ýy­G¹ÆD®C¼3mΜû{«X½àõý‹ÝÜYQŸMML˽Ðõ);ö³“¬þŽ©$AfáÄùiÏSŽ‚äDv½d uÛì€CËk,¶—ežkôö«§ØO£žJü7mØÖN8cDÒ‡yö±N6sÚ×ÆÕª×(øœ”fä:“~7 $yé©ý\¥Ò Ä¬,[Õ’ Lq@Le¸Ú¬Ã홇;¤‰K’OÖGi²tV­ZjÝÞÄØ)f f fà)å Q"~À•  w€á†V˜öÀäŠÆ#6`ÂDˆNA+‚Ç_A_q›øË½bildÆJõŠy”S1f^-—&IKŸI1 -J}ÅVëL¼½@ú">½kAȰÁ¸²'®bÁCôŒh5úìñWÔê·Ïý-txóè—¬ÝÙ;¦™ô€;»hÕ½×…Öîü$w smw°³èÝS_÷wS?i]ÔÊ%+ŒjŸ·~åoY¯3˜vºhWëtýÞÞô—÷'Ú«?—¸e2{ÞØÍvø}êNK+.·ÝeÇu€L Ö7áµq™am÷ëö²]å“hå¢åöðæw'çD!oÖ}+;äµÔëQjl÷šãޕΘvŽUqÛí ºô·ŸO³ÿ´›íÞ¯«—M4Áö|Ámý«b»»Å±/¤ó–Ú{g¾eM]üíÏÝÙ«ˆÿ[›^BûÈÇ ¬Ë ´l}„»›×£Y+Ö¬Yi?|û˜}6ø·e­µy»ÉT¨zó¥’¾˜íwùí½êÆ!È–.™á*’Žìý^¡»†ÍŸ7Ö^y¦‡Õ«ßÆzó²ÂX-_6ÏøoC¯;ãü©îG¢`þ„Û"7Þ8Ûv‡Sl¿zý˜Ø/[×ýoKbe2œÉôö+½mʤ2Uiý“ÎeuëµN“©ÐõòÓw‹”<ß|.^4ÅžÚÚUcuîöw«]g+…(ÔNšð޽ýòÉî3ƬBº(ˆˆˆø­g œ#~YÎÁ‡cð UAa_rd\ò“\}Z0 U­v¼|±À+ ôðøÑra§ëêÓÊÖ± /=:ã<$”K_¨%H¾Dp&Ö@ÒKF&=>:J„cÀI<­ìˆ¡…(±ðç‚×E_?ã';dá¼2m˜ö\â‡8¾L€`®X~üñG›0aBVp¨L•“3FT¾P¥ÃÁÌl3ãâ°éð\¡ršžÏ9•_mÛ¶õÀ[ÇŸ÷ÜJ³>ßÞi§Œ;¼9ÒØÎ·®ˆÊ"þ¶Ø 7yòä­«DǸ¿ù ðúÜx¯&Ô™ûýl›ø¦{XÈû÷Ú§j ªÛ{6±*uªÙôϧÚüŸæÚ¬EŠØp2PÁjÔld µw´µmÁ‚ŸmÞœŸ\uËB7ŵû·Z¥jw¦ÑNîýº’Íœþµ ­mÚa—sÒ€£GïjéÞ/—ûú×Åš Ï¿äù$<uê¶píf>/óçŽ5¦Õ«õÝ ðHQ333ð[ÏÀ 0ÂFá¼I"ÇF8¼@tºëßTC0l$[o¾È±1ñÁ9ÐAš2Hãi>Š¥¸Š#[ïTÜÎ%! ƤàYkâjÑeÆÖÄÕ:“Äà†â#½Æ@ž)ý’(Ä<+òWLúP(—ŸÚÏR&ñ1f f f f f f f f`cÌ@&ptÏ5"ø²1>‘qÎ111.å ±UM88àÀ ƒÀ"dè ð ìésÁCȹÀ"á/RLÅÅWöðÈ¡0Žô¡¯ÆKY§æ€¯æÂøò“-ºbI(Öм‘­ ÐG3 Ž ½H}éÃñÅãÏXØÂ#§O‹ŸxÇz^•Dá|¤C†^>ê‹Kñ5F©·ª¹X‘bbbbbbbbÊ98*ç' 333°3°G``zÀà¹ÐA€1ð…àò€EˆÇžxØp!£%ž°é°ƒèãG,.äô!ùÃCø 6\Š Úºnþ„cI‰‰† „×dˆEHC&€ YxÉN /1Yœúj•ˆpLGZ:ìåƒ\UEȤ§E'Ù§Ž33333333°‘f Gé§333%å íî¦^H¦A 8ÊèCèÁ/°H#?éB?g–€?ءâáV!¹æŽ!¶Â=Ðë ýˆ\­c½Æ¡_$á˜/10Á”ÉÂÓjLâ*6­äØi²‘½tÎ$ñÇ [츰ãâ@kž$!é‘3ìh%w¬ï‡­ld.nU# ‘bbbbbbb6Ò l·ã¶ïA÷&³¿û†êîàù¼?'~‘‰ˆˆˆ(ÿ ”3p´§Ë˜øÁrw1Q ©¯Vàþðø ÏÀ†>r.ä´ŒC i,lÁ-4Ú)–Zü±S_<}H}ÅHIs<Ê)‡º{Ðb賂Lƒ;6á%—­ªðÕD±#ž®°ïÄÞ{Å/]蜘lðW’Ö#²)f f f f f f f`#·Ms­Y³"Þ­p#~ãÔcbbÊ8âŒ#Ø` ‡.;à âÁÑBÈðQ_±ã' ÂV>´Ò#OË…8†æN‚LsÂ_˜ öŠ\q[<áœ/…À'œ„À‡°…ÔOõR“eL‡¿.dZ¤âÒBèˆGŸ„32%ɱ~Û-$]èœVrbC±™S<Û%!RÌ@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@Ì@yg œ£Žnýà`x˜ørZlÀ>Ð!ƒGIN‹-•GÂ&TÁäD^G É{̓¸ÂB3žðéD^ޤù3ždØÒ—α¹IN¹-Ò5áB¥Ñbé3q&ÍàŠM‹Œ‰Á+<ö ×ĵè0È[qñ#:ZÉñ mÑQ‰Äœd¯yÐrI?Ìñ‘bbbbbbbbbbbbbbÊ9å QqŽV(5 Ó…L:ìЃ;p’9öè„{ Lc9Ub§ñdƒBÎE üÂŒ$Œ/[Zˆùá“7¼$¤ä002…tÈÅÓâ«Ñ—Ž8ê ü¡¨ƒŽ'9DK ôš‹c}å Ðj±…‡ð…'&UØÌ@IDAT$²xW5Ÿ’ø3333333333333P¾Ø€#°0Z.0@x°DàèhC^¸…d™œ™· ã‡øqÁ(ð»¿cýØôÁJª$tq Z|3ç@?3¦å&ò%&ÍÀºäG²˜4±´(ÅÅ>ì»®!ðv|˜dZ(öŠG‹D² ùÑâÃÑ"Óh¹ð“­c½,nU#‘bbbbbbbbbbbbbÊ9pÆØ!ÜC}Iè¸$wlîà#pB}ZàOHèÀ1h‰‹¿Hrúa â0>­ÆÑ¼œ(‘ÃË>/"`IH“pa ¨‰)2ô,ŽB&€‡>¾äô‰ ^}tŠ­q‰… ìÓ"Ã^ “À)'òÕHè3/Ùc†‘bbbbbbbbbbbbbbÊ7pÖ¾æ K[ÂÐAè±Åõ‘Á Çpl;‘ì°Õ¸`ô¹°%†ˆqÑã'9vÈ Í„ 2Í/}VRà¬Ê !!ľxúLT“sl!Mì5¶&ŒxÅׂsAȈàÃøÈéà —ìë äøaG‚wUó©ˆ1111111111111å› 8RBàH[Äô`ö I¶à ÈÁ+ xù /]f‹;.bà/ÀHq‘AôáñÃV¸¼ú¡x§ÎM žÛ¢@#[f@ ¹&öÜ??ø0DÈñÕåXðÐ'6± =©^A,ú! „Ÿl 2^ñè«*nU#‘bbbbbbbbbbbbbÊ9å urËC„nÁ%@G}ZH€ Wf?œ°ÇF1À(è3–p° çX¯§E&L…>~ò¡/b|ì Í1›]Ê"Ç#ù‹` &i0Àdš(:õÎ0Ùªî¢U2ˆ%_Z-½æ¥1ü„ •/ Fúb£Õ¯1%cN€«2G*T°:uêXÆ ~òäɶtéÒÔ(9±kܸ±Õ¬YÓ&MšdË—ë|­å$ÞXæ¹¾ÓS±bE«[·®5hÐÀV®\i3f̰E‹­ïiÄñbbÖC>ø`;ñÄmðàÁöÀ¬‡ó¢S§NÖ¨Q£BƼ§|ýõ×…ä¹êúrÍ÷·"_[Ï߯’VÇncÿÕÍ*U­h#nûÒ¾þ÷çËÔãØ„ úFàÈÐi‹›âá­°ÅUŸ_|…¿Ð‡Ð… ±å æ"äÅ’kè \à BÄЄ¥Ó¤h5öZ¤ZtØLR,Éie ¸C’9ÖzùÓŠ'Qz.CLt’;Ö÷iK½U­R¥J¶ß~ûÙ9çœc[l±±<:t¨ýç?ÿ±5k˜FŠjÕªe—_~¹uéÒÅjÔ¨!±5Êîºë.6lX"ƒi×®õíÛ7MP1}út›:uª}ñÅöý÷ß§éÕ¹ä’K¬I“&êæl¯¿þz›9sfš¾$ó¼è¢‹¬Y³f6nÜ8»ýöÛ}œÍ6Û̯“Î[o½eo¾ù¦—W«VÍþñxžq=ôPkß¾½­^½Úþò—¿Ø²e˼.|èСƒÿÒF¯¹æš )Íú+Œ¿dÉ›?¾ýøãöá‡ʇìÿûßÛG!QÒŽ9Ò.¸à‚D™˜˜;¼Î¿÷Þ{V¥ oCfǼMœ8qƒXÔ“O>i-[¶,4Þþð‡?’glÈëË6ßß’lm<K>*Tª`}gg«§þ˜wÿí´ãço,KˆóŒˆˆˆX(gਣ[ª°°0 .¾Ô ¢/:.Cè¡ÐL‚>„m:p bÐB¡½ü57bÀk 왋ô´ŒŸZÇz·XÂ1_Ò š” IÐjZ}Éø„€¯üëIq±ƒg±v0@É^ ±àÕbüäO_~ª.r"¯×xš+v¥Ž@n¼ñFÛm·Ýˆ“öÚk/Š`P»vm»ÿþû­yóæ9í/½ôRûè£ýhûÛß’~6Àæ¼óγ¹s禩_zé%À)ŽN=õT˜È®¤ó|æ™glË-·ô`Ëa‡æÃ°în¸ÁóC† 1À%hóÍ7·^xÁó½{÷6Ö»óÎ;ûþÝwßm<òˆçÇãŽ;Î.¾øb/:òÈ#mÚ´iž/ÍúÂXá!¿bÅ »ð íË/¿ žˆ¹‡€_šëPu´ÿþûgŠc?f f`#ÍÀ À7`:Ô³gO›={ö±@ø¶mÛ&sáõ*)p´¡®/YØo”YÏ߯’€£ÓùU©[=™ò#Mï¶e³Š®ÎNŒ#3333ð?•rŽØª&0 Fà@!ž ¶ -€I€EÐ ´¡/µèái!lñI¶øsUÈ[xdð!¸DLéÁ9èËŸBO i©^ŽGçP§‰À„B Ê¤à• úðe›61äZq•Z@-Dñ%gQðÈ™ªH rÅ Åù9ÖÛã‡{ͧÄ[ÕØ¾D5DµÌ½÷Þë+kV­Zå«oøUºk×®þBÏã?n­Zµò>&LðÀÓ¼yó|õÊÑGíå<¨Œ;Ö÷CàhôèÑ¾Š‰íQ[o½µm»í¶‰ÕGl¥·È…ÀÊÓO?µš‡ÌkÁ‚>ViæùïÿÛ¯“ÊN?ýtûÝï~çcRÍCeC;ì°ƒÝwß}^ÎöˆþóŸ pD%U=láÂ…^¯‡ìÉ廾0ÕAÇ÷Û;˜[H<'S¦LID7Ýt“uîÜÙ÷©Ðzî¹ç<ÈǺxqÛqÇ­zõêvíµ×&>‘‰ˆØø3Àë6¯a2™€ò†´ºwß}×Û%Ž˜ÿƲ¾ )×ëb.¥}þÖÅ\ÖE̺Ûnj;_ÒÁªÔ®b#oûʦ )x]ãŘ1111o6àÜ|‚ œ‚´¡s€‡°ƒôÐJ&=„½°Š0ñ±ÁV±äãDi8‹æžO<‘|ˆ^óÀ†K}Ù9Qn ç¶JiÂE_d“Q,ÙÑGN_<¦/{äL9D+[ù…-ãÊ_`öìÂXØÓç‚PäX/S,L%Ž<Ø2œyæ™öÝwßù~øP¹reþ ²6mÚØÃ?ìÕTp h$ºâŠ+ìðÃ÷ÝZ¿~ý<GŒÉ¶/Q‹-ìÁL*`î¼óN{ì±Ç¤¶8 “(s0¥™'[³­ æ ‚IÈ„X7_¾´6ætÇw$Àv”îßvÛm° …`O.à(ßõ…±¨‚¢J ÷ßÿþ7÷ØføüóÏ{5ÛT8ßœ;ꨣ Uxye|ˆˆˆ(§ üÖ‡rJëz6>ë-Õq ˜˜˜˜ <p$‡l_ÜcàB <Þa/ DJÂJh Û@©È#üBò”Ujøñà ðØ3OñŽõ¤xêçlqÌ—²ÙjpMŽÁ‹à±¡U2™¼H~ô_þ´,P‹ õðèà'Óž¾ÆBO,üd›Éc_€¸NqÄ¡Èo¿ý¶?Ô[ªP Pœ›]}õÕ<Áž!±õħB逰ŋ{ F[Õ2#ü©j¤8¨{÷îÉÖ¸ÒG¥™' ÐŸþô'?ÿñÆoø­]^è¨>1b„rÊ)~[;lkã\'mUà ù¬Y³äj!س.# 3ž«®ºÊÏ£iÓ¦öì³Ïzžj0ªÂ"Å Ä ä΀^Ër[hJb[àU6n}YšñJêSžÀÕª¯áùRI×§¸¥õ“ÿºns墸y—ËóÇÓ–ñ”U¨øës¹:C±®—O|75ò»f=ÍÍ?—nÌõ5^>)ˆ6111ÿ Ø@€#°.0 Zˆ7GHÈès ä ¢Ï›©|…Y„öN`%Èe¯xèÅ«%$Œ%ÕK=bƒ^ –l‰-ÆQ<ÇM—„°g0 @ CÂh™¸&ãØ$Qð豇hu4yx-šJ"ôœMÏØ\Äà‘c/vØK¯yÐr)nfÆÆßUm·ÝvöÐC97ó[ª9äãLœ¢ˆÊ#ªUôa’ª@¡Lúë_ÿjtÿù϶>ø Xàã›o¾ÙöØcïž¿QR਴ód‹™Î3â¬%ëæpHœðrå•Wz`èóÏ?·óÏ?¿p„Ïk¯½f×]w]â¾>#ùÈ>*¢ Å«½xÞçÌ™“Ì121k;T¹ sŽÕ…ür¶ * 1Û(ù…Ø6Ëÿ~>€«ÃêwÝuW_X}ë­·&’aª ù¿à@è§žz*Qq7/¶¤B̃ùtìØÑƒÃx϶MbþçõŒ1B↜¯Fµ ±¨Td‹U‡l#]Ä|öÙgc 1ëÔ §Øl÷eL¾F¾&³u•ªÐLâžmʹ¨´ã…ñÊ’—|‡Ò®-mÇsŒŸî=÷ÜãóG~ù;àùäo‹çíÓÙ¨4ëã|)ÆíÞ½»?O»‘ržÜ?üàßo¾ýöÛBï9ÙÆ.J¦çÞ;Ùž‹Ø²Ì . W_}ÕßäJTnÁ{ø¹çžëÿÖx_äÌBòMUñ.»ìbTóží.œù>¹æU”|“­ëY§~{Ù¦íêÛ„WÇÙ¬3¬Ë­ûZ•:Umże6è´7lîsìÀþ=­Á.}¨ŸžþÞŸ7ÈV.*øŒS±JEÛ¶w[k{f{«ÛºžUªVÉÖ¬Zc+¯°• WØòùËmô£üV´:-6±=þÑÅê·O¿óߪ¥+m̳£í«¥ß DógK[§ëötsmhÓ>›bŸ^ömÑ¥™íuÓ>V«Imo¶z¹‹ñâzÕG¶hrúövÅÉ·Ý¢kSÛåòŽVg«MlÒ;lÈYË#[Ûÿìj5·Ðx«ì§?ØGç¿k+¯´Ä§y]›;z¶ »ú›ýmÁ^áØM÷ÝÒv¾´£ÕÞ²ŽMýäûøâ÷ÓrÚF>f f f f • 8?oÐEŸ»8#dGê ¿àC/—°xéxS…'Žb£›PÇz,Cq°ÛÀ;Åu¬'úÌÂ{]ȉC‹.Ó׉r“‚æ¶(ÐÈ–áiY¤¯ÓÊF“R[&IËBi!|DòÁÂ[‘ô´áXŠ'[tÄ •c½Œ6’ðÁ¦D‡c³-­oß¾Äò{Š£zõêÙ믿îÍŠ:{‚£T0Al™êß¿^À_¹#ÄÜ´m®¤ÀQiç ôè£úñÉw&{â‰'|_€.TLqöN;íäÏâCuXq§;{ì±Æé õ 1.wkƒ¹øR)â6Ü:׊/ßìÍØ"Å ¬í 4nÜØƒ1TŸ‹jt6Zø?’Ë^ri€謳β>}úx^ÛL}'xÐá÷ãÇO¶¤¢æÿ˜ÿgˆÿ \àUæVR> <üðþÂÒÈxÐë_†¸Ä]î€xòÉ'{¿^½zù BÎ"Ë$À8€"¯Ö­[7þZ¦àÀ.*íxŠWÖ¼ä <”v}áÖéÓN;­Ðù|Z'ùH ©4ë«[·®ÿÑ¡¨ÿÞ+xí. ñ7ª@¤ŠfÆä Þ[;yÏúûßÿnúpË-·ø+ÈÀ’<ð·®ÃÕѺ!ˤ|Ÿ¿L¿|ú-keHm‡Ïf¿fÅ*[µl•U®ÞSÄ}@ºb° ¿ùKïRs‹ZÖkÄ©lª–-D"[0v®õo÷°mup ;øÅ#yȬX°ÌjtW(Jø–Gµ¶úšô'¼ò“5?¬uÒ™¥3[ÿí¶°*-u¸¦³ízç ¦höˆéVÇì7Y2}‘=åÆk¼ûvÈÀ‚³)g|>Õ^èR¬+Ÿ2{ÿØ×j5«ãE«—®°‡ßísØD&f f f f P6àHx‡@@ñ<@á´ºÀ3°o@&_xtºë)›ö`ĬC¶ô‰/¹bâ£q±§!ƒ°Ë‹ä±l5)ú^z&¤‰ÒÒ‡ÄK ¯à°xùȹˆ8ôñƒW lÅ;6™vŠÏ…ò*îÒÜJõë×/ùâÀ¯†|©*ލ‚øÀÉÐÙˆ0Ä%~øA=ÛV5â„UP¡MI£ÒΓ9ó«+ÄâŸþÙ˜ž>}º¯`~ښƯћl²‰ÿ…ŸêG|a£jáœsÎñqø•` ¿¯Ë­j|Y§ÚðŠù°e. #*|8w+µlÙÒ¿FHÇÿ ¾è®-àH±i øÌÔÖ­S_öBàˆ/ܼê‹ôûï¿ï«+9ì_g¥'ü_§_ ª´Ø ‘;îÎn“¥"’ÊHˆ×Bª¡Dœk`Pà¨$ã1ÎÚÈK¾ÀCi×¾ Q‰ ͘1ÃNå+@”ù^WÚõñ£ˆIþ¶ø‚j ^¯/¹x>˺}˜mdÜð@w¦Óöj¿˜à!Lù;¦Ú‰ÏÅ wèƒ>üðC»ì²Ë ¹äûürÌCPp”+Äø—~´·z ´J5*[¯¯OµÚÍ7IL׬Zm‹~IUûÔnêþ~Ýâ&àˆ*§½nìf›nßÀû„6%Ž’³jÉ «P©¢U¬šÊ%ºïîþÚ>ºð}ØRQ&pè©XŒ4ò¿_ÚË?´FnuZ¤þÞѾХ¿Íø|Z¡ãuhlG}”:Åð†Ùgù8Í&vbbbb g`Žú€€UpÁ‡20 IèäçØÄ  ÁñøƒO ;úSx}äŠãXOš ¾²­ü0æSch<äERæ€Ec«KkR´Z-À  …˜ÔÄà‰…­b8ÖÛK¦¹1q4¶>! Wôô‰EË;;qhñS ì%׸Š]‰¶ª±Maûí·wnf|)Éç;lÑ–'ü©\ÉF|~ñŽJ,Ãê!(úS/‹mtÜá #ª‚ø"“I|YÒÑ¥'çéàh$~½ç×èwÞyǡЗA¶w± ºüò˽OÔ ×íî‰Á„|€£|ÖǸa,¿ÖYS|èׯ_òÅ@ˆùgU ^xašø³Ï>ó@_ž"Å ”%|y¥jm3[¶8?,¬lTjÙ²ep”kL&þÇtÇ@þ7O:é¤$ÞÚŽ^yå_µ±|ùrÿú¨jG€/|9(Ʊpû[øÿI,nW^ #Å!€Ôl;ôÐC“sÌÐï½÷ÞY·óúÍëcI€£’Œ·¶òRZà!ßõ…ïGZw´d‹[–]x~ù;fk#gÞAeYßË/¿ìA5âè=^DlƘ)+…7o¢Â8“Â?PåÄ#¼wˆ¸Iïcú19 /@$v{î¹§ß¾©J?ùÑ–öù cäâ³GÃoüÜvrw9 iþ˜¹6Ü\k¶ /žî¶Š½´Ï3Öã¥#¬Ù) *lö|Þ–ÏKUú÷ùɶé ½€#ß z<Êšî×ÜKJ}sÇW°©Ñ¸¦4º6÷‡YöÌN#•ŒÍ ýˇ6ü¦/mëã·µî÷H zÿúŠª°ŠkšÛ†öR÷ô|º§µ8b›Ä÷Ém°…Ó+ñedbbbb’ ”3p”:&…gPif àr°éÁ¤Ã>”£› å‹ÀD|aôáSxãÉðZaÒÑGŽŸÆ c#×z››p*)ÉG Ð$‰ƒLzMZýptL\ À†>-2ˆV‰BÏÅx²c‘:Ûq¹àeÊKçX,Ñb•8zá…’_$ÃíT©PÙ»wïî´E¦M%ŽÎᜠ3Î8#¯Š#¾„éî_!ªŠ £ì33h霔²ÌsРAþÀpnoOù~‡üÝѨ:ÒyET9¨Òml#Îá ´Í—æ³Ï>; ì «Jº>r~1Í–¾L’?Ö“‹ørÉö¾”‹8;å_ÿú—ë$‹mÌ@I3@Å•†UzTñ…<¤|#¾Ps°>ÿWÀ1UÚŠlmGˆíµ×&‡#‡ €£æu ‡üx˜‚leYßO<‘Üé’Eyä‘Bçeéù,kKEï+üB}ûlý¦_¿~}F<ïuTȆ`Qº;*U³"ÞÇx?»øâ‹ý{P®­p¥}þ4NQm&p´xê"ë¿ÝƒÖwîùinÏïù¤mÓ«µÿãn^@4ôª­ç›Ç$v‹§,´'·yÐV¯äãZŠÖ5p4îùÑövo÷ßõa/kÔ1U•Y•².ú1p4â–/lÈŸ?L9ºO'}ß'­Úê±æ÷ÚÒ™K¬÷O}“s0~n'lÖש‘ª5¨n§MNUQ£ûåƒIöêAÏÂFŠˆˆˆ(&å ñ .øp DüZ¢7?Z.ìÀ'„[ˆ—ZlÐCòÓ8ÈÁ'hñ…ÿõ]ÏËÀIé°Ó<›Ì½âá«ùÈÛb‰ò%Ñ"ða .¾1k‚ôC;Ù Ó¢BäŠA«ùHN¢%A€Hȹà"æSðM>å#=6ÄÀ—R[¢­j|heKÄ—®o¾ùÆóE=têÔÉW`“ëlt”òóë*D •-áõ\G[mµ•ñ«/į÷üŠ…À %õÙ*Ž8ï *Ë<|ðAkÛ¶­?Ó@…ª!æÏvmÓcMŒQ]ÄÃLàˆmbTh»‡m³Ý…ÜP.à(Ÿõá_pįå:çû\ÄüþøÇ?&wÊ“Ý}÷Ýgä"RÌ@i2Я_ÁVXUåeÆÉ8âK-_ìÙz“yèôÚŽˆùߢÒHD@¯;¼n2‡}öÙÇ®¿þzoÂÿ?lH9:ç ¹'xªƒ²XNĶ>Ö$ #r8—ŒC‹3é·BeŠ›/°RÚñÊ’Í‘¶´ÀC¾ë ߨ¢¡¢,ó}…JN͹}PYÖ—ùšÍßÛ£©7ož¿6Â3)>ÿOÚN}ÑEùjÝ8âGªø;þä“O¼[x6˜þ>6àhЩ¯Ù˜gFÛÙK *hç|7Óìú¸uüÛžî°èÝýüŽæ¹*¤Ö½¶Sìõ#^´‰oŽOú0ë8¢ jÀ®¥ tà3‡Z‹Ã['s¸·ú- _R&8šúñd{å€gÓî¤ÖõŽîÖ¶ïŽIèç:9€hø ——6Öý‘‚j¤ÚñÂ]ÝÛ{'>zÎ&01éG&f f f f w6àì,<‚¾€䪊 3A'?° > Ó vbÐÎ!ž¸ø€•(}ùCј´Èä#?úЉŒñi!µ©^ŽG&–/)aØãÇš2M Á^¢^}µNä ‡…ÐW±Ãù¡ ãºn=Ä8ØÑ‡…}ÙâÇ’JTqDU '¼¶–yAŽ€Ç{ÌkÃs42͹cÍO<áÅü NI|øA=pÄÖ9Ýé§_¿~É/¢!p~ËWý²Ì“J"ÝNñ8¸—_`ùU:“´%$8ÂŽóDX+4vìX°8¸¡\ÀQ>ëÃ?ü–A¾|°•N¿ó%>Ü„_.jÓ¦ÿ"¬­7ØñeXU¹ü¢E–‚2£'[»íd“¤EÚë¦n¶ýy»$2G+W´S>+-?ÏvxÌæŒšm§M<;‘sˆ÷cÍïK£’`‘‰ˆˆˆ(”rŽøõDø„€U ÷{àÍ>¶ØqAÈÄë “>8„@!úòWLÙÈ×™$x ~147æ@BFŸXȤS,ôà$ôÕ:¶h"XIûЇA5á0`ñ´ÒkÂòÉ\rH>Z$rªŠ´xôòE§¾æF 0$ôê;•×?´C^"àè‚ .HpÍ÷ ކ &uvgRxÆ€ ¿ªætp«_ˆ38›*)pT–yöíÛ·Ðy]»võÛl¨âáP_‡Jï¿ÿþ¾› 8â:[ï6Ûl3oƒ½Ò]›À‘~1æ‹ ¹b« Ä65ݥΠŠy JŠ* ¶çAT¡ñ¥*RÌ@I2À9-|†0©ÒÈFàÞ¬Y³œg…‡÷â_T•c>À‘Æ +'ˆŽCu!…Å_¢÷Ýw_o¸óõ×_çtpÔx°íŽha¾´‡ ­¾¨cÃkN¶[ s& ·I‡rK¥ŽJ2^Yòâ'þëÃúŽØö°ŸIüx˜ 8*ëúbxލöá øLа`˜©/iŸ¿ÝœAwB ·‘r¦“~ä #ý€Gáÿ†þ7à辚·z#¬8zõÀgí—Á“ G:lž¤Ã©Øôޤ/†»­Õݶ¾ï®í3ŽžÙñwËû9Ê·Ý98­ jmG6¸ÃV.ÒçóÔ°=_;Úšvß*™C.mwÆö¶÷]$ºIïŒw‡`a=ß(ØÞ÷éŸÞ·‘·ç~½Kœ#3333à3PÎÀ‘nµ ÁØ®@K_<Dø†Ž>r6ÈÀ d§XØÒ`“£×öáñ í[üá5.2úŒŽéºÅù“a0‰0$€‰pAL=>Ø@ádéW‹•¯úè%ȃ Ò|h–TÑW"àå_åWyè§¹³.âqWÞ´Ûn»ùƒ@q d CÛ"ráN>ªºáüŸîÝ»:·ß”¢‚‡óŽŠŽø€ÊC}˜æWÐ9sR°J •ež™áÁ¨T#ü?{ç'eqþñŽÞé œìD¬`—XcÇ‚-öü£ÑtIb,±%öFÄŠ=jbWl(Š"ÒDQ¤#(H‡ÿ|gý½7»·{·»œw'Ìóù¼;Ïqè{”»ò¦ ØK/½äŸâ‚ÿ• Á̈ÿP:AD<^;$V°•¤I“&^¬3€*Ž (;xPŠ¥òú’X(pÄÀÅæ~¡%7Ý” …+¢èóevÈ!°åÎ8òB÷ ƹEݺu“È·ßpDpžf¥3˜9üœšæK̹U«VÞ Œ'XEŠ(¤¬HÔ¡ë™ g q¸3+ä Îã÷AĹ[€Fà ±R‚¿5™gúÈž–'¬ñ¥ ·è†²ÚH«ýª8â ný]`;Îg¼ïƒ*Ž83‰³Ê ŠVƒVp”k¼ªªKmŽªj~áÏ¿¬@âI—ÛÕN=õÔÐd­xVT{î¹åb„ @¹®GœqÔf›¶V··V)º½¥M–qûdæŸp6ñd+iÌ-UŠMýÚîÝôu“¶Ø§ªÕôŠ£-ÏØÆv¹v@2ÏŸlÏýTÒ‡ÙúÜílç+÷L“©Ãö?¶FŠˆˆˆÈ¿5 õþ.Sp0C!‰Ñ€7ðÁ(`[üáÑӃЙGô凗â)öòu¬§ÔpYl}@Oc(6:ñ8ÓÇŸ¸È+%’Ê—4˜ìI\c0ôЇ.$t²m‘‘,~JX¶€ÐC܉`C_2üÄ LRÈá¹tƒŒ1éKÏx’´UÍùù'}ñÄ3ˆ³øòÅY>Q¸Ìeêü—?%Â¥ñ«X¦Õ{Òeþœ¬ÀQÝ’:Ö¦WÙª£g=gïgõšÖ·Ÿ¼yl²EM5Êõ”³ê޵ml»^Óß:íÞÅ–º•RoœÿŠM{ás¥™´™g…[ÕÚõé`?dpöPïa6ìÜĦ^ãznUÒV·‘n=ËÔÙìË´‘‹ˆˆˆÈVŽvr9pe‚F©›Ø#PˆV˜öÀäŠÆ#6`ÂDˆÃQZ<¶øj úŠëØÄ_¾èKc#ƒ4VªWÉ«œ*1ójl¹”$-}’" MJ}ÅVëL¼½@ú">]5!dØàG\ÙW±à!úF´Ê‡>${üVr|$×JG|IãðjýgŸÿüs˜3Oí,à¬þÃÊ!Ï<†`ÀB³fÍ\ f#FŒð[±Ø’2`Àckšˆÿ€¿ýv*­8â좑#GúGGó…ŽG‹XÝ ž*$ žjêdCË9ž›'†r°¯ˆ/l£Gö]æÌË¡½Œ Uñå’/ÃáúÈo?bk*/E—]v™ÿ½V?³p„œZ‹>ùä›3'õèmdk3^UÔEÀÿÈWs±1(FÞùÎ/ü<*äŒ#Æ(f~üí¿óÎ;=ÊÏ,Ÿ›|Þò·0ˆ¿:[N”`¬Lºêª«üïŠäÙ¶eJ¶œxê©§&"¶`ó³ÉÏ­èûŽ }ÿ”KEm÷ƒ6¶}‡œ˜rÆÑÇN²/ß#ñ…™þâçÖ±_Ǭ@ úlg…À6ï_žºÏYøÉBûüî3uVj{wéa›Ø>÷•mo/vÅÑ~dÝö`¨„†vºÑ–Íg—AeGnûÀ¹Ø:¸ùuÙ»{™¡ã^>íYûhØø4™:½.êí·ÚE]ß.˜4ÏÜöî4YìÄ Ä Ä Ä T^Ž´UDwôÁvÁª °ïºÓ@Æ%|Drõ…{hU8†|O`¾Â9ðÃàÂH}ZÙ"/=}˜ñÀCB¹ôåZ‚äK'Á0°’ŽXJ™ôøè¨ŽM'ñ´²#†&¢8ÄŸ ^}ý[?Ù! óÊ´!ì¹ÄtÆ‘óóÄþ#/°!s²-$ü¥ËDÏÐy’—¶š ˤaÆù›xÉÃuÉ2[¾pñ´!n¨C £PžÉ³u„mV¢bò ö%ÛÓÂ/TüçV_DÓôXðŠ€#âl±Å¾fðP.à(¥Íþί2à ˆÿHk‹` Û!ø2˯!±²HÛŠ$g«ÇâùFªHl ©  ‡;ë‰N™¾W_}µ_5!ÐC[Zÿô§?e=)Ó_}ý]âç—/æáÊ"ÙÐ(ð»ÌJªŽˆËß+ΫŒª8Ê5VE€ƒ|B`E2Ú[n¹Å×O²8’,³­h¼µ­‹€£Ì1§OŸž”eêó_øyT(pĘ…ÎOÀQf¾™}VïœÎœ93SåûNáç2àì{ï½—Õ6ÖòÙÅçÄï™V–ÊîûŽ[meïŸì*j×8z|ÀðÔ“ÃZ5Ê9[ÚÂC´ÿ½ÑͶtö’4ûLà(T¾ý»Wí}w 4TUÀÑi Î)l=¶Çý6û­ôŸ“Là(Ì+äÇÝðž½îV-å¢úÍëÛà™gY’ÔÏ vÏÿ”Myhr.—(ˆˆˆÈQZ 0à†^áðyÐér¬·uÂ<°DlâBȱ„ƒœ¤AOù(–â*Žl½Se/8BŒ¤à™+qµè2c+qµÎ$ñ¸a‚øH¯1gʰG¯" ‘rBþŠI åòS[ù£€R1ʽòåŽÐC9$Ùz}ùå—þñïá#l5a@ÙóL@%®x2«B°`…_X½n{“- PenQ‘m¡yâ' Š¹ðHìpÞûÛß<ø‚¾Ö >Ûq°¥¯GbcRxîÒÀ“mÅ̳‡8”3fL8”ç{öì™l¥C 348<›/йˆ/ü×_}òøð\vQ+PQXI@‚9S§N5VÅp`{¸­U_ÞÙn¨š±Zƒ/»Z5Á¹\¬úáZÄ_Œ÷æ›oúUl7e+0Dá—ú\¿K²ÍÖòEY[meÃáÔü.=ñÄ~…äÅ´9üýdÛ,gÖ…«²ÃAýl?ͤÌ™«b¼µ© 5 ÿ™¡|s=ÅKú|ç~^hdÒo~óÐ<ç@rýœÉ®ùöðóÎÃø»œxš¿/üŽä¢ðpnVÜ€eûÌÌô0b%-?£<‘•J™~¬úcESæïŸËø…¿lsãgžÕ¸ü¬dR±ï_fœlý.î‰`º'ƒA+-·;ÚÞàïœNžu¦Õo™„Ýõ>›óÎ,ÛÏݼíô¦ÚS5¶}üüÖjÓ6^®—ÅÓÙsÇ>ilÙ&íÉbÙV ísß@ •ýQ Ú×ÿïEwó^ÔußnvÀ‡%êûzÞnß|þMÒ‡Ùõúþ¶Åé©ÃÊsmësé.¶Ý…:¦ÂÍ{ñr»«ÃM¶z·˜eTp´tηöÚÏ_²OýØ?‰®Ì3c•ôà™g$õ\±p© írK¹ñÒ½b/V V V V [j8b«šp pÀ€0EÈÐAàØÓçÒ r.°Dø‹Sqñ•=˜d482ô"õ¥Ç?ca œ>-~âëy@(̇¾òE/lè‹Kñ5Fj­´SKH4<ш‡É“'û›Ø\[Ã°áæž/c¬Rb¥ÿÍϼ!-6Ÿªòû¡äYUóÍ'Û9G†–ÿF³EЋ+ó‰TùÄ‹6±Ù* ß=V6Î;×ÿ|e~Ïæ·62ÀlVÚ±M†í™•)k3–|YñÄÛ´iã·Ð1W~ªjìÈà×öR¶åÀW5UÕxßw]ªzÞ…Æ+f~œ9ÄÏ(‡¶³2”§‡²Â@§2b[&ÛYíš1ÃÕD€SN¿^“»kjÚ±™µß¹£[USÇf½9ÃMs€ŽîîjYqø;Ú~—NÖi.¶`â|ûâwŸµˆûötÊŽÞi˜bÚò¯—Û­Y™ß3Wu½vÞ 6þÖ±éƒÅ^¬@¬@¬@¬@^¨ÀüÁ&tÃvÏ¥ÀxBðyÀ"Äã Og,d*’cý¶5ZHºÐ9­äÄ$†bSìµ>Ûň++++PË*À¶#b†ªãáÕ=^-+÷:žbØ®];ð6‡ÃóÄ8S´TÀý(iTbuë×õ€QæSײU õæX×ýº[“MméÜomÆk_Ú¼1sle%€S¶XQ++++P¾5 ñHNp°<‚ Lü9-6`èÁ#‡$§Å–‹•GÂ&´‚ɉ¼Ž’ öʃ¸ÂB3žðéD^ޤüO2léKçØÜ$§Üéšp¢Òh²ôIœ¤\±i‘‘¼bÀc1q%®I‡Å@¦ØŠ‹±ÐÑJŽ_h‹Ž•Hä${åAË%ý(ÇGŠˆˆˆˆˆˆˆˆˆˆˆˆˆ¨á Ô0pÄŠ#p° @!¬™.dÒa‡ÜóDȱG'ÜPHx„dË©;'tr.bà¶`$a|ÙÒBä‡OÞDðBHÈa`dL  é‹§ÅW¢/qÔøCPorˆ–è•‹cýÊ#  1Ôb á OLH:dñ©j¾$ñ%V V V V V V V V V V V V V f+P €#°0Z.0@x°DàèhC^¸…d™œ™· ã‡øqÁ(ð»¿cýØôÁJÂUIè â@´øfæ@?3¦å&ò%’f`]ò£X$M,MJq±…û®ëcüA‡™&нâÑbQ,H~´øpA´È”-~²u¬—Å­jT"R¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@ W G`€=`Â=Ô„ŽKrÇ&à>‡À!Ô§…þ„„ƒ–¸ø‹$§Æ ãÓjååD‰^~ðy !% àB ¨Ä z&G !ÀC_HrúĆˆ¯>:ÅÖ¸ÄB†?vÈi‘a/Ià”ùÕHè3/Ùc$†‘bbbbbbbbbbbbbbj¶µ8Œk߀ƒsÐ¥-aè ôØâƒúÈà…c86‰HvØj\° ú\ØCĸèñ“;dòDaƒLù…c£ÏJ œU™!$0Ä€øÑOŸD•œcËÙ(al°×ØJñН !ç‚À‡ñ‘Ó‡„?2.Ù;Ö6ÈñÃŽ>ŸªæK_bbbbbbbbbbbbbj¶µ8RBàH[Äô`ö I¶à ÈÁ+ xù /]f‹;.bà/ÀHq‘AôáñÃV¸¼ú¡x§ÎM žÛ¢L#[f@ ¹’ûnˆ‚„|‡DÈñÕåXðÐ'6± =©^Y,ú! „Ÿl 2^ñèk5TܪF5"Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ôpj8ÚÉM F@¸—õi!6`\™ýpÂÅ£ ÏXÂAÀ24žc½ž™0úøÉ‡¾ˆñ±ƒ”c6»”EŽWò%&Á@$i0ÀdJúgH¶»hU bÉ—VB¯¼4†€Ÿp¢òôQÁhC_l£¡ã5¦dä ¸ŠÀQªñ5V V V V V V V V V V V V V F+P €#p áð` ôw d¡ :ú`€èO C§-nЇ<¶ÂVW}Z|!tòþBBb,Ä–/<˜‹ü‘WJ \©¡3Pp3 C K§¤h5öš¤ZtØLR,Éie ¸C‘9ÖzùÓŠ§Pz.CLt’;Ö÷iãV5ª)V V V V V V V V V V V V V †+PÃÀQo7}a ``\` …è „A†ŽKÀz(´“ aÆ@†\ƒ´Ph/åF x=¹HOËø©u¬'|+%ó% ª¤T%A« hrô%ðÁWþŽõ¤¸ØÁ3Y; ƒd/PˆXðj±!/ùÓ—ŸV9‘×k<åŠÝZGuêÔ±æÍ›Û†nhðÓ§O·¥K—2fN®}ûöÖ¤I›6mš-_®'úåtùA)~Hó‹ï_ö­bê’=ÒCºÅ[ØÆoìŸ}öÙFÒÕe³æ]m“ÍŽ´¦Í:Ú”ÉÙŒi¯—µ¤¤5iÚÁšµØÈV­Zj æMruü¦œ]Ä Ä Ä Ä Ä Ä Ä Ä Ä TVŽØª&0 Fà@!ÒçË;ؘX­@úòQ‹žÂÿd‹?X…|°…G‚KÄ”œƒ¾üi!ôð´òHõr¼Ê8‡:M,&jP’‚Wèà ”qlZbÈ5⪴€>šˆâKΤà‘+62­B¢€Èƒ[|äçXozì•OÑ[ÕJJJl¯½ö²3Ï<Ó:vìèB–àÑÛo¿mW\q…­YSö¾4mÚÔ.ºè"Ûu×]­qãÆ‰Ã„ ìÆo´Q£F%2¾Ìžzê©i²•+WÚìÙ³mæÌ™öî»ïÚĉÓôêüò—¿´N:©›³ýÛßþfsçÎÍ©/DQèüˆÝ¿ûñ\é0óæÍ³¿þõ¯•ÚåkPïßàÁƒmë­·¶Õ«WÛï~÷;[¶lY¹ôvÜqG;öØcýÏÉÿøG[¼xqÑï»Æ[µj•ýö·¿µ+ø›’NÙÆ -ò­Ëa‡f»ì²KèZ)_Q^•:ÏúÓŸlß}÷õ£0/Þ³Hfýö¼Ü¶íý‹¤7^¿Æ”zõ¹À6Ûf°Õ«×$Ñ—1«müû·Úˆçæ~¶c-Ëê¹XXXXXXXXŠ*P €#¾ÀsËÎÁó‹ÌA_¶t³+ ‡Vz0 ì!ì…U„qˆ ¶Š%'JÃY” x<ñDò!zå —ú²s¢ÜÎm•Ò„“$8¾È ’Q,ÙÑGN_< Ó—=rFÑÊV~a˸òX„=$»0öô¹à9ÖËKSQÀQÆ íª«®²vظ9)üÚ¬Y3»í¶Û¬[·n9í/¼ðB{íµ×=_dùB[}úé§vöÙgÛ‚ ÒÌüqk×®]š,[çÄO´É“'gS$+f~ pÖYg9TF‹-²}öÙ§2³¼ôÕõþn·Ýv>§›nºÉ†Z.¿#<ÒÎ?ÿ|/?ôÐCmÖ¬YÀ(æ}Ç£VÔ,“²'›Bêò‡?üÁöÛo?¹æÝî½÷ÞËÛ¡š #p”½ÐÙ€£zõÙé¿ø:»C†tþܱöàÐÞ¶f5ö#Å Ä ¬è×þÖ}`©Ÿú'}d#Îza},Cœs¬@¬@¬@¬@ž¨%À‘@²æFVà—nn±Áƒ7@Ø Ñ %a%´€„m ƒÔð ɱÈ=q„‹À‡øŠlB_Ås¦“«Ø*¥Íf«Á•‚ÁcC«b ØÁF~ðŠ/Z&¥É†zxôŠ ð“iO_c¡'~²Íä±鮂ˆ-<@¬‚X™ . @IDATpË-·Û[XQÑ¥K;ꨣl·Ývóz|† æ·Âà3uêT<-\¸Ð9ä;üðÃ{4hM™2Åó!pôÑGùULmÚ´±=zXÏž=¿ó0¿úˆU+á¹8zಮv!y}óÍÚm+)v~ŒG¬¸bõU6úꫯìþûïϦ*HVï_ä°Rì€(ædrŠ}ßÃñ Ž ­ËGa{ì±GZí;wîì·`"äwa̘1izd¬„˶*Ͱ:8Ê^ôÊ€£nKÚ‡ïÝhŸ|ôˆ_YÔs³£mÛ>¬PâÏkŠú÷N6gÖhuc++°žUà˜qƒ­EV~Öó?˜mõ¹w=«@œn¬@¬@¬@¬@!¨%ÀX˜-8C‹Œ–K }pù ³í:ÁJË^ñЋW«›ka,؈°A/K¶Ä ã(žc+&Œ !ìLÐÄP0ZW2ŽM {ˆPGÉÃkÒ¬$‚°AŸÚ ‘šãÒD±Ç;ì¥W´\Š‹_‡±ñÁ·àGûï¿¿±¥bÚi§fãÇ÷ýð¥^½zXmºé¦v×]wyž•AÇsŒ‰~ýë_ÛÁì»O=õ” 2Äó!€À˜áÙ+Ý»w·;î¸#Ùòvà 7ØÝwß­GáʧĠ ™bçG !ptòÉ'çÜzWUéVçû9äï½÷Úõ×_Ÿ6•Ê€£BÞ÷p¼B£bê’6×)--õsD>|øpûÇ?þ‘iRkû8ÊþÖdŽøSÍV5ÀÆo¾þ¼œcçúÛÁG?›ÈßyãRõúŸ“~dbbÖ¯ Dàhýz¿ãlcbbÖ¶µ8;oÐEŸ3GÀ2#õÁ2°càÖ/À¼°lЃM(Žc=–¡8؃mà‹â:Ö}Æ…°Ã^—ò¡E—éëD¹IAs[”idËÀð´LR“ׄie£¤ÔÇ–$UZ‘|°ƒð¥("éiñ°Q\lуV>Žõ2ÚH›‚Ç®[·®=÷ÜsþPkòŘ/È•ÛzXmÁ#$¶½øâ‹F|V(ñ¥ÿÛo¿MÛ²” àϪ¦Ë/¿Ü‡âüœ$g³T'pTìüH¼:£ê~ÿB ‡¹4tÐAÆyM¢B#ür½ïáx…GÅÖEsP[ZZ½ÀÀž#F_¿GðùR޲W*p”Ý:%mÑjctú¤ÄdäˆßÚ{o¥þN%ÂÈÄ Ä ÔŠ Ô©ûÝßÑÕÜzåO…øÕ4pä?+Ü4×8Çü«‘nYÝã¥{±±±?ü ÔàŒF2€6âS+CÊ@á´ºðÅŽYdò…G§Ë±ž²éQ`¶!PIàq‰¡¹bâ£q‘Ó‡AØåErÈÇX¶Jо‚—>L=}H¼ôÈðaòšP3ùÈ6ŽˆC?xÅÀV¼c“|°S¶Cß‹]¬­Aƒf¾¶×;ºŽ­~¶¶×N¿²¾»ÿ%QÝÇV.ç2 )QD&V V Ú+P¯i}Ûî—;Úæ§meÚ4¶:%©Û¥5«VÛ’ÙßÚWãçÙ‡7ޱ©ON)—[ÓÎͬß»ÛF”ZIãÔÿâÖ¬Xe³ßeoüòe›óîìr> ŽÚïÜÉvøukÞ½…Mñs{ã‚¶z¥îySá·:k[ÛôÄ-d6éîñ6î¦1Öq·ÎÖë¢ÞÖ|£6íù©6òׯYé¡›XßËv³&›yÇÕËWÙÇÃ'Ùkç½h+¿ ÿþ¥âòZÌx‰O·–¶à£ù6êoØüqeÿH ÇïÜ¿«mwaokÖµ¹Í|ãK{ýü—måb¾·DŠˆˆX÷+P €#>`€:\ðá…Œ(}  “Ÿc?06|ðŠÇŸ~&pD?õòÁNqëI¹„¶â±E¢14žWTô’9`e¶Ø‡k´š-ÀàCb$¨ÄÞ`«Žõö’)7&I/„\qÐÓ'-w0Ä¡U¾Ø`/¹ÆU ì ڪƶ´SO=Õ¹™=óÌ3ØSµjÕÊþûßÿz3ž‚vî¹çfuÙyç“­=×]wÝwß}yG€E<± "7m›«.àhmæGÎÕ U÷û9œëS¿~êF›óxòT,p”í}Ç+8*¦.>ùŒ—ÒÒï8p2dˆñšk®±ŸÿüçÉèœÄöO¶ƒòd?ÑÍ7ßìeê£c;gæS¥W[ÓÀQ:%¶ë€«l«íÏQJ9ÛGïÝÝ;nDNýêÕËmåŠ%Ö aË4›çŸ:É&O3R·n=;æ”1Ö²uÏ4›lÊ€£-KmÐO?J\ÿ–´p  ‘bbj² Z5´A“N¶ú-U˜ÆÂÉ_Ù[M³i½ÅvĨã )Mù]ç_¼äA§ºõêÚ6çoo]÷-uÿ43ë°Kç2s·êgæ›_–õ÷Íߨ¸Þ·ÙoÏ´^÷±Þ—ôKôw¶»ÁV|Í‘etÔ˜¬Õ¦m¼à«ñsmøöÃlÇ?îlÛÿš'(§ˆ³”6Ø&ûB–Ì^l÷oy—­ø¦x ¦˜ñÚ÷éh>Uv¦åœwfÚ£»f9»ÑÕlÐäS­i—Ô? V/]aw¶¿ÉV-‹GõþÆ6V V`Ý®@ G©) … 0ý-HÞ ö¡x-X¶È¸ âã§>¼b Ï`<ÙáÖA+ŒC:úÈñÓ¸aläšcsN…’|4%IdÒ+iõÃqБ¸&€ }Zd­ Ë7lô\Œ';&ÙÀ]Ø!#.¼ìBré›lWÃ*8â‹+_`!V3<øàƒž¯è…•€@`Îe—]–Õœ_ÿûß^G\âWtÆ‘‚„«EÂílÕ­Íü˜CuGÕýþ ÈáéfB~æ™gú·'çñ=¨Xà(Ûû®ñˆ[pTL]#“JK«8Ê7ì"•”¤þ¤¼úê«ö«_ýÊ«‘ýë_ÿ²m·ÝÖ÷±=z´¢ O¸èTÓÀÑ> ³MÜ!Óe´ÚæÌm³g¾cÍ[v³.ÝörÛñø3hVpT#›ðÁmöò3g9a;ððG¬[«íë…Ÿ¹ΗYËV=’±0¨8jР…töT«W?õß}컯¿Í˜öl¤XX®Ànÿ`›ŸºM’Ū%+lö¨YnõÍ k½yT4ó«£ç›kõ¾'±kܾ‰;n°Õk–ú»ƒbþØ9¶tþ2ë´G—ÄfXé­¶réJ<ƒ¿/ùӸ߷×ñr•GáÈ/uq[YFc¯moþjD™ @.8 Ýs7ò¢Wí˜ ƒÝjª2 ÿÑ]ï³9ïÌ Ý­íŽíí°×ŽMdc®eoýîõ¤™XXXu½5 ± ü@¸„@"þ‹ ¢>!ÜB¼ìàÐb#À†øð|‚_xì!d|¹Q |¤ÃNy8Öó´èO‹ä´yåK ¢IàC\4J~h'dšThƒ\1h•äôá!Z ħ=r.ÆÕ Å€”­é‘ÑÇ—R[ÐV5ž¦¶å–[úçœsŽÿâé;¼l¿ýöþ +&øß~ûíY­Û·oo=ö˜×é o>ÀÛs`±Ž'¼A!pôÎ;ïd}ªÚC=d#GŽôöž¬Íü3ŽX…óÙgŸeMP,sÛQVà „Õýþ È8âl#¶©5nÜØgxÒI'OÊ+8Êö¾k<(8*¦.ÙÊ\ZZ}À‡ÁSÏ'Ÿ|2IeÚ´ivâ‰'ú•IlEã }§œrŠ× vØažÇî¼óÎóO#”sm9ã¨CçÓV­Y³Ò‡Û’ö¥Rumw8u[½j…5oµ‘>èõ@göÆKX¿þW¦ÉæÏë¶‹M´›éå_|ö¬ßƶ½Ûš¶Ón—&¶ ¿úÈúw_[îž–å{ÆQIIC;îô‰>/{ëÕßÙè‘W7¶±±5\A—­b!•áÛßí·¦…iÕ©çnµÜÝÙšUÜ¢9r]@£æ§žŠ†èõŸ¿ä·‡ÁoqÚÖ¶ë?÷‚õ4þÖ1öæ…#ì€Ç³¶ÛmèîÄêXýæ ¥öíŠo8_´Œ–ÎYbïþõmûhØø*ŽÞþÝ«6棭ÇQ=mÀ]” ê¸Ûš_g«WpkX8åŽ*o£ý»Û¾ÃNœå¶¡=> ýŸû>0кò£ÄæÞÝn‹Üª¬H±±±ëKjpŽÀXx}7úàЪ"äÂLÐÉì°I˜1°ƒƒ>v\≋ÄŠG_>ÂP4&-2ùȾb"c|ZHmª—㕤ò% {ü@ Srè˜öJˆ>zõÕ:‘'@&BK\Åó ¤¸Î,‰ (bÆ£/ û²Å1Žð)hÅÑ£>j:tpnfáv#/Èñ2`ÀûË_þⵦݢE ¿ý É'OË8bÛÍ#<âãsè6UC!päY^XÁJ˜µ¡µ™ã†ÀQEyð¥æÌ™™Tª«î÷O@À@ÎO~ò»à‚ |žcÇŽµŸþô§EGÙÞwÇ…GÅÔ%[±KK«8â/=…Õ["êI]Ï?ÿ|_×I“&ÙàÁƒm£6²xÀ›ñ¤CÎ&ûæ›ô›ðÚtöÖ¤iêo ÿw›;ë=M±\˹D!p´è›ÏmØÍ=íÌ –¦Ù»¹‡õØìHÛyÔŠÇ響hÏ>~¬|ެĎó“n»¶¤Ê¶…äÕ­[ߎ<Ê6h“Õ 8fÔÕÆ9J‘bbjOŽ5È6غm’Ш?¾nï_þN¹ $ŽiÒ±©ÿéé‰h®;Ïè·JF·œÚ6¶¿8#Ñ/ž¾Èîéq[Ò‡)䌣ªÚªÆ¸\ó®¼øUXçwÜÄS¬Y·©¾{½»Û-¶dÖ·I¿&p”ÏxKç.1<»Ä˜÷½Çæ½?Çß°M#;izju2‚/_™fOî÷×Å—XXXõ¥5 õqu>!àE«„{€#ÑÇ;.™xl úà…èË_1e#_gâýÀ1ðƒˆ¡ÜÈ>„Œ>±I§XèÁIè«ulÅD°BûЇA•p‡˜<­ôJX>™AÉG“DΪ"M½|Ñ©¯Üh)¨tÒ«ïT^OüÐyAÀÑСC­gÏžøÙé§Ÿn~ø¡ç+zÙi§Œ3Y Ì3WB¿¶mÛÚO<áEo½õ–?Ã%à(üR|ÿý÷Ûµ×^ëc„À[åxêZ&q7 ÕÚÐÚÌqCàˆy³J$q~M¶9d³Í%«î÷O@Ž€#Î8úÏþc-[¦–©Ÿ}öÙ¶É&›x ƒœ=ôP›5kV^€a¶÷]ã«ਘº0F&•–VpÄáâ¬6âÉ5o¼ñ†Oƒ•jÇ›ZÚÏjÀã?Þ±êˆßW(XZ€#Î:ã—K|ž¼¬\±Èn½¦uÒÏÆdGß¿·}ùÅ+vÖ…ú|1›=ãm{xØ.î̤³m·½RŽ&»Çp{öÙÿ ²O&¦ÿç»2àˆœr¶a»^IœGß`¯¾ðI?2±±µ£™«ƒÈjÅÂ¥öþ?Þµq7`Ë”¿Oèv`©í÷È!É–/Xjãoù ésˆ‡8‡tk“kÓž`VÀÑÌ×§Ûöy(-Ì­zïä›1)À&Ì?>8*d¼MŽÞÔ -[ý‚CÛü|{w ÷îI Oí÷°M勤™XXXõ¡5 íô]Á"¸[Àh鋃(»áNéè#Ç BvŠ… q †!0 9z]aŸÐ^ø þÂT$£eÜpL×­œpÈ—H†dI,$bù".ˆ„àÑム&KŸ¸š¬|ÕG/™@dò¡XPE_…€—ýï䡟rg^Ä/hŸÖßÿþwÛ}÷Ý›¼¶–yAހ䅨vÕUWeµÜxãíž{îñ:V€\~ùåylc«4dÈÿ4)ø8ú>ÏlY›ù‘g±Êjm,bæ¢ê~ÿä8"¯ý÷ßߨvM™2ÅœÐe{ß5±rG"ýÿ—úR¯ñŠ© cdRiiõG{챇ÛJµ< 8úë_ÿêA9rÊŽx Û;ìàÓ=ꨣì‹/¾ÈLÝjpÄJ#V‰&»Û^|úu³¶™ÀÑMW6t«V§Gÿyp›6õ…rÀO]ÓÖ5‚ßq};[¶ô«´q*Žî~|äÓîÌ¥½žØöÚ‹ç'ýÈÄ Ä Ôž ”4®gè˜\hãº~â䀎âÑBøQtÄ‘¿âb§ØGö ŠK_6‘ˆOúÄ宼ég?ûY²ª•#|Y­Œ6ÜpÃä ­Î.ÊæžÀAÙù¬8bË ‡x:Ö¸qã<_]ÀÑÚÌD«8ªî÷O@NqH3[ 9âÜ&Ê, §Ø÷Õ4üAûí·Ÿ}ýõמ_çØÒi¼bêÆ_ZZ=À‘€ÐpÅOYcÅ” Qo=IÃí¿ý¶üÖ¶“0Àû+¾ïTãKó–ÝíøŸNNF=òoöÖ«HúÙ˜LàèÆ+øÓæ~¯‚G÷ß¹}5wB9à¨qÓöiÛËn¾ª±­^ÍŸå2Úm¯kœ_ ØD޽ç~7ÚæÛœ–Ç•FI)"+P{+àî‚:ôël»ß0 y2Yf²œ5ôÊÏùsŽöºûëqä¦e&î©h™g•)Íæ¼?×þ{èc¶jIÙß’ªŽŽþàDkÙs?l®§ªÝÑæ_å_?ðéíó€’t³=‰²&sÅQ¡ãmvò–¶ûû$£L{þ3så»6ð?Ido^ð²ýçûI?2±±±ëKj8Ò2Zp0 >ÐÀBÞu=ð# \;|à±/_l~„u Ç;.€$lG±‰%ÂRlbAØpáCKìp×õ}üeƒ¬Bb|IƒÉž41B¯xèBB'ûÐÉ⇒­ ô߀°¡/~â&)äð\©oO)[Ƥ/=ãÁ#/h««X½Íž=Ûñ^³†0¹©aÆöòË/{É>`À㜕L ¿¼_rÉ%þ¼£Ê¾8³ýmë­·öá8»å«¯R+ª 8Z›ù‘tuGÕýþeŽ˜3«ÖXå“IrŠ}ß2û÷ïïÃ(ΙS~ >‡BwÜqÞFàR1uÉÌ~iiíŽ8û«Y³Ô“¾´Z)Ì¿K—.6|øðDTSÀQÃF­í”óf'yÌ™=ÚªÕ²‰8Yà¨MÛ­­Qã²óN´ZI4lØÊNùYúÏ€£Ž]v±C}Y¦6súëþ j•ý=L"++Pãh¾QsÛá;ÛÜÖ©:õ¹5*#€ÝçÒ]Ò¶¢=sÄ6õÉ)e†yp!p$ '—Ûvîh}.Ý5QßÕñF[þUÙº ·kk‡”è¯2 §nýºîIog¦=îöV×Ûª¥ºOBæÅ¬íx% KÜYFg¤åÓêÂ3¨2Wnå•X4ŠˆˆX*PÃÀ7ß|ÁçâK;-˜„ÇzÀ‡t´Â,ô%_¶È+Œ^ ˜6qø0?˜à±UÂ3שùb¯X¤±R½J^åT‰™WcËÅš}’" MJ}ÅVëL¼¿@ú"€M™Š@\ÙW±à!úF´ä¥¾c Å£Ul%g«þG 4°—^zÉ=žš0æ©Îë…Y^­2D£FJ³"_n›4iâå‰ÝÕéæœn[³õ»jÏDwé­¶dÆbßo¸A#;nÒÉiOiË8ÚòŒml—k$q?{|²={ôSI¿P¦2à(Ÿñ¶>w;ÛùÊ=³=ùÞ öÒ)ÏdÕEa¬@¬@¬Àº^Ž´U2î€O@`¬ û’#ã>"¹ú´`ZU!ÅØ„¯püÀ0¸°ÃRŸV¶ÈÅKO_ÄøÄÈåÒ—k ’/œÃÀH:b)adÒã÷± à$žVvÄÐD‡XøsÁ뢯EøÉY˜W¦'|ÄtÆ‘óóÛ|øR-\¸Ð¯Þà)OQß¾}íꫯö&ØËA½|añeö /ôÝwÞyÇ?*œNE+OºwïîÏ6Ò6'¶·‰ª8*v~äZÀã±M«ºÞ?4áV5r€vÜqG»þúëSï^óŽ*zß dçž{®¦s²Âºvíj P©'÷I_L]ä«¶´´8àˆíŽüNß=÷Üc¯¿þºB&-[̆ âûvòݪƓ8 uix·•_qÅÆï!)~(«.¾Ï®´vþ]2ÜÂÛƒwíàÊ.¿½£µŽfÏe½vº(ëÍ¿¶÷ߺҟµÏA÷¤$#€£–­{Øq§MÈⵤ‘‰¨µ¨×¤žßr¶jù*›òðd[íÚšwoaǺ§Ž‰¾úp® ßq˜áwÊÜsÜ·N)úŸÛŠöùÿ>S×·¬ iÛ»ƒßÆ6oÌÜ4Ýþl°q"æÀ o¿ƒáwL϶°=oÝ7?søã6õéOó™õk½Õ†‰&à¨]ŸvðóGZÝÜ"¦è¡ÞÃlþØô<Ñð”¸]¯éovïbKÈõÆù¯Ø´>ÿΫ¬©8Êw¼znNƒgœauévµ,~®üÊ,"+++°îV GŒ„ƒðÅB><Á"cS÷»><:lÐér¬·uðøa# ?âBȱaK|>ÄÐAÊAlOù(–â*¾Š_!á\i0’‚g ¬Äբˌ­ÄÕ:“Äà†¤ñ‘^c Ï”a^EäS–<«òWLúP(—ŸÚÔ¡()»¼^YÁ—ò 6Hí¯ç)XvüÁøÕ>-Z´ðs2_ÆYĹ6ÿûßÿ’í2#FŒð[•x$ø€Œ­i".~ûíÔB¨8â좑#Gú•H­øÍ›7ÏŽ8â[º´ìÜ!pÄS³BÆ¢}þùçmÚ´i¡¨`¾Øù1PuGÕùþUzÜqÇiïc6à¨÷=<¨œÚ²ñÁôï=Oa»îºëŒ§÷AÚé;ºÈWmiiqÀÆ÷ë×Oa<ȳ`Á‚¤³6À+ñ.»ì²$Þ<`Ÿ|ò‰ñ´µ:$r15 e{´ýªUKmì»ÿ´Y_¾m ·¶ž›kºôsàób{ꡃíðA¯*uwQê H>g½ùò¯íˆÓÿNò”µïØ'm [Ü1Gm¼¿ üɉ˜ü8|;­^½Â&¿ß>™ôp6u”Å Ä TS6ØfC;âíãýhkV¬²Oýئ Ÿd &}åVðÔ·]Ýjœ¶;–ý=wÃ{öºM ÌUGæÎ9úðÆ÷mê¦X½¦:z3ëqØ&Év·[¥ž$ëÝËÖçõ²¯ØC][2{±½|Ú³¶|Ñ ÛôÄ-l“#zÚ2÷D·{{Þamwhg‡¾zlb»jÉ ûä‘ɶ±[ùT¯)‹ÅÓ)p4á¶lñ—‹ÝyN­ËÞÝÓœ›sœ²Ñ~dÝöHS ít£-›Ÿ¾¢28*v¼^õ¶ÞÚ%m¼“æÙƒÛÞ&‹XXXõ©5 ñeáà €;7` ´àÈÐAàØÓ炇sE Â_¤˜Š‹¯ìá‘CaéC_—²N倯ra|ùÉ]¥¤*5tod« Ö0¨& Ž ½H}éÃñÅãÏXØÂ#§O‹ŸxÇz>õͨ¬(Où¢—þô‰Å¥ø£ ­jÎßÛZn½õÖäK8ÂU«V¹ƒeW«DáPžšvûí·[£F¤.×6Ì?.\Š8’,³åäO<Ñ?Æ=Ô…ÀQ(Ïäÿüç?û'{eÊ í3?ƨnàˆ1«ëý«8"-¶ØÂÿLÀCÙ€£”¦ük®÷ýì³Ï¶N8¡¼C á°hVÚdR1u c”–¤†¿7Ìá½÷Þ C¯pHÇ“ {ôè‘S‡~ØZ·n]ã‡c+Ÿº% 옓ßs+{zJ”³}ôÞÝí°ãF$úB€£'ØÏù¾l:§iI‚9fáW¥åpÔ½Çí€Ã Í*åñ+û˜¨Ô<Ä Ä TqBਲЬFºo³;ð²(1ÝæÿzYß¿ï‘ô+b2£ú-8Ðj5ïžÚÞšË÷Ö¦×ú‡¿ylÚY?™ölwÓÉrG™>ꇀ˜da{Ú‚sÊ­zlûmö[3C3ËŽÒ”A§²ñíÏ<Ëê”pÛš¢çʦ<4YÝØÆ Ä Ä ¬w¨ÀØ„€°x.t` ¼@!xd\üQ/<ñ°áBFK<ð Éi±ƒà‹xÈéCò‡'†ðl¸>´uÝü ÇB‰DI\ƒÂ+bÑ×'^h#€ YxáC_/1™œújUˆpL¡3ÄÐa/äØ(7éi56ü›î*Šš7on|ÁåL¾˜fÒ—_~iGy¤“¤c›«@X•«’•¸BÚm·ÝìòË/Eþ`mVq½øâ‹~ÅP¸íMÆlùÌ©Œx"‡wóÔ)K.„x¬ù /¼¸:?O:é$;óÌ3}Œc=Ö>ûì3Ïçz©Š<‰]ïàÌ®»îj3gδÃ;,ë”Âó¡hl{\›÷AìW»eþ\òsöÔSOù§ê,¬Ì¤Š©‹btêÔÉa V¹ÝtÓMRU؆v³2À4ógšI¬LZ²d‰‘”?+æØ®ÉÏИ1cü8zrÜèÑ£ýÖbó·¿ýÍz÷îäÂJ=¶ò3|ÑEyàŽø{ï½wÚïmâPLIICÛeÀ•¶åv?u£êÏZY+W,²‰c‡Úø±wØQ'½ëË—-´Û¯Kmã8ù¼™¤nãå:'©çV'Ø^ÜáeŸLnÏ>qœÛ¶XÏö=ä>+ÝäвàŽ[ºtž=ýðÁÖµÛÞÖ{×?yâwÞ¨¿|ô³iöuäW‘MÔÅ Ä |¿àpè­ÎÞÖ¶ùÅŽÖ¤CÓœƒÍ5Ã^ü?ûú“…ål¶øé6ÖïòÝÊ+®YµÚ>zŠºäM›?n^9_¶¼ðØ¡ÖÑmË$|Çßò[áô²¿kkк¡ò‘Öz‹ômiFÏ zÚz_ÒÏÚíÔч™õ†;‹iÀƒ•9<ÚþµŸ¿dŸº•VkÜŠ©\”yøÊÅËí®7ÙêÜž–QeÀQ¾ãñ9=xæV¿eꟊ+.µ¡]n)7^ÙÈ‘‹ˆˆX÷+PÃÀQWa¾ÜÒðA 8ÊèCèù€À†6´‘.”9“üÁ>`èCøqó/¹r» [}A@¯+ô#6rµŽõ>‡~…„c¾ÄÀ\gP’…§U$ q›Vrì4ÙÈ^:g’øãP„-v\Øqñ/kÞ$­ ’Ž|àiåçXß[ÙÈ]ú> $_´hx27“'O¶©S§æÜ† Û…6ß|s¿J‰­n€%™_” Lc­Íí@ùã¼&@оïùUUžÊy]yÿ4µõêÕ3€œM7ÝÔxòÝøñãíóÏ?ÏúD?ù„m¡u } å9siÛm·õyŽ;Ö/^\hˆ¼íYYÄï*[á¦OŸ^ãQe‰×©SbM›u²Vôt_êÚü¹ãíÛÅ3ÜX}nT!?}I½Æ¶a»­Ýʯæ6oö ›“Ÿc´ŠˆøÁU€ó‚Z”¶°V?jmÛ5±e —ù-k ?^Pîö™“«S¯ŽµÜ¸•ßÖV¿Y}ûfê×6ïƒ9¶dÖ·2ŠS·^]kµù¶ávî°lwg7gô,[øÑW¶zeù¿i Z6°Ž»tv JC›ýöLûæÓ…9ÇÈrÞi˜bÚò¯—Û­YÉmdåÄ=Lû]:Y§=ÜçÄÄùöÅ3îþÌm©Ë¤ª¯ûAÛ¾ÃN¿vÞ 6þÖ±I?2±±±ëcj8âü 0.ðƒåîc&¢V,ê«ÕþðøbË… }ä\Èh‡ÒXØ‚[(Ú)–Zü±S_<}H}ÅHIs¼Ê)‡ºœ{Ðdè3&‚Lƒ;6á%—­Vá«D±#ž®°ïÄÞ{Å/]蜘l´OB«4µŽRÃüð_Ù6uÎ9îÌh„ þ‰j¸¬µé%ϵžh ++++++W2œ;Úü«R,¯À9Œªb¼Æ›ÚÑï` Z¥V±ºi¨{âܪeÜbGŠˆˆX+PÃÀg à ÿs ,#óâÍBÈÎ ¬ðZa Žõ¸†ú؃sà#¸†úŠ)[ì!Za$Šþ`&ø3¾>Xð‡äŸêåx•quš˜AEað0a3švJŽ–È$Wtâ5Ž&‰Ž1h¡P¯¾rÐê#bQHÀ­bIŽ—l#pDe"Å Ä Ä Ä Ä Ä Ä ü@+P@N!S/f<¶ëÕoÞÀ¯šjëV\í}ï®ß0öÅ“þk?0)éG&V V V`}­@ G}]ÝÁ[À8t Ø_® °ˆB†úŠ…?a ð¶ò¡•¹xZ.|À1”8 2å„¿0ì¹â8¶rÂ9_ À'LBà ƒCØBê§z©d“Éᯠ™&©¸´:âÑ§àŒ…LEr¬ß¶F IúÃ#§•œ˜ÄPlr*êplç)V V V V V V V V T  gmÒ.f¼ã&Ÿbͺ¦Ÿq©& g¯œñœº±ˆˆX¯+PÃÀ¢‚#€@à\` àÈi±û@‡ 9$9-~´\¬<6/B'’ 2åA\a!ÈOø‹Æt"/ÇRþŒ'¶ô¥sln’Sn‹tM8Qi4Yú$NÒ ®Ø´ÈH ^1àU&®Ä5é°È[qÈÇBGLÉñ mѱ‰œà±W´\Òr|¤XXXXXXXXhz]ÜÇš­ôooýO[µ„{ùoÐǧZÓ.ÍÓâ`ðwþü¦½Õ;yŸÃ” vbbbÖÁ Ô0pÄŠ#p° }€5 ÓåØD‡zpÎC!Çp@!á’i,§Jì4žlÐAȹˆ_Ø‚y„ñeK ‘>yÁ ! ‡‘1 (€¤C.žVÀ cÓ—Ž8ê ü¡¨ƒŽ7 9D+傜³‹(€ÆP‹-<„/<1!é½é%ñ%V V V V V V V Và‡Yw¨uI£ãéqF™OA«òI1^kw(x×ýºû§Ú-û­ÍxíK›7fŽ­ü®*Ÿw +++P ¨ÀX˜-  <Ø‚"pt´!/ÜB2PÎÌÛ…ññ— %.<~`òw¬›>XI¸* „?D‹ofô3c:QnÂ!_Ò\‰àK±HšXš”âbö]×û üA§xðaq°ÕD±W/"`!¤$\H‚•˜b!CÏäh!dxèã INŸØqàÕG§Ø—XÈðÇ9-2ì2 œr"¿ }æ%bŒÄ0R¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@ÍV G€1` àð``º´% „[|°Q¼p Ç&1°É[ VAŸ [bˆ=~’c‡ RžÈ l)¿plôYI³*3„†?úâ铨’sl9%Œ ö[ ã#^ñ5!ä\2bø0>rúððGÆ%{ÇzÂ9~ØÑ‡àãSÕ|)âK¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@¬@ÍV –G*Bi‹˜€lÀ Éœ9x/tâ¥ËlÑcÇE üá).2ˆ><~Ø ÷€W?ôïÔ¹IÁs[”idËÀ ¨$Wa_À Qðƒðƒã°‚9¾ºëúÄ&$ 'Õ+‹E?„ð“-c¡CÁ+}­†Š[Õ¨F¤XXXXXXXXXXXXX®@ G;¹éƒ!·ࠣ>-$À ƒ+³NØc£`ôK8X†Æs¬×Ó"¦B?ùÐ1>vrÌf—²ÈñŠC¾Ä$ˆ„ 8ƒL‰¢S_à Érp5­ŠA,ùÒjBè•—ÆðNT¾€>*mè‹b4t¼Æ”Œœ W8JÕ#¾Æ Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä ÔhjpN!,>à„,´AGlB}#ð dè´ÅMñðƒÇVØŠâªO‹/„N¾Â_èCèBŒ…Øò…s‘?òJI+5t .p†!b(aé”­ÆÀ^“T‹IŠ%9­lw(²€"ÇzB/Zñ BÏ¥qˆ‰NrÇú>mܪF"Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ôpj8êí¦/,,Lƒ lA }0ÈÐq B…6`ô!lÃÈÐkƒ íå¯Üˆ¯1°'éi?µŽõ„o¥„c¾¤A•”  $h5M޾d>øÊß±ž;x&+`dìà  ^-6ä%úòÓê"'òz§\±‹ÀÕ‰+++++++++++++Pèaàˆ­jcbÞ¢:Ëݶ&A+І¾|Ô¢‡§…°Å?$ÙâÏV!lá‘Á‡à1¥ç /Z=<-¤V¦í’%Kì믿¶É“'Û«¯¾jsçÎM3iÕª•ýþ÷¿O“åê<óÌ3^Í9.^¼Ø,X`~ø¡9²Ü¼Uå½bEøûš¹*ëÂûVÌü”g!ïsI?ggu–uèÐ!TyБŸ³+¯¼2íç,ͨ†;úÓŸlß}÷õYðsI-"ýp*ÐoÏËmÛÞ¿H¾ñ v¯1¥^}.°Í¶lõê5IôeÌjÿþ­6âùŸ¹ŸÍøž—Õ¥2®Žþ‹i5½ãúv¶léW•9®•¾û&Û‡=œ3ÆœÙ£í¡¡¬ÜŽ+ðýW`øðáÖ¥KûôÓOí¸ãŽûþŒ#Ä Ä Ä Ä Ôª ÔàˆX.0 ÝÌòÅp†-6` Dôáå+Ì"´wê+A.{ÅC/^-q a,©^êôúR,[b‹Ç†qϱÆ…ö ¦hb(-‰+Ç&…‚G=D ¨£äá5iVAØ O}3IMŒñ‰i¢Øã‹öÒ+Z.ÅÅ/ŒÃØøà[ÔŠ£† ÚUW]e;ì°ƒ ‘›Â/ÊÍš5³Ûn»Íºuë–Óá /´×^{-Ñ&m·Ýv¾ÓM7ÙСC˜#<ÒÎ?ÿ|ß=ôÐC €ð¨PÚ{ï½íÀLbåò¤ùùÏn£GNLºwïn÷Ýw_Ò¯ˆyðÁíꫯö&ùø}öÙgœ[¸pa6¬Ë>ûìc‹-Jtbª².mÛ¶-j~ažù¾³fÍÒ¬˜Ÿ³Ä¹–08ª%oD‘idŽêÕkäÀ¯óŠ8îX{pho[³Zîór[êØÉçͰFÚ$5¸õê¶rå’¤_Õ̆í{Ù‘'¦~»x¦ó[vq+iëÚ—_Œ°'(üs¥ªsñª¦—^z©•––Ú¤I“ ¾¶QŽjÛ;󉈈¨Þ Ôà쀛W]ôYÁŽ ©–—n~á¥Ø‚ z° Åq¬Ç2{° |±S\Çz¢Ï¸vØëR>´è2}(7)hn‹2lž–Ijòš0­l””úØ’¤ B á#’v¾E$=m86Š‹-:bÐÊDZ^FIøbSðh¶ ±bÅ-·Üb¬¤a ÿ%;ꨣl·Ývóz|† æ·ìà3uêT<†rÈ!vøá‡#ö4hÐ ›2eŠçCàaåÊ•vÀ”I2’=öØÃ¸Bêܹ³ß‡ŒÇŒª½ì—¿ü¥< „;v¬·kÓ¦uíÚÕ¶Új«4rž1c†—±"çÄOLôü²ï¼óξÏꤧŸ.ÛâÀ ™wß}×ëBàhÞ¼yÞ®eË–\cõ“ˆ›[VïˆÂºä ­M]4hPÔüÂ<ó}ÿeû9»ùæ›í¹çžK~ÎŽ>úh¿zláÊ6Õ©6´8ª ïBñ9T­p[Ò>|ïFûä£GüÊ¢ž›mÛöa…’þœ›=ôïlά2 ¹ølÖÏÆÛºU^?· [ؘQ×Ú¯?õgsÝX-“1»oϬ[ƒÈüà+ððÃ[§Nì“O>±ã?¾ÖÍ'Gµî-‰ Å Ä Ä TkjpÆ&!PÐF<$[ˆV¾Ø7 “/<:]Žõ”M{° báà ¶lé_rÅÄGãbOBa—É!cÙ*)ú^zR¢´úÆ ^z§ò>L^j&Ù†ÀqèãϸðØŠwl’tÈ๔/>HÊ­`àhÿý÷7¶„A|Y?í´Ólüøñ¾¾Ô«WÏýÇ6õsµé¦›Ú]wÝåÕlÁ:æ˜c,\Aóë_ÿÚ>ø`¯ê©§lÈ!ž÷Þ{¯]ýõ^§—LàHÀƒô´¥¥¥Þž²üã°å(Œõ‹_üÂo“Òu×]—€_lÃ{ä‘G¤Nkó/Žzè!¦)Ð&›lbwß}·ºÖ¿ÿä쨰.ùGkS—$‰ï˜|çæ‰k!ï_øsøxê©§Úĉ3Sq[ZÊ~ÎÊ)k GµàMX‹²Güùd«àæ7_^.zçúÛÁG—gõΗڨ×ÿ\Î. j¾ ¶²S~6'IdôÈ¿Ù[¯þ!éGfݬ@ŽÖÍ÷5Î*V V V`]©@-Žú€ÔpÁ‡20Ôþ”N~Nœø 3A‡?x„xüé ¢¯1‘Cô‘+2H¹à+[ñØÊ[ð¡ñWH™VdŒ­. ¬¤h5Z€ À‡ÄHP‰ ¼ÁV1ëí%SnŒE/„\qÐÓ'-`qhñS ì%׸Š]úú|'¨ˆêÖ­ëW|p¨5Sýáð«…°ƒgÕHHlIzñÅø€€!ß~û­?÷H[Õ°¨:è ƒŒÕ9¢ìaÅÐÚ$a¬LàˆñBŒ|9)•–æTU÷Ž;î°Í7ßÜÁEùï(2?$à(ß÷/óç¬"ΤŠ_ ÌULúù,d¸R­Úg› 8ª(Ó­6¶A§OJLFŽø­½÷ÖåI?2µ§ÍÝæÇŸ‘ú»JVëg³g¤ŸµW{²™TUj8Êçs¤*Wå3^UÕUqjbLÛXXXu¡5 õý®†à¬4â‹<Þ![¼A:ìC9:° Z.°GÈ!âã§>¼b Ï`<Ù w¡Æ!}äøiÜ06rÍDZ¹ §BI>š€@"â “^I«ŽƒŽÄ5lèÓ"ƒhUX­ ÂF ’&©³ð%.¼ìBré›lWÃ*8Úl³ÍìÎ;ïôŽœ«Ã™@Ùföß½°"dĈþ¿óˆ8PP(“.¹ä’äl¢‹/¾Ø^yå•4€Dölû Ï$Ážï8bž€2¶Þýë_ÿRZimiiÕGaMX‘ÅV *pDîù¼áÏÙ7ß|c¬ôçŒØÅà?C< ðŸÿü§ÿäœ,@#ÝæÌ-¶*²*®W¯^ÆŠ8ΨÊ<Ä›e¶göë×ϯpkÔ¨‘?à¼wïÞIZá™_‰029+кͦ¶ÓîC¬ËF{YýͽO-ûjÞûòóWmâ‡wÚâE3ÍŸ­m‡lò¸{mîì1¶ïÁ÷ZÃF­mé’yöäð~UÐþ‡·N]÷ð1ƾ{½½ñò¯HÍgŠÈ=é±SÛ¡ïÅ.ÖŽÖ A3ÿ3 qe¥Ã±ÕÏÖöÚéWÖw÷¿$ªûïØÊå\$%Š"˜ ZØÎ{üÕzn9ÈêÕoæ#¬^½Ü¦O}É^yö7ש9¢fŸO[¾|¡?Cè£q÷Øï\—á_¬_F˜ º¼W}w»ÔÚwÖ}JÊxÅòomÂØ;lâØ»²z³¥÷¾]ÇÞ6íÓçÝØ.± Ûok{ ¼ËZ:ðZ¹r©;¤ü6·âë’¬OºÛ`Ã-íè“ß÷¶¼Ü{Ûæ¶Ð=E­bªcÿèPÛq—ßþœ-ùvŽ}ä~G¹flaŒT{+P,pT¿~}ÿ-Îw䳃û›ùóçûfñ` ¶¯so[Ùò“ŸØ€üg ÿxãóƒ-謢7nœÿÇYèSpĽ_* bÝpà ‰{1ã%Î0|¶í¹çž~ëøµ×^ëÿ^ò¬N8Ázôèa|v¿ôÒK†N÷|òa˜ÇÜ>ú補#²ÝŸmèóËüÌÍé±±±ë@j8âËKw —HÄÁ×È Z.ìaÀ,¸ÄËN­ôŽMü4~ÜPÑâ =„ D1ð‘;åáXÏÓ¢WOبŒxrØÿû_oÆÙ>çž{nVn´…Œ-a6$TÜ´AGq„ñä6¨:#Æå,$è7¿ù¿)òŒ—ÒÒµŽ8Lœ+n2¹!0`@òD®°.?”G…¼Åüœe¼yw2‡ âí¯¹æð¹œ9‹m•l³äf\ÄYKÈDèØV˜ùtAéÕFàH•¨¸­S§Ävp•mµý9:í£÷în‡7"§ ÊÊKÒΰÁøù§N²Éãïõ~uëÖ³cNc-[÷ÌGŠÊ€£-KmÐO˾ 1þ-ÿhá¾Tñ™·vÔ¤i;ñ¬OPþ©OùÌãGÛwÞRHÅίX¿pì|ø¶í··#NÌþQ´lÙWvÇuí²†éйŸ{ï_It‰›murҙŋ¾´a·üÈV¯Zn%% lË^gZ—n¬Õ›:i“Äô«ùìëS’þœ™£mì»ÿrÀrj•k½zý6ÄöÒA®ÄÁ1¼çÝ×ßf}YÐÿe‘¯â ððt2~èŒ3Îð-ÿ àŒÆLzÿý÷+¤öíÛ{PˆÏæ\pžÙÈ™…lC¯È‡{î-Bʱöì³ÏN;—‰'ξð ޽Øñ±óåÏ9çœ$Îdö6ÛlSÎâ|ŽòÏF>ù‡ ”yvcèÈ<¹ÿáÁÜ;pŸ“í©º¡OäcbbÖ¥ ÔàˆL°n`Á#è ¸AiUrìä#?° »­³´@IDATÀ&aèuC¬ô±ãO\|ÀJ°ã¢/a(“™|äG_1‘1>-¤6ÕËñJRù’&=~  )9tL{%¤ ª¯Ö™xâΟ‰ÐW±ÃüÐ…q]7­ˆ‡"ìè˾lñc | º³2dˆ_1äüüª žVñ_9=qŒË.»,« ¿ ÿþ÷¿½NO@ ÿÅ;óÌ3½žU < ª.àˆ›Fnæ¯È‡-s¬RÉF¥¥k¶ýå/±í·ßÞ‡¿üòËíÑGM†R]ü€£Bß¿b~Î’âÈ„ÀQE®€H%%©_¯W_}Õ~õ«_ysd¬<ÓaæØñĽɓ'[»víü!é"pTQ…Ëtû4Ì6q‡L—Ñjã üì™ïXó–ÝÜþ½ÜÀ~^pT#›ðÁmöò3g9a;ððG¬[«íë…Ÿ¹ÿ¤/s Bd, *ŽX tÒÙS“•@Ø ̘öìZQ½úMí¤³¦¦`3§¿iK–̲ÒMM‹}Ûµ+^Š_±~i©äÕiÔxC¶ë¸£·oÙz×òQåßQpä¾{Y¹b‘Õq€`II£D̪£wÞø‹?Ÿê„3§$òʘ§ú±}þé3Þìȓ޶ ÛõJ\–/[hŸ}òk½áÖ¶]êïµ”wß´±-úæ uc[ƒÿ•O=ö˜ýýïOLYAÊ}úçŸý£F²iÓ¦ùC¶ùÛ8• ê°bbea• ÷|fpáÃCABʦ°…ž{Qæ“h‹Oñ iCàP‡# æÃ‘á1:ÃàŽÕ^:tð¶x|ðçÃê“X!îùgb¤XXXõ©5 õqµ>!àE«ÀÀ¸Œèc‹„L<6}nî Ñ—¿bÊF¾ÎÄûcàC¹‘Cê†1%£O,dÒ)–°ú|¡“ܱ¹‰`…ö¡ƒ*á0 0yZé•°|2'‚’&‰œoEš{ÖocÛÞmMÛÉm“-üê#÷´¾É¶¦|Ï8*)ihÇ>Ñç¥Xo½ú;=²ì˧äÅ´GŸò¾mÐ&õ·ÿg;ʦLNÉÝ{d8ðKÆ]ìüŠõSkÓ²ÕPïS1ÀuçL©ún+ß)?›¬Ðš3{´=4t'· ±…íØp·­­—³iš ²råÊ%Iú çO¶ž>ÙÌÿÈ:o´§[mô\¢›=ãm·âmO·Â(u/ó£-޳½Mô{›‘j¾?|¦kÅ‘þ)CfÙîcxøG„«Qµí˜'®^pÁöõ×ÿÏÞyÀKU\|@DlÈJBbEE,Hì]ì€K,ÑøWcK¬IìšØ"b/±W°‹” ¢ X(‚ˆJS@Åÿ|gùÝ7{ßî{»û€÷xœóùÜ3§Í™óÊÞýíÌÜÙAÏ  ïûiàˆ÷oVÍ@ºg Å/€AÄç ‡˜ÒÀvlá×CDX)uòÉ';žÒS©ãÅ1 åcàH>Ü›ð%÷+»í¶[Ö9Ûn»mX=¿ç²MÆiâË2¶~Cñ ó´õ­V«@]­@ GÝוŸ\¼I+ÐÒ!@dzAG¹ÞذƒbaCHc€aLBŽ^W܇Ç'¶g lñ‡×¸Èè3n<¦ïVM8J$Ã` 1øTOH„ "!xôød>ùg'ëÅa’š¬|ÕG/þŒ/R>´KžK…€—?«Š##Žì—öMLú¦¨Ð7óÞ½{‡Õ3 RÙaÚ«¯¾ºcûľÿþýû'[ÕŽH8'€6ˆ›·£>z©­8 ƒ¤^¸âpo- O©“nYYñÀQâœb8/àã?Î’.ÀQ1?¿ø÷ŒeÿÜŒ‹¸yx‰Œo~ï½÷Þp¾‚lŠiã›X¾Ö9«ÚDüžñûvÊ)§„ß7-±oÛ¶m;ž È™_œë“Gq5ªæ8n’cK–èþ;·t_OGÝ -çÅÀÑÜ9_¸»nìàŽùsöjÀ»nlïÚo¼¿?èï!Æ”/^tÏ>ÚÏõ?azÐà–«[†íLÕ¯¿²ÛÿÈQYÀΘQW:ÎQZÄ9KGôMjê#Ü£÷î”ô4XÍ úÓ¬¤?{ÖD7ô¦ŽnÕU[”4¿Uý™CK¢.IBE2ÕŽ>x÷F÷ÒsLFÜý€§ü µC?ÕªõïÜþG¼ØW¶J¨ßÀ÷ÂÖ6ç²M|ƒ¯k뾟WþÿK¾ÖÖlŠ9ã¨{÷îa…5ýõ×á‹=1V³È :4y+[âœl9—o®6Žx0÷}úô ¦€LlùOo¥CYêx¹r¨J–Ž´R\~€]¼—³º Ò9¬Ò”ãIµÐÀ³žÌÛ¢E‹p– º1cÆ$+ÍéY¬V¥5 u÷u×È£Œa¿„;€_€G€3pa'gV‚[ô\È2y6þŒN á!`¬|ÖH1¥£¯¼Ñ+.­tÈ+% % €=  Ä$”<„ŽÄh僎ÉÅ6¾&Nè(-„EAGù+.vŠ­qd¯qŠÐ©¸ôe#‰øÄ¡OìQþ*˜¸ééСC°4h{ï½÷ªôíÖ­[ø¶ÃôÙ0±3ßÊñm4räÈðmšGl{ì±Çûø!öúóØz>ÐCKòpì0õÂͦöç§TYݲ²â#@)žšÆÍ7N11&c‹TúËËŠ#ò,ôçÿž±úê‹/¾ÐÔÝ_ÿZ~ˆº„ÕÙGºÎj#nx_ýõþ³Ï>sýúõ ¼n”±êˆ¿ˆUuéCQ‘pD #ÎÔùéå«=ØntóUkT꜎½gG7uÒKîØÓôņóOÉzË?-kkfÒqn›®ñŽ>ò‡A÷î{kÿÙÇqŸ|˜½ý¶*àˆœ÷=ìõ¬íKï¾Î½òÂIIÜê2ü½ßaåÿüf„-WI\ÿûÚ³÷•IæúKWvsxIóëø›ÃJòãíf]nPï¾7;¶Ÿå¥_ùí{¯¹Çý°\T*pβûîèâAd½í:×k—Ý&$ÃP—˜ ŽøŸpÌŸ¹WÉМٟy€òWê&mÛ ûº]÷ͼ!|hè6þ¬£òŸ]bhLV àè ˷èŸ~úéáaéäóGñŠh|¾úê«°%Ÿ˳f•ƒ½éxŽø2ðdûí·&VlïúàƒÒ.¡_êx9ƒU!Ôû!fl7ã¡ñÊhäñÖ¹Ë"¶ï¡ƒÒà+³t,A®§Û'{± X¬u¼5 u]\^pnªÅ<&ZDÞ® `[üáÑӃЙGô凗â)öòõl âAŠ öOc(¶0b@ôñ'.6UƒJLö ª‰1zÅSB²E'ûØÉâ§„e+=Ä.6ô%ÃO¼À$倞KwÇÈ“¾ôŒ'yö:g¯¨ŒØóÏrc^[Ë*óØáà`HûÜsÙo¸á†áÛ2t|KÅRe$ŽÐí²Ë.î¼ó΃u'N os-Iàˆ-uÜÜh ûøáã%êaàÔKYYñÀQ\¶«qsÈ +yb@ª |>àˆÃ(O:)óᵺuaœ˜ Ÿò,öçÇ9XÛm·]’­_o¿]¾"€ã^½z%ç?a´¤€#Æ\¸paptñŰ’qt£,àˆ§°ñtè€p“&M |übÀQ\ÊyV±âHôÑûCÜ‹OP7g›Žn¸¬abàè±ûvq“?¡pÄS×´%Šà·]ÛÚ-˜ÿmÖ8•G€ »íÿd²¢GžØöê‹ ;+P5:é§´ê¦+‡§‹•2¿÷Zr]:mz´Ûn§’bÞó¢JŽXU6göYco³ÃUY‡¬— ¥W}M7Ì=ÿøáYcÑáI€ (ÿBeÄ3pãþw[;ÔlŠŽØ¶Æ{2Ô»wo®X9¸­YäŽØ"ǽŒ¶¹Éžöµ×^s·Ýv[NHÀQlŸ~?LëK/§¾Þ±M¯ –¼•;ŽØŠÏ—4ªë‘GËf5SA°Æ*Þ4¥ØÖZ¬Vº\ŽØª~À•„;À¢f=$ð¹bÅñˆƒ ˜6qVZÜÒ‡a‹¯Æ ¯¸žMüå‹^±462HcezU¼Ê© ³ Æ–KIÒÒ')’ !H}ÅV‹ž‚`@Ž&„l±“=q ¢`D«|èC²Ç_ñh%ÇGr¶ºá_ptâ‰'&«/XùÇꪨU«VÉo]”Ë'> Àƒ²sÜX<ôÐCáàaâðˆV>\]€$þ¶Nßtq#Ʋj¶ÒAlSÓ7eA㥬¬zÀ‘BÆ+ZTt¬nÑ ,_Ïd±Õ-VPuë‚D/…ίԟ_ü{vóÍ7‡›ëhøÀZj ü’Ž'^qÄ9¬€ƒt£,àˆßC–ÊÊ%=r8/~áóÞ½{‡žâÇzãË+дY;wèÑÁè7/q#_97éçbÒÀ‘€8ºçöߺo¿W8jÔx­¬íe7^ÞÈHÑû]f´ŠÀÿ:3ÿÎ{õ¹ÞmòÛ£’´–ôJ#f•«¥Êi‘ߢ™ p•ëüá´“_ñg äèÿß’æwà€wJò£.V[˯t:Ôûo§”Ås&ÐÇ>À¼,ÅâN©ÀÑÿ^×ýðýWY!{ö¾ÂuÞâ„D¦ß ]qÔ°as^Ò ¹¹qcos#žþCÒÃaêœu%zõÅ?y0ñ_êZ[K*P(pÄùC<_ñ¥E.Г>ã[ÞO6Ûl³°Š¦sçÎÜJxŸˆÏ9R<Œãƒ§ßxã°]¿20¥”ñ*$U€@î¸ãŽá^,íÆ™ÌJƒKôõ=q7ÞÈ3C‡M‡´¾UÀ*`X!*PÃÀ‘¶ªQknŒ…c€]°*(îûnÀ4q ‘\}áZU!ÅØ„¯püÀ0¸°Óºú´²õlÂKN&B<ðX.}…– …ÁI0¬¤#–F&=>:*„g€I<­ìˆ¡‰(±ðç‚×Eà ÂOvÈâ¼Ò6œð_ÔzVWðf±ä@‚C+#”1bD0ᱪ½{÷çÁ¤}b°€íHœw” xÀUOñO«ºI.àˆØ|à·¨±$;×ùÊ£¬lÉG#ZÕõé§Ÿ†Ç 3€–®ëÜ­–o&yü0”\*4OÅT[¨_©?¿ø÷Œ3ƒXe–¾Q® À‡§ë1ËZ­¤Ñ®·ÞzáI|’p¤Jänú³uœPþÁÆâÃŒs[g¤ÕŽZ®ÙÙo©Ê\K4­VÒxi°¹žª¶öz[»½ú©›6åµðµªþ&E0·øcÖV´ïÚÊo¿«z—qÿ?N-i~ýÿ8¥$¿ì·»"&˜2­ÀQ½ú+¹cN-?7k†ÂßCz¤2w.ý{ñØý}ÝäÏž¯`g‚š­€€£x+r®ŒâûÞ‹vÞyç fñ—^¹€£ØaÝu× ÷MÜkèa课újwÏ=÷$¦Žxßg«_ bAO?ý´;ÿüó«¼÷¶Ðñ°-–ªŽØšÎ}œhè&bþ€fÒsž#gXò0(ß}‹ü­µ X¬u¹µ8`$ÀGxþ‘c#^ :]ž XF¬æ€$b T@Ž „Œ1ˆÎRÈ §|KqG¶Á©ªœ‹! FRðL‚•¸ZtéØJ\­7Iün˜ >Òk äiöèUD@!ò@®BÈ_1éC±\~j3K)2vU¾®²Ê*nøðáÉ LúQ°ùÄ[zˆxŒmLÜñ!|µÕV bžÀÅ!”ù€¾Qã­l°A¦Ú+kòG ÂSU8¯ 4ãpp€°\TV¶d€#ç®'»|þùçŽígOWÙsÏ=ÏÊ"O“j‡\O3IÛšg©~Ê!ÞªF¬ª~~éß3ΰâ›Ö˜jptûí·»7Þ8¤•þ6•›a~Gµ* #ŽâŸ`E¾^½úþŒ£yþ÷ƒkzS7{Ö§êVh«qŽN&ð­þ`ì… g‡1 8 ¹Yó²Æ8"ÏþPíU6 ºE‹º[®jáW ”(Êrªf'ý¤¹BVb1d|04ýBç·ûÙÛï õ˼1Rõ¨6G̨ÿ Óüã™}éß|e3ÿ%È÷° í°ëí®C§C“~U¿¿‰¡1Ë´œG×¾½ßÚ˜ Š“a¥´žrš?8›‘C¡Y™ ñtT¾ÀªŠ°çÎðƒ8³hàÀ‰[ ñPúË«bQ_ÕxÉÀE0UGñvù|«Í9äpÐwzXî9ÜÈ*`° ¬¨¨aàˆ¼Â)ÀøÀ pÆ@ N >=}.x9X„"üEŠ©¸øÊ9Ç‘>öÕxëLø*Æ—ŸlÑUIJ JCo ðF¶šŸjT“ŒGVþ©'cC_úx|ñø3¾ðÈéÓâ'Þ³×J¢8é¡—úÄâR|ñ–—El¬€ØƒÎM 8VFñòc¶ùp p ºÄK–9φÕ2P>à]—.]ܵ×^ ›ÐÒZqÄl¹¬`«”o º²²êGŒÃ7ܨBœ…À˜P|³¥ó ‚bñËú믾½Óêb½øBó”½ÚBýªóó‹ÏxrfƇdñ3ãwz°ä][4Úøpl;[:»²­jÜØöíÛ7„‹Ï¨â›Vý, Qã)¾úÖV¬À–=Ïs[ô8;QÌúîcàñî§³? Ë :À«v6ëvºB¹7^>Ó½;ò²lî´{ö9?28j¶†ßŽtÔ8‰\©[ÔxÚVÛÿõÛh¿ÍqšþÔQnæWc“¸b2‡†Ïó]þ­gè;»¹ÓG«Z­f-~å~\0ÏÍ›;ÙußöÂ’æ×}Û Jò+›ËJ«èNmŽ6íz²ëÑëÒd>i¯q“õÜáÇ~šèyŠÛí×®íW†p¯bT›*på•W:îK |÷ Ê—móÜÀCü_g‹2Äù[n¹Åµk×.ôya»2ï)_„õèÑÚpv^šx_t‚âÅôÓÀ2VŸyæ™°Òd¨ÎxŠI[Èû&v•G›nº©»æškÂ1°Í÷_°°*‰÷̘òÙÇ6Æ[¬Vº\Z!€Mè;€çBqƒ/P7­âñ…'6\Èh‰Ç‡kÉi±ƒà‹xÈéCò‡'†ðl¸>¶õÝ Çb‰DI\ƒÂ+bÑ×}l#€ Y|áC_/1™œújUˆxL½ËB‡½|c£Ü¤§ÕØðÙË8¼ *b5`…žüÅ7lVÍS5ئÁY@ØÌö">ü#ayµ¶õ¼üòËa«ßöõîÝ;<)Kãr ó[oeð¬Ê€>ܦhÅþùnËÊ r*[qDüø4æÅáÕ,MçÛH€,K­<òÈÐ(»á†¤r¬Òãå¹áä›Chüøñ(cù6Ëˉ½Î:ë$~<’—§®AñãôyZ7 ,<"ž6¾ …´í/tR/…Ö¥ÔùUçç—þ=ã,+€žäÇ™N…G—_~¹Ûj«­’Yò|÷ÝwI¦:ÀÛÓ8È[tï½÷†ŸgSµiÓFâ¤5à()E^&×£íþy~8'fúÔ·\ÃFk¸›ôsmÖÛʃÏóÜìáö9ä•$žÎ°)䌣7Fœéö;<{ÑåçŸpk­½eÖV­$¸gŽÚn¸KÖ“³È÷sçùLøà÷ÉGVP÷î{‹?èˆ,ùMW4õ+—ægÉè¤WñVÁùOŸ}ü¸k°r#×ѯré´é ¿”3˜Èse·æZ›—0¿ÒýÂÀKà%Ž÷ê '‡¨³¾ýØMüªѨÉVnïƒ_ „ü4ò“.öóf øƒ:ü ú~`’+° ɰî^WìGläj=|4ýJ ÇB‰¹Π$ O«$Hâ*6­äØi²‘½tÞ$ñÇ [츰ãâ?$!é‘“v´’{6ôãV6²G—ý© IÄöV¿œÀ…ƒ¹ÉŠ¿9Š?(óÔ4žT¦%ݹ†aù8ߢ‰*°éÔ©Sˆ)û¥ €±rE[äËXnN_rÉ×ððm'Gùì¹y:õÔS+lÕâF–ÚʈoFù†4••¨’§ÆˆçWÝŸOó{Ö²eùöÆpä†ðŠÏ‚`ñ 7êñï#5{çwb“jGŒÏÙò‹8Gc5ְñs§Yý•VqõǯìéP‰UFõðÝÛzðàåÄ®àè?÷öñ¾#<±uâŸff};>+€£víws}÷ɬ:HÛçëã—ù7^n1èO³<è¼Z¹Àswß¼±›õÝ'Y2u6Úä ·ÓnCÔ­´UªšßìYÝêÍ6LbU×/ T & Å¡^xj€ÿ^¦Ë8"¢ýx+ Šóÿþ»7¸—ŸË¬œ•ÌÚÚSžÚ¨Ã—4ù¨gϞᾆ/1üñä `i{ÞÏY=$°D[íä¤íÓ}V_óTÎiÓ¦%ª|À§Ÿ~zÖv8må®ÎxÉÀž)ä}û8Šýc> ˆÅ:ñ¼—³êHg8ñ$R¾h4² X¬+rj8â[w0.ð–Íò¡K˜ˆZpH}µwð‡ÇWx6ô‘s!§eZHca n¡<4^l§XjñÇN}ñô!õ##Íó*§<ê bì@“¡Ï˜2 îÙ„—\~´Z}„¯ÅŽxºâ¾;ì!üO¼t±à(> œÕXlo‘ÄŸo‡ù†•oY}¤z=ÿüóa+Bü{ÀòyêÍÏEßV³]á’K.ÉzÜ2?;>Lð>ÝäŸ'ÏÄÊßÚŠXi¥†nëÞ—¹_ûÇ»gþÕfÛüôã\÷áØÁîÿt«ŽøoP.\0ËÝzM«ÀÇçÑ蜙¿9ÌíÐ÷¶ g¥È³ÿ98¬"ÙyÏa®l£ìsIæÏŸéž|p·þ;º®=ÿ–ݶۻ=|6È y‰óŠíÓ‡^ÿ¸pNÈŸ³—òQÙF{ºw¿³à„ý/¿üä>zˆùòyîûy_†¬’©l~[ú¹éœ'üo¸¬Qµü‚s5_¶ësëôÛA9£<ýÈþîÓ ]«µ6uû^~^ޭ׬é.È^QØuës]—­Î öl»íšÖYq›·èàϲz?‘åZµ”(3«øGüü×lS¾ÊT6œyôôÃû¹IŸ='‘µµ´líâ!Zõ£4úé'÷Ÿÿü'<Cï¬â:«}E¬f5 ʈ·Û³•|âĉáK äà´èEàêcODèÀ5ÔWLÙbÑra«ðøƒ™àÏøÄ†ð‡äŸéåy•qu–˜AEqð8a3švJŽV“\qЉ×8š$:Æ …b½úÊ H<Å,Ñ*–äØrɶdàˆ€«‰x‚ì'L˜n‚ø ž‹°a¥Ò&›l¾Ícé6O6±…\ÕªZÆ62 Ž;†§´pÈ&gqó[—ˆo%YÙˆÅrxnL¹8D=ßï7¤ldûߨ±cs>.xIÕˆ•Eü °nÊ”)UÞ,/©qëzœzõVr›¬ãø€Ï>ß|ýAE*WJ©ÉJ ¹V­;û{Mý9Cÿó@âŒRÂíÓ¼eGÿ$®žî»™¹éSGúß½'WªžìýšŽ§Â­¼r¿ï Ç6®ÎõNz[ÉöÏ=¿zþóÓýßÇÁxæŒ1þ<©l0¤T¿ìÑëfßÇÆMÖu«5YË-øá;7wÎäœÛ ëæìëάxå7÷2œÛÈ—^¹Þ?uïŠkÞwøBKÀRUÕà=‹/AxŸ`åò·ß~Æ(YTñ }ߌ#¾@äËæÆ6¾\õË7Ïx»8 šÎ}Êgor«€UÀ*°"T †£î¾ÆÜP¶€qè°¾ \j=x|ôaT±°ÁO7«ð¶èÔJO_<->àè¸ÀI)'âSÁž¾|‘‹÷lÕ„s¡'€Oœ„À‡°…ÔÏô2ɪøëB¦I*.-„Žxô)8c!S‘<¶­ÑBÒÅþðÈi%'&1›bgòŒ‘UÀ*`° Ô ´hÕÉ5jÜÆM›üzN@cƒö»ºßï“Y½Ã¬G½zž{û‹ýv¬ÒüêNål&V«@Uˆ#VÓ–‚Âqø8 _6r¾gBY¬V½5 uõõG+€À#¸ÀÀ/Óböòg<ɰ¥/gó“œò[dkâ‰J£ÉÒ'q’fpŦEFbðŠ¯¢0q%®IÇÅ@¦ØŠëE!:bJŽ_l‹Ž•Hä½ò å’¾|¿Y¬V«@ݨ@¿ïù•Z¿ “ùèý¡î½w®sßú•M+ù´»lu–ë¼Eö<·_ÛÆx›é·n•æW7ªf³° X ©@)ÀÛ9“Õ[¬’þç?ÿ™]pá…†-ö…Œm6V«€U ®W †#V#€U A` Èt!“;ôàœ‡$BŽ=:á€BÂ#$ÓX^•Øi<Ù ƒs¿¸óˆãË–"?| &‚C@@#cP I‡\<­€Ʀ/qÔøCP? ä­ü• rÎ.¢C-¶ð¾ðÄ„¤CVôSÕB{± X¬VZ] Ô± Ÿüp7aܰ`[ª_A™‘UÀ*P'*P pÄÓÕZ·Î>gŒbp6‡bY¬V«@¦µ8ë3 å£”[@Ž6æñ•œV “gƒ]=¸-qá‰v¡˜ž cÓ+‰W%¡ƒð‡hñMç@?Ó‹ò…’&ÀàJ_ŠEÒÄÒ¤;ø¸ï»Á_à:Ń‹ƒ­&нâÑbQ,H~´øpA´È”-~²õlÙV5*ad° XêXVoVæ¶Ýù_nýv;çÙwß|äž~t÷í×ã›Rý’ÆX¬u¾Ú=`À€0OçÎw®e\O?—'§:´¨s‘â˜Æ[¬VºXZqö€A÷P_@:.É=›€;ø‡PŸBø:p Zââ/’œ~ƒ8ŒO«q”—%rxùÁD,†”$€ I0 S,dè™-„L}|!Éé"¼úè[ã þØ!§E†½@&S^V#¡O_²!Æ›Y¬V«@ݬŒ7iº®kÙzS×´Y[˜ü<½ïfÎxÏýôã÷y']ª_Þ€¦° XêLØnÆÁâP¾T¤'ËSéXqÄ!Ú_~ùex‡jY¬V«@vjpÖ¾æ K[ÂÐAè±Åõ‘Á Çðl;‘ì°Õ¸`ô¹°%†ˆqÑã'9vÈ å‰ Â™ò‹ÇFŸ“8§2%$0Ä€øÑOŸD•œg+Ø(al°×ØJñН !炃whÆGN^ þȸdïÙ@Ø Ç»Ì;}†¯öSÕ|<#«€UÀ*`° X¬V«€UÀ*`° XªYZi1p¤-bz°{€$[0äà¼|Љ—.ݢǎ‹øÃ 0R\d}xü°î¯~ì#Þ«ó“‚ç·(×È–PH®$â¾€¢àáÇar|uy6<ô‰M,H@O¦W‹~ á'[ÆB‡ ‚W<úZ e[Õ¨†‘UÀ*`° X¬V«€UÀ*`° Xj¸5 uóÓC„nÁ%@G}ZH€ WºNØc£`ôK8X†ÆólÐÓ"¦B?ùÐ1>vrÌe—±ÈóŠC¡Ä$ˆ„ 8ƒL‰¢S_à Érp5­ŠA,ùÒjBè•—ÆðOT¾€>*mì‹b4ô¼Æ”Œœ Weêa¯V«€UÀ*`° X¬V«€UÀ*`¨Ñ ÔàœB8Ø„ úFàÈÐi‹›âá­°ÅUŸ_|…¿Ð‡ÐÅ ±å æ"äU’Wiè \à BÄPÂÒ))Z½&©6“KrZÙîPdEž „^þ´â)„žKãäž }ZÛªFŒ¬V«€UÀ*`° X¬V«€UÀ*PèaਫŸ¾°°0 .°Bô CÇ%`=Û€IЇ°c C®A Z(¶—¿r#¼ÆÀž\¤§e üÔz6¾UŽ…’UR*€’ Õ49ú’øà+ÏR\ìà™¬€ A²ƒ(D,xµØ—üéËO«‹¼(è5žrÅ΀#ªcd° X¬V«€UÀ*`° X¬V®@ GlU#ðF ÕYè/°0 °Z6ôå£=<-„-þ1É.° ù` >—ˆ)=8}ùÓBèái!å‘éåy•qu–XL,Ô $¯ч(ãÙ¬ÄkÄU!h}4Å—œIÁ#WldZ…D‘+-¶øÈϳÁ?ôØ+Ûªæ‹ad° X¬V«€UÀ*`° X¬Vš®@-ŽÀ À'¸àÁ9 x@Z0x;H@­ô`ØCŸÅqˆ rÅ’eá,Ê<žqDò!zå —ú²ó¢üÎo•ÑÄ“$8¾È ’Q,ÙÑGN_< Ó—=rFÑÊV~q˸òX„=$»8öô¹ày6ÈK“GTÆÈ*`° X¬V«€UÀ*`° X¬5\Z ä¡àwÀ¸Aă7@Ø Ñ %a%´€„m ƒÔ ä~!yÆ*“¾Ä.ã+ä)¬Å³Oý¼­Ëk)rÙjp%Ç„àEðØÐª˜v°‘¼âËŸ–‚h²±½bü¤íék,ôÄÂO¶iû7ýed° X¬V«€UÀ*`° X¬V«@ W –G` \`´Xà -2Z.<DôÁä+Ì"¶÷ê+A.{ÅC/^-q a,™^æô±dKlñØ0Žây¶r¸žÁ4-@ £%q%ãÙ¤Pð豇hu”<¼&ÍJ"ôœMÏØ\Äà‘c/vØK¯üjܸqø{oÒ¤‰›9s¦ûæ›oÜ¢Eü{5² X¬V«€UÀ*`° WZ€7袿À_àiàH}áÜs 뀗NÛÓ„…`ƒlBq<° ÅÁl_ì׳è“„öº‡]Ú׋ò“‚æ·(×ÈV¥e’š¼&L+%¥>¶$I+°Æ³ÉÊ xù`á‹­HzÚx,Å“-:bÐÊdzAFIø`SôáØ{ï½·ëÙ³'ñÜyççæÎøÊ^š7oîÎ9çœÊLΞy晤/f¥•Vr;ì°ƒ;æ˜cÜÚk¯-qhÞzë-w饗º_~¡|Îzê©nuÖ |e/—\r‰ûú믃ɑGé:wî>ô}öÙnÁþ>²©K—.®_¿~aæBZǼÛsÏ=³|pkìØ±îÄOÌÒ•’gVëX¬K­ûÛßÜÎ;ïâo½õÖá`Q‡ÜŽ;îèöØc·úê«ghô /¸‹.º(çÿ,cëX¬V«€UÀ*`° X¢ ÔàHx‡@@ñ<@l!Z]| Ǽ™|áÑéòl \z؃mTøC\b¨E®˜øh\äô!dv‘ 1–­’¢¯à¥EO/=2|˜¼&€C¡ä#[A^âÐÇO¹Àc«xžMò‘<—òÅI¹ ýõ¯u}úôñ!œDš6mZà+{i×®6lXe&‰î¾ûîsW^yeÒ‡iذ¡»üòËÝ[l‘%OwôÁù£>êZ·n6©Ð?üðÃÝ„ ‚üúë¯w›nºiào¸á7xðà öûï¿¿;å”S‚|¯½örÓ§O<+Œ~øaרQ£ >2ña3¦RòŒý· X–^rG-Z´pO<ñD•ƒNš4ÉqÄî‡~¨ÒÖ ¬VeW­·¿Üuì|Xðýwnp#_9wÙ ^ÄHËKžELÉL­V«€U € ÔàH ˜X||!Ó„N~žMüÀ! ü?ø±°£¯1‘Cô‘+2H¹à+[ñØÊ[ð¡ñWJé+3ÆV—VR´š-À …HLŽxba«žMV!!SnŒEM¹@ ôôñ£ "6-~нäW1°+z«Z)À[9hDü1ôèÑ#tYíóä“OJVý÷¿ÿMú|»Ë-·8¶Œ@|›ÓM7…m#?ÿü³[o½õÜà¶Ùf›pi‹H ÈÜ{ï½y¿ù¿ë®»Üœ9sBì8b‹Yß¾}+¬¨Ê]qÅYszðÁÝ«¯¾V&1ßßþö·nÕUWuçŸ~K/¥ä)_k­V¥[Ê€£ï¾ûÎÝsÏ=á2Ûg·Ýv[׿ÇvUèŒ3Îp/½ôÒÒMÒ¢[¬EUàÀþ£]‹VƒÏ´)¯¹‡ïîU”ÿ²2^^ò\Võ°q¬V«ÀŠRŽº/®3+À à!ð97»Òƒ7H‡},G6AË!à9D|üÔ‡WLáŒ';á.´™›îr}äøiÜ86rÍdzù §bI>š  É@ȤWÒêƒÅ/èH\À†>-2ˆV…Õª l"i’:Û_ârÁË.–!—γÉv5l ee†*-++swß}wÜÿýÐ%í²Ë.aKz¶¡uÔQîƒ>¨`Þ Aƒpž1 ¯D’>WGèÉñÚk¯Í2Í­¼òÊîå—_vlIc%* ¡Rò,$®ÙX¬Õ¯@.àPˆ7tV* ¨ŽGÚm·ÝÜYgD=ö˜»øâ‹cµñV«@ W`yd–—ðØÒWLdŒO ©Íôò¼ê4:K¬‚!Ä”2%‡Žä°WBJV}µÞ$ ¡%®bÇù R\o–€VÈ‘ãчÅ}ÙâÇ’jõŠ£7ÞØÝ~ûía>ÂýûßÿÞýø#¿KUS)€L8b¶Ñ]pÁÉ€¹€#ÅŽŸ¸Džß~ûmâSSJž•Å3U ®T k×®á<µÍ7ß<ˆÏÓÊÆŒãÞÿý°Úçí·ßvmÛ¶uüM²Òï_ÿúW°?öØch`ÃvÑõ×_ßyæ™n³Í6 gq†Z|¨=õ€a»ëV[måÊÊʶRV ’ƒ¨Ð•‹þóŸÝ¾ûîÜ8 Ô¨Q QRK½zõrlͽúê«ÃÜvÚi'wØa‡¹öíÛ‡­¶Ã‡ºï¿ÿ>ƒ§Á)o¼1؇‡pCÂ]DÀÊÊ|gÕå« Oc,Õ= Ò%Ic¨…(Ye•Õ]í.v~}ˆk°r“0³E‹º)Ÿw/={¼›3ûó ³­_¿ÛäwÝæÝÎp›´ñ mævì—_~rsçLv_}9ʽ;êÊЦKɳ:ã¥Ç·¾UÀ*`° ÔLj8ÚÒÏZø„€­6îö`D[ì¸ dâ±èƒC¢/Å”|½IðãS1”›ðìÑ'6Ò)zpúj=[9¬Â>öaP%Ç!&O+½–Oz"È!ùh’Èãíhèå‹N}åFKA¥“^}¯ zâÇvÈk5pĶ´’gø€ÃùJ…R)€L P± Úo¿ýOnƒrGÈo½õÖä¦ÿýïî´ÓNs³gÏFU)•’g¥MiXÎ+ÀY`§Ÿ~ºc›jeÄAóÝ»ww^xa0»êª«ÜÉ'Ÿœ¸´tÐAîŽ;îpœ³&DA&B7dÈ Ok”^m!ÀÑï~÷;ÇÁúã“#àJuˆ'5zè¡!ĶÁqfZšs˜¯žtÉÓàX1qH7O{ÜgŸ}Òn@?õ&ƒ%YÅ´Ö*P3¨çÃ>ܵÿÕ^þnªžÛ`Ã]£4¹Ï'>õ›õÍ'nì;ÿv³¿›˜ÈWkÜÆ~ì§ ð“("æ™GtÇ?”HVZ©¡;ü¸Ï=Ý2‘åbfÎã‹WU/ÏâÇ˕ɬV«€U ¦+PÃÀQ·ÅóËàlW ¥/ B€ŽgƒŽ>r6؃AÈN±°!¤1À0&!G¯+îÃãÛ Á_˜Šd´Œé»U…É,‰ÅD  @"\ Á£Ç(N–>q5Yùª^2üUPäʇ`I@}^þ Èc?åN\â¿é¯¢¨”ñÓ”•¶U„;ì°Cpg•O\+”JdñÁ‹C³9æ˜0«‚ |ÀiŸ{î¹Á†V4œsÎ9î7ÞHd¹˜RòÌÇdVºP¶P]sÍ5®K>Dùª|áïïÍ7ß « 7Ùd·ë®»†Aiਲùg¥•øWèÜ+¯¼âþïÿþ/ðÈþýï;»Ñ£G‡M<•‘Cü:U­½öÚaõÀÄÿJVôT—bàhÁ‚á)“ÄüòË/ÃSõ$Hdl™å ”P  ±2š1cF˜# Oƒ„ø?ô÷¿ÿ=ð¼,ɺ$A± ÔPTŽ>enQ£zõ<÷ö™óɬܨqìçn•†™¿M›ò†bâtW¶‘£"ºåêîÇ…™ntëù7·y¿$ÚŸ~œë&þ¢×Ïuk¶ÙÂ5[£½¢êûX#ýÝÛú¿»êåYìxIbÆX¬V«@­ª@ GŽ ®!G-à w¿¸ƒtž X 6"BØ È$=þŒN:á!`¬|ÖH1¥£¯¼Ñ+.­tÈ+% % €}æGfJH…@Gb´òAÇäbß 't¢ #Žü;ÅÖ8²×8EèT\ú²ˆD|âÐ'vÑ{(–%pÄÓÔ~ýë_û4ãÃè ¥aK ¸Òć,>Šbàˆ³Ø¦Ö¨Q£ æûñãÇçŽ0b5@¼âÙÈ‘#ÝE]>¬ÑOS)y¦cXß*PW*À,=y3 à>ú裬éqþlà8O¬wïÞÉŠ#Œ®»îºðwûøã'>“'OOuˆf+Ú¸qãB\ 8ÌÂî„NÈÚ¶UèG0C‡u-[fV<ôÐCîÒK/ q«ûGŠÅÿ*ÀmjÆž'»±b2ŽäÇ&ÙÒÇS#Û´iÎ`b;Ú¤I“ÂÓ)e·¤ê¢xÖZj²õê­äúìu[{½mPÓ°áYé,X½µ|ÞÜ©îõOs“>{.Ø8à]×¢eæ^Á3à&Nx8èÚµßÝõݧ|•ÑÛ¯_àF½v~Ð5ÿÿ¦gïr Åõ—r‹ç\z5²ùógº7Fœá&|0Ìo­¸Q1yâSÝñ4®µV«€UÀ*P³¨aàH[Õ(àøvÁª ¸/92.á#’«/ÜC«ŠÀ1ä£xŒ^ çÀvØ@êÓʹxéé‹ÀDˆË¥¯Ð¤P"8 Æ5tÄRÂȤÇ@G…ðl8‰§•14Å!þ\ðºègîJ2cËYœWÚF€c‰/¿òÂBhYnUc•+x øGy¤ƒM Uu6‰‚¦#äÐ{Þyç¶Æ<õÔSaÛ‚|ÀQ0ö/;vt—\rIÖ¡»¬FŠ·Ñ”’§â[k¨KàMòÎ;ï Sâl0VUE1p´ÝvÛ¹… fG_|q‰#FÀ[¶¶Øb‹0Ķk¥Ç« 8bU!mwêÔ)¸=÷ÜsŽÿiP'³Ø¾òÆÃ­9Û(MñÖ²\ÀQ>àÿG½{÷áØÆÇ*©êÖ%›õ­µ­Å2›uû?×}Û‹ŠšÂMW4ö ÿ‹üÖ³=‡¹²_e¶Ã¦ƒLþüy7ü©£ýÓÕ&¥U¡_Lž8Tw¼œI˜Ð*`° X–yjp$ÀH8€ð °ä؇€ȃN—g–ë„y`# ‰ØÄ…c!c âƒs ƒ”2Hã)ÅR\Å‘mpªêçbHƒ‘<“``%®]:¶WëM?€&ˆôyZ†=zPˆ<«òWLúP,—ŸÚ‘“Â_—%pÄ£¬µZ€•?|,”JdrGËy%ZÄ¡×:,·*àˆ\YµtÅW$‡ý²jJ«З’'~FVºVž±rˆDUQ  ŽWqæçŒA`ñwÍÖqâGÙ¡‰ÅGÇ&-`{i¬4RÊ›>‚ó?(Mœí´Ùf›±À¥x«Ú!‡΄Jû]xaùU§.éøÖ· ÔÆ Èl³ÃÕî7›Mc‘_5”}&R¤t_N~ÅŸtau»æ\óÜŽ»qk¶Þ<6Mø1o]î^éLßçvÁ»]@IDAT®œŠÉ³Ü«ôñâÆ[¬V«@ÍU †#¶ª §à pàŒA`2½iñ†‡=}.x9X„"üEŠ©¸øÊ9Ç‘>öÕxëLø*Æ—ŸlÑUIJ JCo ðF¶š€@ L2z‘úÒÇã‹ÇŸ±°…GNŸ?ñž < çC_ù¢—6ô‰Å¥ø£VoUc5ß~C_}õUXáSè¹!¥2¹€#ÆæÃ!+žÒTp„Ïk¬>Ë?þZJžŠc­U .U`ýõ×Ožœ˜>¬9ß<«±Bˆó‰ ­VŠÇáÌ¢ûï¿?ÅÀQŸ>}Âê"”&LÛÃ8ziPUÀà4ç*é ÿ^½z…-i¥GÕ©ËÒ˜¿Å´ ,é Ä€ÌW_¾å¼kë¼CtÞâY[Ѽk+÷Õ—£òÚW¦X¥as×¥û®s—\ýúœPNñÛ’“§|â¶Øñb_ã­V«€U æ*P €#0° =`ð\è ÀxBðyÀ"Äã O„þ%W`’a›2öؤýˆL­gƒÆ¡_)áX(‘ Á”dái•ÉcC\Ŧ•<ž€ld/7Oüñ(ÂVãcÇÅ×Sü´‚=rò§•ŸgC?ne#{tµz« òD¤þýûÃ:>œ|ðÁáéIAPÉK)€L>àˆaºté⮽öÚ¬,ñ­?ç«ä"Ë~úé§ƒŠ§7±‚I+§JÉ3×&³ Ô… œuÖYáñòÌ…³|øûÿúë¯óN­:Àѹ瞛`ÿÀ¸Ë/¿<ŒÃß2‡FwëÆJÝrp´ù曇ôÑló¤²R¨U«VaÛ*+­†ê^{íµœa*Ž6ÝtSwÍ5ׄÿ?8ó¿Rþ— •Z—œÉ›Ð*P +°Ã®·»M2»åêîÇ…s’~ÌÔ¯ßÀýáT¶‡r •¡îìæfL­nhëÕ«ïšµø•ûqÁ<7oîd€7pëm°ƒûyÑŽ'±ýò‹îu3n W]à 8á«$Æô©oº‡†n“ôaŠÍ³ºãe n«€UÀ*`¨± Ô0p´•Ÿ8˜o~|Àc&¢R_­Þðð‡ÇWx6ô‘s!§eZHca n¡<4^l§XjñÇN}ñô!õ##Íó*§<ê bì@“¡Ï˜2 îÙ„—\~´Z}„¯ÅŽxºâ¾;ì!üO¼t±û¬ûôÓO§½óÎ;Éj‘xýîw¿ æÏy#7ÜpƒTîóÏ?Ï:ü•UGøÿüç?Ýšk®ÜÒg·ÄÀQ¡yj|k­u­œvÇw8=Úžs‡n¹å–ðbΜ9…­XœÇÃÊ=Àœ /¼0”AÀ«zªZegñwË9E¢{ï½×}òÉ'á©bmÚ´‘8iŸÕI¬~„ø;×Þ‰aÄð³{î¹'ù¿©PÅûøq×`åF®£¡:m:(Ù~ÆSÕ7Y×~ìgaŒE‹ºÞ½Ùw·›ýÝg®AƒFnû¾7¹uÛöNrùÊ9nô›åÿP“gã&ëT{¼$c¬V«€U F+PÃÀßœ à‰Ïa–‘¾¨2@pa%ø€GÐ ñlÀ%ÔÇœDèÀ5ÔWLÙbÑra«ðøƒ™àÏøÄ†ð‡äŸéåy•qu–˜AEqð8a3švJŽV“\qЉ×8š$:Æ …b½úÊ H<Åh‰M«Xž }l¹d[-àˆ ùèÁt—]vYP·k×.lûÈgËï»ï>wå•Ù¶e»²ø‚=[Dxz«Dú`G?d¤ÏÕžþùaºÊ€#ô<=‰Go‹ñØðôÁݬ,0‹‰S|èŒá-%Ï8¦ñVºV¦M›:@T\o~ݽ{÷’#¦!C†¸öíÛç‚ÿaœOÖ»wï ×ÿ—ý÷ß¿ '¾)¨üÔWûòË/gýÿ:î¸ã€{šbà(­Sp€:¦R£BêÂÿäž={†át¨v<¶ñVÚ\Î: ÿÛn›Tšæõ—ê;7ç6Úä ·ÓnC*µ—2 Iž¯ýùçùîö­SaÕS1yÆÀQ¾q$Ï7žôÖZ¬V«@ÍV †£î~ö` €-`ºì€/ˆWœQëÙÀãƒR,lða‹N­ôôÅÓráÃlt\à$È”q„©`O_¾ÈÅ{¶j¹PŠÀ'N‚„ã±…ÅÄx\š1¸i’ŠK ¡#} ÎXÈT$φmk´t±? M›6Íí½÷ÞlĪÞu×]ö¹¶mÛŒÕùˆ3QØê6{öì,“RòÌ `«@¬Û;yª"«p2bâXå×µk×°r‡•?l[ÓŠCV²z)>'‰m\¬J=ztxÂ1±¹ä’KBÁùc€×<)íôÓO‡ò Šÿ/¬tbK]!û¥íO8á„°õ9+"zr°]pÄjIVl²eNÿû4Î6Ûl“€I¬Lš2eŠTIË6»Ýwß=€ÙÌOõ«ª.:‰1ÆŒ¬Ë[ØJÖgÏ{]»ö¨ú/¿üäÞ~í÷ögéÊ6ÚÓ?íNÿ¥ÐjYr:ø|ôþ7òåóÜ÷ó¾ ÿ·Úo| ë±ÝE®IÓ¶ì%˜üùóî¹ÿêÿdÎ=”\m¡yòrIŒ§q­µ X¬Vš«@ G]ý̹ù+€À#¸ÀÀ/Óbö° èÔSO Ã=òÈ#îÿøÇ’ÚâX¬V«€UÀ*`Xá+P €#°0Z.0 @x°Dàèhc^¸…d ¼Y°‹ãã.AK\xüÀ.äïÙ06}°’xU:ˆßtôÓ1½(?áP(i ®Dð¥X$M,MJq±ƒû¾üþ S<ø¸8Øj¢Ø+-vÅ‚äG‹D‹L9Ðrá'[ÏYÑ[Õp4² X¬VeSe µnÝÚqÆ+‹¾øâ‹VµlÙÒvØanß}÷M&~ÐA…Ÿ‰À«€UÀ*`° X¬VjU G`€=`Â=Ô„ŽKrÏ&à>‡À!Ô§…þÄ„ƒ–¸ø‹$§Ç ãÓjååE‰^~ð‹!% àB ¨Ä z&G !ÀC_HrúĆˆ¯>:ÅÖ¸ÄB†?vÈi‘a/Ià”…ÕHèÓ—lˆñ&†FV«€UÀ*P;+PÀ‡_WEœ­Ä!æFV«€UÀ*`° X¬K®µ8Œk߀ƒsÐ¥-aè ôØâƒúÈàÁ#h!Å É VAŸ [bˆ=~’c‡ ÒÈ l)¿xlô9Is*SBC ˆ}ñôITÉy¶‚Æ{­„ñ¯øšr.1|9}x@ø#ã’½gaƒ?ìèCðE?U-xÚ‹UÀ*`° ,“ 4È 0 ŒÅ—öynœ»ÄJ¢^½zåœßøñãÃFŽ´·œ2¡UÀ*`° X¬VjT –GšA i‹˜€lÀ Éœ9x/tâ¥K·è±ã"þðŒD?l…{À«ûˆ÷êü¤àù-Ê5²e`Ô’+‰¸/à†(øAøÁÇqXA„_]ž }b Гé•Ç¢BøÉ–±Ð!ƒà¾VCÙV5ªad° Xji8·AƒÌ¿ö\O][Zi7jÔ(LÎê#ðæ°ÿ3f¸¯¿þzi iq­V«€UÀ*`° ¬ð¨aਛÿ€!·ࠣ>-$À ƒ+Ý'ì±Q 0 úŒ%,Cãy6èi‘ S¡Ÿ|è‹;H9æ²ËXäyÅ¡Pb DBã™E§¾À’åàjZƒXò¥Õ„Ð+/!à'ž¨|}T0ÚØÅhèy)9A® 8ÊÔÃ^­V«€UÀ*`° X¬V«€UÀ*P£¨À8…px°ú€;²Ø}° @ôŒÀ'¡Ó7ÅÃ[a+Š«>-¾:ù ¡¡‹1bËÌEþÈ«$®ÒÐ(¸À„ˆ¡„¥SR´{MR-:l&)–䴲ܡȊ<½üiÅS(=—Æ!&:É=ú´¶×€*Y¬V«€UÀ*`° X¬V«€U †+PÃÀQW?}a ``\` …è „A†ŽKÀz(¶“ aÇ@†\ƒ´Pl/åF x=¹HOËø©õl |«$ % ª¤T%A« hrô%ðÁWþž ¤¸ØÁ3Y; ƒd/PˆXðj±!/ùÓ—ŸVyQÐk<åŠGTÇÈ*`° X¬V«€UÀ*`° X¬5\ŽØª&0 Fà@!ª³Ð_` ``´mèËG-zxZ[üc’-þ\`òÁ| .Szpúò§…ÐÃÓBÊ#ÓËó*ã<ê,±˜X¨AI ^¢/PƳY‰!׈«BÐúh"Š/9“‚G®ØÈ´ ‰"W Zlñ‘Ÿgƒ=~è±W>¶UÍÃÈ*`° X¬V«€UÀ*`° X¬5]Z€OpÁƒs@ð€6´`ðv€ZéÁ$°‡>!‹ãäŠ%/ÊÂY” x<ãˆäC ôÊ.õeçEù)œß*£‰'Ip|‘A$£X²£œ¾x¦/{ä$Œ¢•­üâ–qå/°{Hvq,ìésÁ (òl)–&ލŒ‘UÀ*`° X¬V«€UÀ*`° Xj¸µ8ÈC5À/î€1p!ƒ‰o€°¢JÂJh Û@©È#üBòŒU&|‰#\>ÆWÈSX‹g)žúy[%–× Rä²ÕàJŽ Á‹à±¡U1ì`#?xÅ—?-Ñdc=ùAØa¯ 9qhÑ¥}½(?)h~‹rl5QZ&©Ék´²QRêcK’´k<›¬ ‚—v¾ØŠ¤§ÇR<Ù¢#­|<d´1„6v86•1² X¬V«€UÀ*`° X¬V«@ W GÂ;ÊÚˆàÂ` ÑêÏÀ¼™|áÑéòl \z؃mTøC\b¨E®˜øh\äô!dv‘ 1–­’¢¯à¥EO/=2|˜¼&€C¡ä#[A^âÐÇO¹Àc«xžMò‘<—òÅI¹pä‹Q[¨~ýú®Y³f®eË–î§Ÿ~r3fÌpóæÍ«-é%yì²Ë.®_¿~îå—_v·Þzk"7Æ*°´+Фéún£÷w›¬í&NxÄ}9ùµ¥=¤Å· X¬Ë]–õûô²o¹ûXÂV«€U ˆ ÔàH ˜X||!Ó„N~žMü„™ÄàxüÁ'ˆ…}‰¢/ì$¿(|e+žXòÃ~´€AĦÅO1°—\ã*voUëÝ»·Ûu×]½‹sC† qï¾ûnàÓ/õêÕs_|±[e•UÜüùóÝÙgŸí~ù…tÊ©S§NnàÀAðÉ'Ÿ¸ë®»®\qGy¤ëܹ³[´hQˆ³`+䲩K—.Ä`ŒóÎ;/-Í›7wçœsN¶ažÞ³Ï>ëžyæ™D»ýöÛ»ÝvÛ-éçcfΜæ)ý©§žêÖYguó¶—\r‰ûú믳ôM›6uǼÛsÏ=³ät¨áرc݉'žtûì³Ûzë­+ØU&øùçŸÝYgå~ü°¸z´ÒJ+¹áÇ»•WæW͹8ÀMš4©zAÍÛ*P`¶êõO÷»®J¬¯¿T;{‘1V«€U`…®À²~Ÿ^Öã­Ð?\›¼UÀ*°BT †£î‹‹ †Á‡G0x¼B¶ =xƒtØÇrt€´\`Ž_†g¯˜ØÃ3žì„»Ð ãŽ>rü4n¹æãÙü„S±$Z’HDdÒ+iõыБ¸&€ }Zd-ñ‘󩜖K ’&©³ð%.¼ìbré<âÒb wÜqî°Ã N¯¿þº(ÉEo¼±»ýöÛ ÀOLùË_Üî»ïD>€5¹@¡ë¯¿Þmºé¦Áî†npƒŽÃ~ÿý÷w§œrJà÷Úk/7}út×®];7lذ ¶¹÷ÝwŸ»òÊ+Õ±Çë?ü𤟙;w®Ûi§õ£>êZ·nôó1Äž0aB¢f…ÑÃ?ì5j”ÈÒ «ŽvÜqÇ þë_ÿêúôé“6©²ÿ’X½Ä "@[ãÆÃ˜€‰ß|óM•ã›U`ITÀ€£%QE‹aX±+pÁ¸²²2÷ÑG9øºFËú}zYW×~^6«€UÀ*®@ G]}>àÂ%qðµ>ÔÓra#ÜB¼ìàÐb#ÀF~9ø-¾ðØCÈÀAé°SžMòC¯xÂXÐ˾Jb BI@ ƒB Ä@£éÇv²A¦IÅ6ȃVùHN¢¥@ZE„ŽqŽ å¥|hEøHŒ>¾´Ú‚·ªÅÀò€8€9¢4pÄv¬^xÁ­ºêª2 «iF•ôÅÄÀ[·úöíëlbÊjÄà|=zôn¬öyòÉ'“o½õ–ûïÿ›ôcàˆœÆ—èbæÛo¿u÷ÜsO"Š£{ï½7'†ñ]wÝåæÌ™“ø]qÅY¹=øàƒîÕW_ «´Èû·¿ým¨ÕùçŸ|Xá³ÝvÛ%þ0ë®»®[k­µ‚ŒÕEcÆŒÉÒ#è[+Ž¼á†ºvØ!ÔmôèÑYcYÇ*°4+`ÀÑÒ¬®Å¶ ¬à}–¬x>ôÐCë䤗õûô²¯NþÐlRV«€U`qjpŽvhA_À rH«Š øA'?° À&aÄÀR úØq‰'®°Å£/a(“™|à±¥¯˜ÈŸR›éåy%©BIÃ?PÈ”:’Ã^ )YõÕz“@€é‰ ‡ä£I"·£¡—/:õ•-•Nzõ½*è‰Û!/8zï½÷Ü Aƒˆ‘Ðf›mVá¼¢4ˆrÌ1Ǹ#Ž8"ñ4aKzK[8b[[Ü8_H´<G¬zàÂT&Nœè9äM«¨¶¬¬p`¬¨ÀKј³° ôùWKqH ]*P»£Ìïtæ_q(¶MÁ*PG+°$€#ÞÃêúû—½O×Ñ?›–UÀ*Pë+PÃÀQ·ÅËà|W ¥/ž_:ž :úÈØ‘baCHc€aLBŽ^W܇Ç'¶þ‚¿0Éh7Ów«& %’!Y‹‰B¾H„ "!xôø`ÅÉÒ'®&+_õÑK&¤|h–TÑW!àåϪ"䱟rg^ÄÓ_Qz«Néóm8œš'kÄ”ŽX]ÔªU«Ø$ð¹V÷¤# ÙfŸI°<GŠÍáÜ¢ßÿþ÷Ž-pÅRYÙÒŽÖ_}wÐAUH‹\o¹å– r Xº¾ï¾û†î7Þ¶èõêÕËy䑎ˆlÙcÕ+ɦM›&7k­¾õÜZëlé¶è~†[³MØ~“ð!måUšfU'ûpìznÃ_íåºl}–kÑê×®^½Ì¿Â¾ŸáÆ¿·õúîÇ…åÛD³ùÎ-;ºnÛ^èÖk»ƒÓ8?ÿ<ß};sœ›úÅ+îÃ÷nw3g¼—¸Õ¯ßÀmò»nóngø§¼µñãeþåÿòËOnîœÉî«/G¹wG]ÚÄÉ«€U Æ+P pÄŠjVwïÞÝñ%çr^!ÛµÙž¿‡¡ûãÿ˜ÌsèСnêÔ©I¦AƒŽûª† ù5×\“w‹{–cžÎ²~Ÿ.u¼<雨*`° X¢ Ô0pÄáØàyÔrcÍ“ª„;€_€GÜÁN:ÏP ¶è¹ dòl ü.ć¸ÑfåSæI&GZŔ޾òF¯¸´Ò!¯”0,”4ö€/“PBð:£•:&Ûøn˜89 £x™O?Š‚Ž8òW\ì[ãÈ^ã¡SqéËF ñ‰CŸØòÂ\” 8ºôÒKÝC=ÌyŠÚ‹/¾¶«Åþ1p¯°ùßÿþçÞÿýðD4ìï¼óΰ5-ö#ÎçÑ“¼öÛo?7eÊ”`º<GL€ÇÙó”9ˆšœvÚinöìÙ¡_èKYÙÒŽ8Séïÿ{…t¾ÿþû°R¬‚b±`çwvûÛßBUf<5Ž§Â¥‰Ãµ¦â³ŸÒ6Ö_q* sЀ1®Ùªœ´€£ ¹=|ÖƒMzDE×E‹ºG†mï¦OÍ^hY¯ÞJ®gïËÝo6?¾¢SJrãåüÊÈŸüÿ¹†îðã>÷çµLYdwgÎãK¶ÐzV«À2­ ÍÁì8cúÃþZV ÝtÓM_xjlüäØ-Z„-î;vŒÍž3Ù¾Ïùã°­~óÍ7ý/¿ü2ÜëÄùóŸÿœ|±òöÛo‡³«³‚iY¿O—:^(ˆ½X¬V«@¥¨aàˆÃ±!p0 C1à‡7VðpCðØâž>¶?Â:䇗â)Žbã+"¤ØÄ‚°Ñ´ÄŽÇñÝÐÇŸ¸qLt9‰A % &{ÐÄ ½â¡‹ ìc[d$‹Ÿ–­ ôà6ô%ÃO¼À$倞 _cÒ—žñ$Ïþåù(pôé§Ÿ†›1|ˆ.»ì² î1pÄÁÎúSæQÚ<–žƒ)µb…íglC‹oœq 6‡J³Í âðhhyŽ8ðûÜsÏ sá…'ŸsÎ9î7ÞHdU1eeK8â)uñaã¬*c {1À +¬ 3f„oiùæ–§ÊAœ• œ J{Y*PÏý~Ÿ‡Üíw‹æ¼ÈÍžõ™ûùç®YóöþC .3$àhÿ#Þr­Zo&±[¸`–ûì“ÇÜ­:¹5[g>¼I9ä† ýŠ Iêºv¿Ëm´ñIŸ»3¦v_M{Û5m¶[oƒ’1uëù7·y¿$>?ý8×MþüE¿¢i®_!µ…½Ú‡OÓ¦Œtß½mbgŒUÀ*°ì+мys÷ÔSO<ð#<âþñ{@ „Á ˆlÐç=mÛm· ÷>Aá_è>øàƒÐå!lEglhĈŽ'Êr³Í6Û¸þóŸA>kÖ,Ç—aée/Ëú}ºÔñŠ˜’™Z¬V¶5 ±U ü€+  w€á†V˜öÀäŠÇ#6`Ø@ħ Ác‹¯Æ ¯¸žMüå‹^±462HcezU¼Ê© ³ Æ–KIÒÒ')’ФÔWlµÞ$Ø ¤¡/ÈÑ„ Ô!®ì‰«Xð}#ZåC’=þŠG+9>’óÉ ÿ¢#Àn¤ÊÊʼ» +Hø6› n†X=ÂÊ™^½z} ±ªˆ?h=ö¶Ï?ÿ|ò„5€¥I“&=/1p¨Ä65=¶ž,ãÇ_ªÀ«š>ûì³$Ÿ˜a[^üxûøpl¾=Œ¿Y”7‘o¾ù¦ºI{ຓO>9éÃŒ9Ò]tÑEdÉRäèð³`»TÕáß9Ü‹ýûßÿߤi–ôÿë_ÿr|CÛ¦MÇ–~Ÿø¹óó7Z±+°¹ßšÖm› ’"Ìúv¼{àÎînáâ-f¹Î8Z·ív~µÑs‰ÏW_¾åÁš^~e_\8÷«N»wœè§My#sÚ¬ÛÃí}ðˉŽmfCnhïæÍšÈøwÛ¤éznÑÏ?z°4³¥òàAã<ˆµQb3øº¶îûy_&ý ÿ\ˆëFV«@MU€§¸²Bš÷H+às=”-ä¼§ClÉ?ûì³ÿñLJ/°â÷~îEô¥÷G‡vXòX‡ÜàÁåÿ{®ºê*7|øð(iu®múa°j¾,ë÷éBÇ«æ´ÌÝ*`° ¬¨aàH[Õ¨5àŽndÁ.¸¹Žû¾0 d\ÂG$W_¸‡VCÈGñ6á+œ?n¨¹°ÃRŸV¶ÈÅKO_&BtóÍ7»¶mÛº>}ú„¾€£ø¿$à8@!èꫯÎzÄ} í´ÓNaY7Ë»¡±cǺ£>z©Ga ]èxKj~Ç*`° Ôå ÔàH€‘pá`ȱ/.Ï,#Ö óÀF@±‰ !ÇB|ptr@i<å£XŠ«8² NU½à\ i0’‚g ¬ÄÕ¢KÇVâj½Iâ:Àñ‘^c O˰G¯" ‘rBþŠIŠåòS;2cRõk p îé§ŸN_}õUXÄ ˆU#¬üœþùÁ°iÈ!ïÒ¥K8 €NúÉbiàˆoè8\[Û›Èi£6r§œrJˆµ×^{¹éÓË?x¡)++X9öØc“mY¬ú7nœÂdµwÜqGÖª¢86lX–NŽ/½ô’ûðÃÕ­Ð2¯“N:ɱ}-&À¸Ûn»-eñÅÌ/˱ÄN¡7ˆñ éäɓݡ‡Z¡.| ܳgÏIåÄKLÍÜ–ã tüÍá®wß[“<ûØ!î“ïKú0iàè†ËºcþÌ9}š3û3w׿R7iÛnØ×íºï’þCC·ñÛÑÞv8õ‡DÆv³›¯Z#éWÆ´k¿»ëë·ÔÅ4þL÷ƈ3Ü„†…mu±Îx«€U öT Pàˆ/r^yå•8ÛéwÛ-ÞB[>Ÿb«¹¾H“Å…^Xá<@VsïoÏ—ý’h—õût¡ã-‰¹Y «€UÀ*P×+PÃÀQ7__áà €;7` ´àÈÐAàØÓ炇sE Â_¤˜Š‹¯ìá‘Cqéc_—±Î䀯ra|ùÉ]•¤ª4ôod« ôÑÀ$£Á‘¡©/}<¾xü [xäôiñïÙÀú@q>ô•/zù`CŸX\Н1ŠÞªÆŠ#λÑM‚™àgqHdzÅÑå—_î¶Új«`Û¯_¿d ‡j³t[KÈã'µ¥#œ9_G @ç|æPÛ% ³„<Ž´Ê*)L‘ pr”¾ÁÄÕH?þxÎHee…c9)ÔϾªUB1p4hÐ ÷Þ{åO¤ÒÌ«wïÞ¡ËïÇÒº‰ÖxÖÖÞ ì¼ÇÝ®}Çý“o»¶µ[0?û)ƒiàˆIG43ñ™0n˜{þñÓ¾ž˜vЀòß¿ÏüÁ}þÉ“ŽG¢Þâ^|r€º•¶¨ÝgÏa®ìW{ç´›üùónøSGg¥”ÓЄV«À2¯@¡À+Xq5*`+Ù®]»:žŠ±Š6}à6ON{î¹ç’|`ǽ ç--ZÖïÓ…Ž·´ækq­V«@]ª@-ŽÀÀ&ô€Às¡ƒcà Á ä‹/<ñ°áBFK<ð Éi±ƒàñ#rúüá‰!ü.Å„m}·p±X"Q× ðJ†Xô±bLÈâKv*€|‰ÉäÔW«BÄc <".„{ù ÇF¹IO‹;ø7üUÅ+ŽŽ8Ãp#¦+®¸"œ±“ŽX)8Ä’nˆeÛœq#ºòÊ+ņUI:Ä2pÄ#qÙ*׺uëàÃYB Õàˆ¹P3êÉŠ,Ðð%••Õ~àèC +ÊÒùÇßÄp”®ÎŠÕOo'ÓAÔq¶Ù᪬§ŸÝvMk7àį“qcoûöÎÞŽ¢üûsÓ„B)7â‹‚Ò»´ŠA‘ŽôˆXú—®€ÒTD¤#AŠôÞkAAP$$¡„Ò É}ŸïÜûÛÌÙ{Î={öœÛÂ<ŸÏžyæ™çyfæÙݳ»¿™uî;8É‹aQí½Z<Úï¯á&þï.·÷^—Š{þé_»¿?ñ‹$Ÿ…YfàJnË®j¶·l_|æ7î©Ç޳,Å‘bb:B²GÇ÷÷4´9ü(Gºk®¹¦c$5ÄÔ3¦ …ÄK Ö8Ô 2ʵ|ñŇj5å³9á žj®ÓYë«i'£³%4í ­ga塆XRÜC™ä)¿@G ìTÚ™Zþ Gvy;° ÉÕ° ÉÐîA¹¶ÐßÈ•ëmTù ìDÅl8§R OªFÐxtð+ߤ’£§HGú*3•Ä;€"tÑcC­ÙIB*GN{Ð#•ÜXŸSéHŸ²\SÕŽXl’Oφ7B ãf8w8ü`ý¢,ÄÂÒL…ƒŠGÈYH_—ž~H•—/ìcÇ&ݪ€#FÄð5ˆ/Œiaê8Úd“MüÜ~F(-X°À¯gNSóÆöðmÖöørÚ“O>Y8B'\T›<ÔÙ€#0ÞFΟ¿ø«Q=iüe±l-BNÌiUl  Ñ£ó8à(\°|ÍmÆŒ…΋ätƒHÑG‘h¼ñÆþmª8R$bš%Ë.·–ÛeßB,{Ò›w»å–_¯` [è ਋FbÊY÷K%Eoü÷F÷üßÏvÍm¾ÍÅÓÇî¸~k7uò£^·K—în×ýŸu}-±]¸pž{é¹óÝ{Óžq={p+­¼§:ü|Ïñ€SŸ¾C ˆšèõ-šïþýÂ¥îµÿ\ëfΘèºuëí6ßö·Âȱ‰¿¿?ñs³ÎHò‘‰ˆhßð…O}Tƒ^Ç{¬¿àe ‹V3²ˆû‰E‹ùÑF|˜‚—_Ð<àï=xá³Új«¹O<ÑÎ{n­ ?bÁºF7ÜpCR6nÜ8?2=À o¼1ñÉÔ¹sÎ9‡¢fÔY®ÓYï šu0 bbbbšE £u­Aà`x˜ør4`”!ƒGINŠ)#„Mh“‰|)$ôÕür±U=¤`ê„GŽ QŸdè’W™±¥IF¥5 KÂŽªD%O‡htØHê@FÃàå}ˆŽ«áêt dò¶_”‘JŽ]¨K#‘h“ôÕR6•?k|&J/ŽÝ’ ¾ªöç?ÿ9ù¼ýQGåžzê©¢¦Üd±è5ÄèšÍ7ßÜ/0¹Æk¸Ù³g»­¶Úª™Ý*«¬â.¿üòD.ਾ¾ÞOeK Z`¸± ç®pÔBu‹ÓÇÓO?½@¾ë&T¼©äs¿¼Í,F£GçŽüñ‚¯¼°ÿùÏ«¢@Þ †|I†7©¢)1Íöšà†®°QIõO>~Íõ°RRpÄßÑ®û=S%JóÊ ¹Çl\CMâ.]{¸=øg_•¥SìîÝg¹8J—§ó€PWœ?Ì-˜?+]ó11í¥–ZÊ_«VXa…’-Øxã“Q@LC;ûì³›]›CcÖBúùÏîÁ¦ôºF¼ã^(¤õ×_ß{ðê±ÇKòb:Ëu:ë}úÓÒhgàˆGà`€BX2mÈT†åàá4äèSO9 ðÉT—%zªO:”AÈÙð]˜‚‘„þ¥K Q?6™ ç•*CÅÈè@*C.ž[ˆ¼Êð£¼Àò€:”±3C¤ø \m1Ö<"ªC)ºð¶ðø„T†ìo^’á‡;|°{á…àJKÄWÓ:˜¿ÿÒK/¹õÖcqvç§©!+E|ö–ÏßB;;úè£7oLƒÛi§Ššþùníµ×öe ÿè£ügìB5 „L‘ ¿l²ß~û9Ö(€X4“Å3³S½¾ô¥/•Ue½§ &x=¦ùñ–‘©b¥ˆ7|¡eæÌƵ^Šé 6ÌñÆb´S8¢ª˜¾d,̹×^{ù,Cëz˜NXŽX˜œisibôë®».3M‘QSë8L:5)süñÇ»vØÁƒb[n¹eÑ©xÒé’¦mÍgî¿ü‚ÎΛ÷¡»çæÝ[iÝOñeó?ûÄ]~ÞàD¯‡8Úq÷ܲC¿D˜ÚG÷ݺ‹{{⃡8á»víé6{ŽûÚ?0™þ*“b›º6Û½úÒ•î¯i·âWwwnvšë·ÔÈÅJ)nʤ‡Üƒwìm£?L•ÄlŒ@Œ@{G OŸ>þ#áèdÚÄË·æÚNI:t¨;ãŒ3’)õj?/t¸ö… b3j餓Nò*Ï?ÿ¼;äC |ÉV/äÈó‚ŒkpX'òÎrÎz_@Ÿ"ÅÄÄÄ´u€²Að¶ €‚2ÒÇVrRLÆz½Ð?åà¤ø…Ç7äòi¬¯›ø·›;ç{˜ã¯¾9¡ß»Ï²n©þõ–q€Y3g¼ém- ¯‘Ím£$F F ý#ÀßáÇ;F!1ºwÚ´i<*Õ²=z8^Ö0±¸66­Iñ:ݚѾcbb:f:pÄ/7²`Â=”D›äÆ&à6‡À!”'…¥G+PŽAŠ_ìE’“}à‡úIUÚe¢D/;øL„ÃJHp¡T¨†É2Êé)„Lyl!ÉÉã¼ò”É·êÅ2ìÑCNŠ }L§LäG#QžÞ¤ƒ§QŒ##############оèÀ` Xø<˜ƒ6@0Ê ÊÑÅå‘Á£K É©Hzè¨^° òlèâCD½”c'9zÈ Õ B™ÚÖMyQ’㢅)!Ž!*ÄŽ¼xò4T3¶™ŽŒúª[ ÆF¼ü«CÈÙ døð¡~ääáaŒMúÆzB9v葇à W¢õâø##############ÐÖè À‘ºGš"& °HÒg@^Áˆ2ñ*K§”£Ç†ìáÉ/2ˆ<´oÅ¥IÎKk,.‘.S¡*\ónð‚„|è‡DȱÕf¬xÈã_€žÆÜb_äC@;éReÈ xù#¯ÑPqªш#############ÐÎhgàh}ë> Œ€ p 6:Ê“BlÀ0ØÒùpBù£ O]ÂAÀ2TŸ±¾œ™0òØÉ†¼ˆúÑƒÔÆbz%~1ÈJt‚Šh¤Êg©¡”)/p†Æò¹R_²%U‡(W»T‡€Ÿ°£²ôQÀHC[t䣧ñªS2Ú ¸ŠÀQc<âoŒ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@»F GàÂ!àÁÈî@ÈBÊȃM"`>Œ2Mq“?ìàѶ"¿Ê“b Q&[á/ä!ÊBŒß²…s‘=ò²$ÇeMAÎÎP!„5Xej©ê@_TJ:“äKrRéîdEÆz¢\ö¤â D9›êÁ'e’ëó¤qªQˆ#############ÐÎhgàh]ë¾°°0 6°Bä £ŒMÀåP¨&AB7ôŒ2p |B¡¾ìÕ6|À«ôi‹ÊI©;¥Æz¶,a˜•T©¥¨¤ê€:G^2leo¬'ùEžÎ ØA$=xBø‚WŠí’=yÙit‘‰|¹êS[Ñ‹Àщ#############ÐÎhgàˆ©jcbÞ":óm[“‹ hC^6J)‡'…ÐÅ>$ébÏV!tá‘Á‡à>UÎA^ö¤åð¤Úј+ñ+åÅb0¡P•Ò(xˆ<¼@c †\À¯A 裎ȿät ¹|#Ó($ˆ\>HÑÅFvÆz}ì(G_í‰SÕ,‘bbbbbbbbbbbbbÚ;87Ÿ`ƒç€àmHÁà!ô =¤*“@ø„,ôƒtË—lLT€³¨-àðÔ#’ >(W;ÐaS^z&*M¡ãÒZ%a'qŽ-2ˆÆÈ—ôÈ#'/ž“—>rŒ"•®ì”ze/°}Hz¡/ôɳÁ (2ÖËäKSŽˆL¤vŽ@Žò ð ;` lÈ GâÁ ô…h„’°R@Â6AJò¿¼Q«± ØâG¸|ˆ¯ÐNa-Æz’?åK¦jXI…  ˜®*Wãè¼RSÀ:²ƒ—Ù“u6,‡§\¾~ÒúäUåøÂNºiý§m‹##############ÐÎè ÀX˜)8CŠŒ”M ypÙ ³õ­8ÁJK_þ(¯?0–Æ\ã/:” Ä’.¾Å£C=òglË„r%„>•©R€FJÃÕc“@ÁSŽ>D ¨£ÆÃ«ÓŒ$‚Сœµ‰à©› ?=ôµ!Ç)ei[•&9-­±¸Dºê()TçÕaRé¨QÊ£K#IÖ›Œ ‚— z¶èŠTNÖ%Ò¥ ¤²1ÖËHC tââØD&RŒ@Œ@Œ@éJt«@IDATŒ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@;G GÂ;ÊÚˆà  ‘jÏ@¼™lá)Óf¬§bå ¶!PIà~ñ¡¹|b£z‘“‡Aèe"dQ–®E^Á«/ôÈ«Näya'^Ðô£¶`+]ñø’êà!ªCõ!o‘Ô𕚠ÑÕ¦ŠÕ(Ru€`‡ð¡apÄã ]ù06…„Lm£.ü¨nü@ÈQN;RÀ |“b'èK®zå½8UÍ‚)F F F F F âôïïzl¼‰[🻆7߬Ø<ÄÄÄÄÄÄĤ#ÐÎÀÑMíÃ`¤˜<Þ![P9xƒÊÐå”M²E8Bá_yxùžA}ÒîB*ŒCeä‘c§zCßÈÕcKF•’lÔDøA¦r5Zù°Êh¸:€yRd©«QAèDR'µ¶¶øeƒ—^(C®2c“éjè@UG]V_ÃÕõêÕèÉ~¾ðO›ýÈôǜԽ»«8Ð7¼÷^N'Ñ,‰@'ˆgÝòË».#G97ÿ3·ðŸvüd¤bvu+~Ùu<¸™‡EïLs “'7“w:AïÞ®ëj«ûf/üdzvÂeúÏëÝ,¶ÿZ½¡à|¨* ™ö»õÃÙ±^׫·k˜5Ó¹O?Í–š_2ÔÚuÝu݈sï>¼þ:7ë¼ó2X´¾Êúë¯ï–]vÙfM™2Žð ÍäM0Øþã7Ø ñžö¡‡róæÍkÕ.t„x~éK_r=zôpo½õ–ÝbUqÕª‘ŠÎ[;«¬²ŠãX˜?¾{àšUWWWçúöíëú Ý¯_?÷ᇺ>úÈ-ZÄ£E¤ÎvŽÖµX—HÄÂ×úC!eC0 6ñÒ€Cªrc;Õƒø)¶ðèCÈÀAä•¡§v›´rùÆB¹ìàËe%5T QH>Ô“2u*ÔA.¤jääá!R¤QD”Q/À¤v©=¤"lTŽŒ<¶¤Òª¦ªzü W×UÍunò¸Eÿýoc 9~y–[jã]ƒ=OÚr ×c«­]ŸµÖ´Ãe¡›qÆÖºPHu£G»þ{ïmQmhÔùœE"éxÚÝF‡ ÌR‡æí¶»o×Ä7òû1K#‹Ù-wée®·ÝX¥i棺NºåæF`¦È5@ʵ¾ÉoÑÔâ»Ô×ûkOÿ-¶tŸÙh£é—]ê¾ý¶køàçfàUc,Ùu×]½×_|ÑýùÏ.Zõ×^ëFÛu1MÏ=÷œ;äCÒâN—ßzë­Ý)§œâÛý½ï}ϽÙÊ#½Ú;ž]íë¯ý«ïïqÇç&L˜ÐéöYlpm"ÀqÏñm´ÑF,Zi¥•Ü–[névÜqG·ôÒKThôðûÓN;-‚Ž‘‰™Ž#ðà hÃÃ5yRð =kTr?”É삇Oaø@’ò豉Ç/6ª ]ò²†¢:I‘ÉFväåõ“BJs%~iTVRÀÐÇŽ Ôdjet}5HT^©©xä¡#¤ø•ï°}”…~-›€Vø£¢葇…yébG’ªq´ìù绾k®¥úÜÄí·snÆŒ$_ ÓÅøGÚƒ?4íÔSÝü{ïqƒ ,Zj“M½lâ˜ÍlÐÇQ!u3Æ ?ít/œ¸õVÎÍ™S¨ðÍ‹gŠ¥<ÒõúÊWÜ\{™}ÁaQ›ò¥$Ë5¢˜]ÿŸýÌõ ŽÇž£Fy78*Ͷ//¶ÿÂVÔúø,w>„uwF¾³ô¯è~·Q«õ?R6ìó^{ͽûýqvåäÒÙœjy=jî}±¤›]“†ÿú×v% /׋Ëá&nôBA rx ;è ƒ¼§—_~9áÓ®O?ýt·òÊ+'â¡C‡z>GIH*bÚ;ž8ªhw-ÑÊÅ€£6Jÿî»ï.Ûï· ÔÞo¿ýlg¶œeF…V@;GëYç„OxÑh#á`FäÑEO7hÈÄ£‘‡(D^öò)ÙšŠ·Ç€µ6‡‘Ç2•Éåà$ä•Û2á¬B?´¡R58ôCè<©ÊÕ`Ù¤;‚’:‰œQEê<å²¥Lyµ”€ªLåÊ[‘/Ǩ‡¼*à¨Ï¸!ßo¼‘ÅÙÄM6±Vkÿ ÉH]º¸á·ßîß4ÏŸ:ÕMÛ}7ëMCŽ2†¯™Z‰x†zË_uµëiCžç¾ü’{ÿàƒÃ¢6å‹>HfhA»‘<èºØ°íeh«”Û5=>3œmÜýÚV׉úWt¿7G ¦¿ï¦_|‰[ðÏç]ƒ½€¨³7ç=6ü†zÔQÉÈÖÉœ,ú÷+EãW³ëQQï®ë­ïFüîwE5Ìwuݹt·p´Ç{¸Ãl„&ôôÓO»#Ž8Âóå~yä›ù×ÛEà¨\¤²•·u<#p”m¿|´ZŽfØæu×]çžyæ÷ž-õÀ”ÎM7ÝÔ`÷é]ì{ì±î±Çû"„*ö1F ÓG £õ›ÁÆÃ=¸)yñ`tŒõeä‘ @Ÿ?!éÉ:øT†À$ä”k óðØ„úÔ.öðªyê ë´ly +Ñ*£âð!䋆°A4žrlÐÂÆ’ǯ:+[å)—L 2Hí!åîT@y^öŒ*BÚ©íô ÿOÛ–›ºn¸¡qÎo¼ý»PMýîN¹|õ²á÷Co¼ ž´ÏÞÉ"£qÄQ®pºRñ ½ÕôÁUF^í¦\~IU†¼EB1+©ô_ :¡ÁC”Ñ06ê( u,ë;N(#x¤> eøÁŽ2ù…W{Tô)C ˆ2—¼t"á?äñm+ìæ§ºa+¸Q7Þè̸û.7#ÏÉÞ*×ß{Ÿ÷ñɃ¸O>9iPÍ#¦ØH¦OÕ´³…x†ý®åƒyè·R¾Ôƒd9?Yì"pT.ŠíW^nÿÕìøÌx>D¢šó¯ÀQdòô¯ šUªŠrû½˜]ÝÈ‘nÔ_®óEï_z‰›;~|15W“ëQQÏMB[p¶þž{}ÆŒÝmW×–‹có›ßt'7]+y¬èàÁ´#-®ÛpÄz/PC+^ç«§o`?8ªäØ©D7¾jlÓ¾²ä+©¯Ô1Y‰µ©p¤²Rék¬á.¼ðB_|óÍ7»sÎ9§”j”ÇÄt ´3p´nS(¸¨‚1 yT~„a€+‚G{x¾É£ ð#¬9åè±ÉŸüÈ7¶"ô!ùÆ„Žê ÅwXe}{ü†>)+JT’•T™ôi€:Fe”Ëe!Q&ýPÅN –® Ê!ÀtÈK†xôáÕñä±…Q'y•SŸäUMUs=mm M³õˆæßy‡ç+ùÑ‚®:Yôoîâ5ŠªŽºusÝ·ÜÊ-ó­í]¯/ÿ?×ÕÞ/´EKçØTˆ¹/½äæ¿üŠ[ôÒ¿Š7×Þ÷Ý}w×ÿ›Û¸+¬à>ÿøc7ëñÇÜÌk®u S§4·éÓÇõ1}h.`ÚìٮΦ‚-³ÿþn)›nÑÅʼ3ÍÍyþùæ[5íLµ¤¥x†ª¹Ìí¦¼› }îgS—²OPwµÍyñ7û©§Ü§6Õ>—$³í²ò*®·­ýÑÛÖVêÒ§¯[ðÁtDz¢¢‹cçµ3§¹€£J÷;¯4.¶øoŸoïè»=÷škì/t¾ëòõUÝ ýÐõµ/~þñGî#;†æÞrKAL‹-Ž]`gCÕYDxî 7ØùŠôc]×^Ûõ¶¯Aõ²5ëì VŸOŸî1¸È¾N´`Ú4·àþû¥í\5Ç5^rî¿\ÇçâV'\¦ó!ÏùWm\ý?ô50€ô¬í÷Áì¿=å×ûü7\ƒmå(SÿšœdªoÀ×ÇF¶p,ÌûË_ìXYÇ-wôÏ\â÷ž-̺èÕÿ8ûdhS–Þt3÷ÁÕW¹Ù—^j· \ãʹß ÛÇþC‡ôŸG¤& bjp=’«¢©Å¦þ®ÆµDΚåÞÞv×uuÚì«j›m¶™;ƒD1%å÷¿ÿ}Ñf¦…•|•içwvcÇŽu#FŒ°¿€>¶tà÷_ûðÅ«¯¾ê^yå‡Ïö¢4pÄ—ÆÆŒãö·ã„íÙvâkk,þî»ïmfwûßÛj«­ÜÚö_X__ïºÙ_Ÿâ+T|µì1võÕWµ­4žET lKà0c»¾ãßð ¬÷²i¤L".sçÎõ_°;ßÖ¶0Ç×ûˆ;D¼ˆÛºvmÙgŸ}ܪ«®ê°ÿÀ‹ÿÏþã§I¥Èåí«ªû_è_³ì¼bJ% ’¿d÷h!á—ý¼ÐÖ8ãØ§ ìCêZqŽí£ö! Êhk­‰úo×Zk-G»9^X¤žóÑ>ÿøÇ?ÜH¹YÀž/ý'ôô£ù¶ذÈ9狜¯i»`m¢ßÙÔWίJíÚ ÒâØÊ—JÿïÿþÏŸÏ”zè¡îÙg«z_\ªš(ˆ¨qÚ8Zߺ~À–„;À°¡C*ÌB7„ËWè?è€I á§kSJB†.¶ªƒ¼ü›ØË–rùRÝÈ ÕÕ˜+ó+£2j¾]65’”<¢4R^¾•R/†¼ GB†ºø•>~å "`Dªö‡¤½ü‘JŽäLuþ:àÈŒzìqWg7]-­=ajE©`A×3Ïtóï0ð! ª€#›N0â–[]×e– <²óìÁìÝ}÷)Z®În`FÞ|K²®FZaêI'¹=X ®³›Q×]ïe“mÍ >›ll_jî…€¤Šv4À2-ÆÓäºVg7†Ð°¦¡ËvÓŠŒ[`k|ÂÞn—µŸp1tï¨é‡õ=Þ¶›’Eÿ*ÄÙñ±ü•Wú‡ãÐ&Íą¼vMŽ+Žòìw»3®8.!4ÉÒ{°®¦¸øéŸ¬÷Õ´(|V»Ï?úÐMÙÍìÒ‹`Úáˆnlñœølâ[îûR‘(÷qƒJöŸ©Wu|ªÁAÚâù ½œç_UqéÚÕ-eƒöÜK­(šNÜlÓæ€L ™©èWPŸ÷yáE¾–)6jø©§&5684i‡܈ë¯w]m¤“hš}­gþ=€Š—U²ßËŒ ãì뻹]9uéU §Õ\{)ÁuéêFM˜\¦ÿér÷™=0ÿõ‹§ª•0­…x½õÖKÀ¢+®¸Â]RbÊ^º®J€>ã}ÓM7ùOy§ý(?ÕÖ!Üe—]”mó4ŽXì÷Ûßþ¶ûîw¿Û¬3í%_]¸i¹å–ó Ÿ+/E Å|¢_IkÑÅ_ìeÊçÙ²M§áþ¡~Ú¨)F F ãG £ ,BºÙÜŸ€1*(ÌKŽŒMv’+O &=£à²‘?MV”àØa°¡‡¤<©0äâUN^DýøSý’—Lq’•pNà U‘Êð¥#S96: „± À$žTzøPGä_سÁk#ða'=da»Ò:œ°ÿ´ñUQÝ„9û4qÃ4[Óa¡öe—` ¿õ6×Íð[`o§îj7¢©O-Wõ³37<€tóMn¾½1íºÜP×wÝuÜRߨÈ}6q¢{ï6ÖÞ®Ž0ÐHFß~››mëZôøÊJn¹Ÿ,þ”qú náNèºñÑoƒ ]Ÿ¦›œ ÉÝΰørñ´äúGM[•ÌóP4çòË“ò¡WŒ÷#T0rê}{hYdo—Úb ·Ì¶Û%z“vÙ¹p}¬q¦U3›à>µ7=ì­]ÿ­¶NbÆ…‘*¹ì’–T8â(ç~Ï—b$[7É1ýeÖ»þ6JŽóš~ÅŸÜœË.ó|Evã¯ps "2ð`y{ÏÈh‘½‘ùè#þèa ç2Ûnëöm 8’+ÒrÇuÅûÏþCª9>öy¾ÜùÐd÷üË{¾Sí@[gé±[øp>Ì0Ðeεscëi_—ü½½ýÅ£Œý«´¾8ò ,ñC»yaLS®Áy›Tiö#íÁhMèíŸþÔ-|þ¹¤¸“ûzTÌYYoíà†Ùh4}xýun–=è¶&ñÌC&ôÇ?þ±äˆ˜t*:X7IþŒ¨xàÜk¯½æ[xeTi/ #F¨,e#‰¡é6ròyÑËÃ;tÇw¸_ó¼&`4 #F ¢1eÊ7lØ0|0úè‹NqL±!@bÉHš!C†¸ m=K!Þ°é‡c‡ãŽiR_þò—½4´ct |N0@–E›ù¬üž{b]/z†BàˆQa=ížb_¡C]"ÀÏßü¦qýMÉò¤L3ÀZÇFBÄ…QC,N¿À^èðõBÎŽ«4pÔR}ø!æÐO<áŽ>úhÏçÝÞ8õÃ1αNÛ “O>ÙÝŽ&NéÇlŒ@Œ@ÇŠ@Žx¨sà#¼Brt„CÀ äÁF›±ÞOX&ÌIøÆ/„uàŸ?NÊ µ¡ñÏtq}j|ɯü`+ð-Æ•*£Qðt‚ŠÕp¥”¥}«áJM%±ãŽ›Fc£rÕ<-CŸrPˆv W d/Ÿä¡P.;¥oTiûßpA×Éûïï½þZ³FT ³‘L1ƒÕÑ`_ìiFvSÀˆ›–·©Cz¸žú‹_¸?”w3Æ ?ítŸO/ÒZìAÒëðÐo€X/2ô°Æ·O!@’·I£š˜²ñ´›”vãÀèhéÍÆø”€œ4ͼë.÷ùSOyqW{Ó=âwçz~®(zÿ§ Ùƒ£¨‡GÚjM¥­}Bzȸï{Õyÿ{ݽko Ã)m¥Ö<Ék§6‘V2â(Ï~Ï—R{ß»ð÷)ÓÖ8íy”­¥RgûÊ‚ü€ý½Z%v|¡jª÷¢¿üe2-éPØÛa¦È‰–»ô2×Û@‹,ÀQ–ãºâýg@HÞãS}Ó²çC“rÞó/ïùÞe5ÝH{@ƒìáa²Üh¶(4ÿK6Ñ}X8R¢©É>ÉÚ¿JëëbàÄȦGT4ÕŽ›öÿPoà¹ÈŸÇv^/cÇÐ2ÛlëæüãY7½ék_ï÷Ôÿ¯ê°§B·ÂµqÝ›>%ÿþe—º¹6ʦ#P¿ÿ؃{a[f?ûŒûàÈ£ì¿^—è°´6üWlŠ/£ ³Ï>ÛÝÂTÖ T pÐÂC=Äh@£xf ¾íE!p¤6\o£à˜ô¹]—†Ú1ÃZ.´“iVÛ(‘gˆ¦û0Ši<ŒLñ =zôè/pp±ÓN;ù0¢ýÔ@Úpš_©µuŠG¬¿Å´¬ùv} ÁGCìwL-cꥈ©^Gy¤Ï†ky…¾¤ËÚ=L‘c \¸4å|Q p§b*Ü/í?¢Ží)›!4Ž5ʽùæ›~z§F¡sÁ¸{î¹ÇÝe÷S"âËô<ô˜È4>üBy÷ƒ|+8½Æî' ²ëˆÿügDŠˆè<hgàˆ©jÂ)xPæÏ”?0Ò.¶!ÓC47è“gƒ‡³ñð‰>v‹ û”_l¥ ý¨\íAGõyeû‘­ÚBý²“.eeI («h t.tªFÖP©*¦1ÒCF¹Hy•‡õ‹ÇžºÐ…GNž;ñÆzÐ ÛC^í¥\6èÇ›ü«Žª§ª™ÏÊ)Xе¥O¥W-wÉ¥®÷×¾æÛ6ÍÞ8οëÎòí´áÃõö whÎ ÿtÓ9B²O×?ô°—0êfj0\?ý ùá_®u³ìFVT 8ÊÕN9Uš1žR'­d ™åmaÚž6wšd#Ãlœ4…Àˤ ¨{߀:›&X÷=^•©l“ì&LÓ®d_8Êk'§Mifà(ç~Ï—4Ĉ´OÎ:« õËž÷××Ößðk©lóM_V‰m=)¨ÎÞøŽºòÏž÷ǭݘ§Ó¬ÀQ¦ãºû¯’ãÓw,ü©à|È{þå=߇ÛCU7@!£É}ß>-ÿï°åÙø úWi}!pä§JîÜ8¨þ¯O&m›´Û®¶ÎÛT×Ç~†üC¿fÜôC L®Á~÷•tïî†àκtP[ŒæñUðÓeÕÕÜ ö ƺy¢…RL=ê¨Æu $¬aZ__ŸLÝáaöÞ{ïÍä½àˆ‡Í/ÙÚ[Sá®´)Æéui2UÚŠJiàˆÑ*L“Òº;TÍT¾¯~õ«¾Œ–‰Äz2Ó×JšBú"G¬Ëðí¶ÛίÆ%+ptß}÷y Eû"{1zí¶Ûnóî&÷ßÿ‚}Ø! …ý´ƒM…B_äo°µü´?ÉHÝzë­~tùwÜÑBƒÏKŒÐYÚþo!¦«=þøã-ºÚÂF` 8bÔm€¥$úÁ~à×o$ˆ"Õìù&e &Çÿðáý˜>°ÿ´OBÝÈÇÄtÜtà lBJ°x6Ê ÀxBðÈØÀ"Äc ?tØ‘â|BrRô xùÂròìáñ!ü6ù„u-›0¬”h( W¥ðj ¾È£…:˜…›ôÙâ“Î)¯Të ‚ð Q†¾l££¶©œ”2ôàÿf[›SÁ‚®<—˜‹^ pÔmÌâÑAtpþ”·Ýûö¦ÿó¿Û +æ\Œº|ýënäÅ—ø"°ßi6%Á>Ç<~|bªsáƒ$uMc¸u0õŽò¶°aÃ¼Ï ÖÉÓΤMLÖx†v™ÌífL~JaÓƒdè ¾Û&›ºáM ·N¶7ò‹^|ÁõØaG7ÌÑ…¦þò”ÂE—½Ô¹bÀQ^»&—I’8ʵ߫ˆKùÑPp¬Ðelý†e¶ÛÞ÷EÇY^»¥íavàwwö¾JY€£¬Çu-ö_æãÓ÷ªð§’ó!ïù—ë|·7Óõ¶&´hÞ§n²=dä¡ÌýËQ_½}øanaÓBªõO6Ž>üÔÇ~oÜ8ßì^¶ÎÈÐCK€£ZìwgŸ·_î’‹]ﯮìë`±÷™5˜r’'ÎemºÚZU62cЮ¶žX@\sµ›m# jMŒÐÐ(#×0ÉB•Gáh|¿o/XdšÅ¦?ùä“,ÕU¤Ã(Ö}i‰˜NvÐA%¾!pÄè Ö½aêRHŒ®ØØ‡ÕÁC3ô<ØC¥@€/"pÄhâ •š™8âXáøa¤‘ˆIvì@Höû7ÓôA€¡Ól´€~ûÛß&"N!pô/ùÌ¢ÓiP3œj9Îþ§þ˜oª™it÷íß|óÍ“c°I¥YG¿²)ÉŒ6¢?O5Þž8qb2OýpTÍ~PCXÈþr[b ¾¾Þ‹~øa÷ 9ŸŽ“ôc##Ðq#ÐÎÀÑzð@°RÀ™PF¢ü4²SYhgj øƒeØAä!á’« `’¡ žQ® ™tð\©±ÞFõo‘0ÌJT̆s*¥±ð¤j C¿òM*9zê€t¤¯2SIì±(B=6ôØzØÆNR9rÚƒ©äÆú|˜JGú”ŠÒ¶.èúÎoÎqŸµ0Ü> pÔݾʵ‚½}… Ö²›ú¶ˆuøÕ.õtƽ÷¸OìâÚ`sãC ¿ÞÊKñ7“L÷ $'ÿð‡¥¿Ö–v–£¡‹JâÚe~0Fã|bÓö>¶›bÄ›F]uµ/z眳Ýgöæoà©§¹¥íf š¸Ý¶Îž<<þŽòÚ…~á³G¹ö»ŠÐè´Jã@“ìF»aÚÔtÓÝÒ6…bàN£=ŠG•Ø ¿ëîd¥‰›nbÿbÍÿ/³GYëZì¿ÌÇg*rŸ9Ï¿<ç;‹¯ºívßâöx†©•R%ýËS_M´þ~ 8zÛ@È…O7¾oHGUïw{Àl#Bú­ËýŠ4ºñ7˦udêj/FœûûfMœj îË6"¨™q A8ãˆ#Žð묔P-W±ÊY6úQÓ¹BGO>ù¤ûÓŸþTÕÃx螇¦í”#@ M #—_~¹™9`ÄØ±c½\Àë5i=$Ê>M<À´¿ˆÀSüøº´›M©ûí·=þdŽX¬šc­…_ù*§K9_&c:¡€d,\ÎÚFi §zWpÄC )ô·¿ý-™>—®3̇À_AD £ÓíC$L¿ƒÔGÕìü1eŽ5ÐV±)çЃ>èN>ùäùhÄŸÎvŽ˜²æÀ~À0a"JÁ! å•‚g@ØÃc+<òÈÙ“R)¤ºÐ·P;T_¨'_J±GOyñä!åå£QZâWF%Š›‰Ñ§u†< #ÈT¹± /¹ìHƒh ¶j(zøÓæMœèÃc/âñ…vðÈñ IÐ {@'Rõ£m#[c@ bno𦨔&=˜X›šÑ@»Èj-ž¤ ÃÅJ €£&¥:›{>ÈÞ ÷Ûs >¸öšÆ7ÃMëlôÿÙÏÜ€ïìä•Xüu–-ZX’¹ì¢¬‡ððAr’Ý7¼ñ¿’¦Å *igb_a<;c2?˜/ÝßÕ7M‰`ß©·ƒòY7r”Õô”wÏÿƒÿ”w8}­ÔB¿ÉÆÙÑÙàòÚ©-J³G¹ö{ß~¹ãGw´aøö˜4…£„ŠG™íìa\#ÆZå2ÔÞ35¨¥5޲׵ؙÏ0pUœ•žyÎ÷:›:0êú|‹sJ©°yê+Žš¦:Ò`Gá1ŽªÝïý=Î hš–Ò¡GÇœ€£íað³·Þô#°(NOeLr³L™Ñâ¶./¼ðB&_•G8äA—O…ÿÐ^€ðIõ41E`F@Nº¼’< 'û2V胇oM]BG,ÒÍÚ2i:5]pDŸ¾ FŒ3ÆóéŸo¼ÑOñù"-ŽÍ(6­7ðQìSö!§@Ä.\ãˆ/ˆýÝeð„Ñ;ÇPKÇ1#”ƒù´`ÇBÔéOØ#gM!Ž]¨¸ä 3ü„‹Ñ3rˆDå(ާ8 c¤þ8ªf?PÇ6â›u 8ҨܞŠå1?í ±Æ‘¦¡‰Äƒe¤7tò€3+Á<‚”rx„òè“ÇF eàÊ˧tчHÙЕxìÁL°§~|CØC²oÌ•ø•r‰â1•ŠBçaƒi¤Î¡§Æ‘ª’ËeâU:IuBa¹òjƒFá‹à@–HåKrìØ¤[þ*e¨`A×ïs‹lQÀ–(| Ÿ¸Õ–Îîhš©÷²)aCù©———|¡Ýt÷²E2‡Øüò.½z'~°C_YGWÈQžIÙ¤Ú)ýJã);R=˜‡SOÂò„·õ?᳟Ú\ü÷< ) ™®k­åFü¡qM§·ÿÏF%غ¶¾QW[÷ G/$v(åuš€£¼v‰ß&&+p”k¿W—6Ž l¨·/vA|ÁmÊ·§¿5…È'uõõnÔ5×z¾ÀQ-ö_æã3èH5çCâ&ãù—ë|·KëmÑs(\ô<©» SqÿrÔW pTÍ~ÿ?æ¾ø¢{ÿ'?¶+¤.‘eÓŽÅŽ´SÁè¾­·²ÅòæÔ¬u|EŠÅ!'Mš”Éw¥ÀQètûÀ_¶b ’¾bEyzAãЦµù<ÀQ=’©}|‰ iZË®aLÕ‚¾HÀ#SXÒ(Ÿiúa­5‘òy€#ÞÞk¯½¼»¬£”PÐ_ 8ên£€V{ÛZ”à`z £/Èø3–`%ˆQXŒÆ*GÕGÕìFe1ºâKxãÆ«zapï,þÄÄ´[Ú8ÚÀ:ÎM` ‡6;à âÁ‘BȰQ^¾c§ä›6=c[Û=Õß{Ÿ¯k–-øá1G—­·ï÷¿ï–=à@¯7ÉÖÖix÷Ýf6ÓÙš€‡fJ¡Àø{Ø´ÜìÁÄÈ ÇÞ”Aáˆ*ëAÒ×Zâ§…vz‹ñ kbCšû|}Õ’`B¨;Âö[W«š¸åÎ^ÿ…ÅžpòÉ®ÿV7ã“öØÝ5Ø–xöv¬ee7ì Yÿ–¿Ú¾`×´è¶×iÚyíßMLVà(ï~Ï—6Ž,£&LpuÝ{ø¨4ðlM„‘·ß‘|òü³É“Ý;{.^s$Ïq]‹ýWÉñé;VåùÐtÈ,NÊœyâbŸu²}ñ˜ÿZébDë1‚kûí·o•õÈZêw,‹ˆ¨}Ú8Z×zŽVG°ñ‡~œ°ÊÁ#‡$'ÅŽ”‘G¬¼e=Q&’2µ¿ÂBS˜¤:á‘cÁCÔ'ºäUfli’QiÂ’°£*QgÉÓp6’:Ñ0xù€WP踮N‡Á@&ßa›ñE©äØ…º”1‰6I_í eSù³Æ· % ºZm·Ýƾ¿ø¸¥ÐÝÞ ®pÒɾøýË/ssm}…²7ê£î¼Ë?ˆÍyî97ýÐÆ‘GvwèX dk T'¶Á›øOíø=ûº‘'[˜µþ±Ç,B„ˆ¯d_=zÅó?v#Q7dH•ëA2o;­1yâö!zŠMñ u{Ûâ£Ú>¸ú*7Û>R}þxÔÍ·x‘ÿ ØöÛÙѽÐõ³¯Ò Þë{^þþ%»¹6Ê“Å×׿¥= 4± 8Êk¸òlVàˆyóì÷¼qikàhȺ>¶H)4Å+ÿü‰Ç=Ïy²œˆZˆaø7òyŽëZì¿JŽOÚ™ë|¨âüËÚÙÏ>ï³/¬ó£» /Äz…à'Wÿ̾ÒúªŽòî÷ºúÅ#Þ>¾íV÷IGýl´ìekµÍãÓÚMtH€£®wsl4™>žà’oY‚ÝX5;lØ07xð`÷Ê+¯dž*–èàÁ“/ñÐ.p¬F‡#0Þzë­dÔˆÊÛ*Í ±^ 7â‹gk¡qA¾Ì¾âWoÇ¡ˆéZ€Å(k<‹Ù摵6pÄâÉÛÚhl覛nr¿iZŒž<Äiýõ×/hvµÀÇ‹N3½ :Ö®Iqß•"€¾ðÇçê¡–€£5ÖXÃwÞyŽ6CØ'^}õUÏWóÈ(p‡…ÆÑXлUåÝáH¹G}Ôa°Tû¢eÂ=…¸°I¦ºL”ÈTŸt(ƒ³a]˜‚‘„þ¥K Ñ>l2Î+!U ‡Š‘Ñ ÐFRrñ¤ØR/y•áGy?ä¹òQÆÎ@‘Ê^mAÎ0 :”¢ a OHeÈW9õâÖû t}÷¼ß»yMŸ~-WcÝ ²Ñ¢i67~þý÷Yd,4¸aö´6”zÛ†?/|þ9Ï È`M—÷lÎûü§žr ö©dÿ)x’=Ðx]zì^7 H…£O˜*1ÕØ^ðÏçý4¹:›vÕÓébëK0ÝMkÏà(σdÞvæ§ïpÓOwÚ¼Â/Nò9F™¼{Äá®Á>k¯­\O[`О{¹i»ÛÐl¦‹ØöHuÔÅÊ í!i‹F.Xຮ´’n_@ш–·mÑÖ…Ï4΀ì²Ê×ÜÈK/õ6ü¼w¡í‹ÿ½á†z¨ëikO¥IÀQ^»´?G|Vü`„[ƒ|J^˵ßsÆ¥­£îßÜÆöõ/’ðL³õIÍ™í–?ú˜d*aRhLµÇu-ö_%ÇgÞó!ïùG¬òœï>ÆvÌ µ…ùõ©y€ºwÏ9Ç}þúkÿ1C—w}·ë–±}ö6Ó €ÍÛ¿<õuùš³^äMÃã ËGy÷{øEÆE6µëÝß7_lÚ7È~l$Ò‚‡T¶MÓ:[gÔ•ö¿g4ï7Ü'¶ø5£,{Ù`šJ¾tH+Vg”Œ¾FÅC1ÇYH@Ÿ×i™a×ÄðášÏ×3êé=×]wÿô8#oX_°êg¶à:ë¬ã«ä³ç,¢Ý”8ÚgŸ}Ü틟"žmàÁœLÕR©ÕeðYã™¶Ë›omàˆéiL}]o÷goØñÍW¾†ÚK¡4U á/uÄh0@Ÿ§Ÿ~Ú/‚Íè&ŽuF·fB!pÄW×^{í5?-öì²Ë.^‡úrûí·'ùj@ÅñãÇ'Ÿ¶Pdd]"¦<¦ÒVŽ&Úrê©§ú*§¬kåÝŒãÜ„8Wµ ·¤~tnóH11;8ß3 eãPlA8e¤!­ä¤™Œõz¡ÊÁ%Hñ ¯.M)rä`%´…r•›ðÈ(K·|Ú§‰JYI ò°Q‹FãK’_ôàüe½=v*“?òapÐUGÑ—?Rô ÆW*‹ícÑ"ST'vÒ5Ö—?ÓªÔ%X›5VvÜÑ¢Æ~ÏFấ,Þ¿ìR7×nxEz T¾TºÈn'³8qÓÛcéußf[·ÂÏ®lÉ4|°Êó ™«UÆ3錽™zùŸ\¯WLDiF@ò.ö¦v„@ˆÒºä?ºõ7Ó‚CÒ”£P&þýK/qÝì$ùzX0Õ0¯|“ 8 eð<ô½»ï>i±Ë³ßóÄ¥­#¦H ¿ãÎäËjéŽO±·ª=F´õ¿öEátÄ<Ç5NªÞYO{k,¸_áÿK®ó¯)xyãâÍ­oËÛÃHÏúÑMÞŠ'~Ay1™·‰× êó UNàˆúòì÷ncƸ᧞4·þ/•Ó­eyµäwÊ Ç»Ï'LhI%W Ñ‹•h›m¶É4%E@‡ì”N:µà[À‘ÊK¥ ì…ë½¼[dêx)›ZÊóG¬sÄ”}Y-ݦßÙýBì` µ]’5žiÿyó­ l\uÕUnÅ÷7ß|³ŸªÆ—í P˳ƑâÀ×ôX低JÙ´ Éñr¥–Öâ¥êfÝ¥ 6Ø 7p”e? °¦cÇŽõM0ÅúcGyd©f5“Ë®YAÄÄt¨tàŒ° B¸‡ò<È#×&¹‰pô„C(O !ü ‰2p R|c/’œ|è?ÔÖ£v¡+9¼ìà3Æ• àB#¨P /d”Ó9R™òØB’“Ç7„xå)“oÕ‹/dØ£‡œú™N™ÈF¢<½IO£Øš?“íÍÞ¢—_ª¸ºÛË ;î8ë ] ÈÞšø©S5¾!OJL¯Û˜ÍÝ€]vv}×hüÂFRÖÄ|bo­?f¡Q{Ø,FØ/ŒÈhZÛG:ŒbúÀn²>µ¯l4¼ÿ¾Ä®ÎÞ’ºåVŸÏ¼vIŽvÖ"žI£môÃ@Q¥ÑW’7,˜ï¦ÛRÆù2­µ¬½Íë»æZRõ)£&¦r²_» €ŒÕ1ÀF»ôßbˤh=p¼cú‹þõ/×wÜ8·ì¶Pº4˜ÌÚ&¢¼v²·tØ 7&#Ò±›c#ȦrH(JøJ÷»7¬0.]V^Ù¼ìroZjÚf_¿ì~ûLËkç+²u†üöw®Ïj«%}e±ó÷O>É5؈³pÔGøÅ¶\Ç55Ô`ÿá£ÜñYÕùãüSðrÇEì!¶ŸMSóS9SÿkŸÙ¢Ç³í“÷³ì3Ù½vÞÙ =üo•÷ÿÓg¬¯‹ªyÅøfç£Öó ÿÛôEË™Mp|cÏrì÷®ë­ïFØC{jö?‘Ũ†:|©®×˜1v]YÀ¿z|¦/˜þ¾{דּÝçÏÚ»VZƒÑÀÔhÊ”)¼É2ràŽ;îp¬á“&}ÍIr¦ù0za»í¶s+E…_Í`ɺ0·|×2Ýd“M’ÑN€<`i:ÞŽG€Fˆð@¯81rŠöó57}a” _÷ Go”úb[ÖxʵikG´‘5“ë®Ë²ô¡}í“Xñ….­¿³È€l€Q6fYHvJ ™þEý!12X3² G…ºðœÙ} Sµhc­©¿-op¨”fJŸ¦Ø©TÖ>âËsÄ©~ŒüaÚšŽ;¦æÑÇ0FŒ¤ïÏ?ÿ¼ïþÊí‡cìž”µ·ðÏqM_Ãÿµ©TÚ•Ò‰òŽÆ€5€oÀóç æ ›Ì)ƒ(Gt”G.)$¤"é¡£zÁ*ȳ¡ÔK9v’£‡ RÈ t©}aÝ”%9.Z˜â¢BìÈ‹'OCÕ8c›é¨Áè ¯ºÕ`lÄË¿:„œ B†êGN^ öÈØ¤o¬'tc‡y¾qN‘϶Îoi»2Ìn¦?ê©ü•ðpeï.õ£] _^ø¿×mˆ‹M­*7äÖ–´/]_ 1žiJvÕµÃ\Çy M¢N{ÛTxdŸn`]&»¡)[g .KelgÍâ6„~Ú4¼:›ŠÖÀW€>ù¤å>Úö…[h‡ ‹¿“rÔÓöÁàA®Ñ]øÏJyí²ú/¦—w¿ç‰K±ú[SÖ§¯«°Œk`¤FÚµJµµØ-Ÿ5;2ž5õÚÿOþÜã?&xð©YÿÔð2õI­ê´û½êF´¾ƒd£ë¯s³lºMk¬Œð`ç—^z©èçÈkU? î.m×=F8\|l/X>±ÿŒbŸ@¯Umå‡8¤1%ŠE<øë¿­ÚPi=;ÚHm}®Ø(¨Jý•Ògó5¦1ÈL°ÿxà¿ørz”Z)?•ÈY°œ}1hÐ 7oÞ<¿O¨? …ÀÑ·¿ýmžÐ6¦~žå~®’F•ÐåÜãØYÆî—88vŠ­VÂ<³¸Ø~Èlcb–ˆtàH± ##0=è€=èÁZºà ÈÁ+ xÙP&^eé”rôØð=<þÁ5äD;t…{À+Úˆ·âÒ$ç¥5—H—Š©PH®F„y:‚ÂÂ>ôÓ½IŽ­6c} Èã_€žÆÜb_äÑÁ¯|HeÈ!xù#OýPëOUk¬'þÆÄÄÄÄ,Q`Êâ›n3ómzÚ£KTßbg¾`zk]Ö#FueëBB·Ýv›;óÌ3‹©µº,Žm³$€˜­´XAŒ@Œ@§@;G| F@x›åI!6`lé|8¡Ž|€Q§.á `ªÏX_NŠL˜ yìdC^DýèAjc1½F¿d%:AE4Re€3ÈÔPÊ”8Cc{ØFª`àK¶¤êåj—êðvT¶€> ih‹Ž|ô4^uJF› W8jŒGüˆˆˆˆˆˆøBE0hˆ}%–µŒY4Ù¦.Ê0ˆÅw¶é²">yß^Ó#p¤½Ó/B:pN!,<à„,Ô¡Œ<Ø„ òFàÈ(#/ùÃ]a+ò«<)¶e²þB¢,ÄXð-[x0Ù#/Kr\VÑä\à BøPƒU¦F‘ªôÕI¥”¡#0I¾$'•.àAPd¬'ÊeO*ž@A”³©|R&¹±>OÚêSÕ¨$RŒ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@ÇŠÀSÅÊk±Ht{QŽÚ+ò±Þöˆ@;G,x',,Lƒ lA y0È(c0D9ê€I‡Ð } £ \¤P¨/{µ ðª}Ú¢rRêÀN©±ž°-Kf%UªF)j©: Î‘— À[ÙëI~у§³vP@I^ ¾à•¢C»dO^v]d"_®úÔVô"pDt"ÅÄÄÄÄÄÄ|Á"ÀºYŒ$ ¿Ø†€Å©/¸à¿0z(okþ ƒrx ¯vóÍ7÷ë!µub}111mvŽ˜ª&0 Fà@!Â0ß6°0 °R6äe£”rxR]ìC’.öl`²A|.áSåàäeO QO ©¹¿R.Q\ ¦ai§ª”FÁ+@äáÊ[Ð0äê~R@uDþ%§~xäòL£ rù EÙëõ±£}µ'NU³`DŠˆˆˆˆˆˆø¢F€ѵhxOû _U›>}º_¬º#Ä„EÍYHZÐJ_.ìýŒmˆˆˆ 87Ÿ`ƒç€àmHÁà!ô =¤*“@ø„,ôƒtË—lLT€³¨-àðÔ#’ >(W;ÐaS^z&*M¡ãÒZ%a'qŽ-2ˆÆÈ—ôÈ#'/ž“—>rŒ"•®ì”ze/°}Hz¡/ôɳÁ (2ÖËäKSŽˆL¤vŽ@Žò ð ;` lÈ GâÁ ô…h„’°R@Â6AJò¿¼Q«± ØâG¸|ˆ¯ÐNa-Æz’?åK¦jXI…  ˜®*Wãè¼RSÀ:²ƒ—Ù“u6,‡§\¾~ÒúäUåøÂNºiý§m‹##############ÐÎè ÀX˜)8CŠŒ”M ypÙ ³õ­8ÁJK_þ(¯?0–Æ\ã/:” Ä’.¾Å£C=òglË„r%„>•©R€FJÃÕc“@ÁSŽ>D ¨£ÆÃ«ÓŒ$‚Сœµ‰à©› ?=ôµ!Ç)ei[•&9-­±¸Dºê()TçÕaRé¨QÊ£K#IÖ›Œ ‚— z¶èŠTNÖ%Ò¥ ¤²1ÖËHC tââØD&RŒ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@Œ@;G GÂ;ÊÚˆà  ‘jÏ@¼™lá)Óf¬§bå ¶!PIà~ñ¡¹|b£z‘“‡Aèe"dQ–®E^Á«/ôÈ«Näya'^Ðô£¶`+]ñø’êà!ªCõ!o‘Ô𕚠ÑÕ¦ŠÕ(Ru€`‡ð¡apÄã ]ù06…„Lm£.ü¨nü@ÈQN;RÀ |“b'èK®zå½8UÍ‚)F F F F F F F F F F F F F ½#ÐÎÀÑMýÃ`¤˜<Þ![P9xƒÊÐå”M²E8Bá_yxùžA}ÒîB*ŒCeä‘c§zCßÈÕcKF•’lÔDøA¦r5Zù°Êh¸:€yRd©«QAèDR'µ¶¶øeƒ—^(C®2c“éjè@8jŒCüˆˆˆˆˆˆˆˆˆˆˆˆh×´3p´®uü@¸„@"¾F‘²¡fÁ&^zpHUnlb§z°Ÿ Å}8ˆ|`£2ôÔc=OJ¹ü c‘œ4QQVPC¥ ` QɇzÒA¦N…:ÈåƒT표<p åïëêêÜ!CÜ¢E‹Ü'Ÿ|âïY:b(è_ÿþý}ÿ>ÿüs7}út7gÎœŽØÔŠÛÄ>èÛ·¯ï_¿~ý܇~è>úè#¿O*vÖÊy—Þ½{»å—_Þ}úé§îÝwßÍtl¶rW¢û¦”»þu¦ã3îÔ%/í ¾´Dô@IDAT­g>!àE£„{€u‘G=6™xt òà…ÈË^>¥#[SñvàØAøPÛ„¿ GF_è¨L¾(Ž£ÔD-Î*!ôC*UƒC?4€Î“ª\ –Mº#È!Ù¨“ÈÃéh”Ë–2åÕ6Rª2•+oE¾ÿ¡òšG§œrŠÛzë­ñí6Úh£yà–ø¬°Â î‡?ü¡ëÓ§[¸p¡;á„Ü‚úßYÜý®]»ºÓN;Íuï®x‹ËÒ@Á½÷Þ›wú|Þó¶˜Ýµ×^ëFÝ,&Ï=÷œ;äCšÉ;›€ÿ7ú }ï{ßso¾ùfgëBÒÞbû/)ŒL«E`·ÝvsGqDQÿo¿ý¶£¼åµ+櫣ʖ[n9wôÑGûæ=ûì³îºë®+ÚT•vØÁDZgÏž‰ÿñoøÃŠÞTsÌ1nã7v<Ô‹þóŸÿ¸ /¼ÐQgHË,³ŒûùÏŠÜgŸ}æÁÀ¾—_~Ù=ýôÓ^V Tef©¥–r?ùÉOÜ·¿ýífž«_zé%w衇6+ëèÆWZi%·å–[ºwÜÑ-½ôÒMÈ{øá‡ý5™8‹Úë:]éñ¢ö®³Î:þ¸œÑŸ¿ýíoîÌ3Ïô@±ä­‘–;¸?:òÈ#3Wý÷¿ÿÝÝpà ™õ;ƒb±ë_Þã³3ô7¶±sE £õ›¢–Áø®@J^<Dø`EyälAHO¾ÐÁ¤:À0&!§\[˜‡Ç&Ôþ‚½0ÉH©7¬Ó²å ƒ¬Dch, B¾hDƒà)Ç(l,yüª³²UžrÉò ƒÔR€%UäxÙóŒ<´SÛéþŸ¶­¦T츦Dg1e"Э[7·ï¾ûºïÿûŽ›7ªÅÞÔòÐ1a©µ˜ò@rÎ9ç´¨SIá¯~õ+²ü÷¿ÿuðíEyÏÛbv§Ÿ~º[yå•“® :Ôó8JBÒa˜bû/l\G9>Ã6Õ’oþmºé¦þÁQýxå•W çÉk¬á¦Nêöß'i^»ÄA'`ƒþô§?¹¯|å+¾µO<ñD"¥›˜³ÝvÛ¥ÅIž]À¹pô£Z.»ì27jÔ¨D/Íüìg?sýë_q}}½ûË_þ’ä‹1'Nô/)¹T b„Ñ­·ÞZl¥ýr-ãšÖÙhàÀîî»ï.ÛlÔýöÛÏÔA¹=®ÓyŽÚúƒüÀpÀ°Eé½÷Þó}«Õñ’®$Ëy´æškº .¸ mZ2Ï=ÒqÇW²¼3»þå=>;cÿc›;vÚ8bX=¸†@¥` ú<\ #€gCOeÆz°(ÄQ ¡ƒ\ ºöÔA™6c½oR° F> ë@† $Ÿ*#¯vS.¿¤*CÞ"¡˜•Tú€/Pƒà!Êh©l(£s¡Že}ÇieÂŽ€S†ÙË/zò­z¤¯zŠ(C‘—Ž@$üã‡<¾ _­™ Z*öG\­Ïh#5 =>õÔSýÐð´MVàèwÞI›&y€£k®¹&ÉWËàoذaî7Þp{ï½wµîrÛç=o³Ø=òÈ#þ(G¹wO«–Ûåøl­´uÿx cÔ¢Fºœ|òÉîþûï/è:Œº)¯]è£3ðü2ÊFT 8 z™švÅW¸W_}Õ}ãßð £S Fh@@¼D¸úê«ýTzò€õ<83õë»ßý®Ûe—]{ G†ÀS©î¹ç?µ ðiõÕW—‰÷W ðK*`~ûÛߺ 7ÜÐ[Ð?ŽSÀ,@0&V[m5׫W/÷Ë_þ²¯CUæŒÖb4Ù3Ï<ãR˜ 8 àÂñ{ì±î±Çó|8jíëtÞãeÕUWu—\r‰o3û‹}Éè0öç»^¤Ð~޹ô¹î «üÉrñr6+ÝqÇî׿þuVõN¡Wìú—÷øìŽìThgàˆÅ±!pnH…<å?üaƒ7€+‚G{xÊÉ£ ðÃEš¼ìÐc“?ù‘otEøƒä»ñ‚ßèOuÈwX6ä±ÇoèӲʼnJ²’*“¾@|PåòGYH”I?ÔEFcåé ¢ÜÁ–¼d؉G^mO[u’W9õI§ªY0"-HßX2,™( ptÓM7¹ßüæ72iõ´­\Ku¨Ø T)ÝPžÅ.GaÄ:_nÿu”ã³µ¢ÖÖý>|¸»ñÆ}w ²Ž2Ìk×Zqk ¿#GŽô@B8J´pŽèøÑ~T°vÚüc·Ï>ûø&2%è¶Ûnó<£˜ÆïùY³fù‡ö™3gúv½°ÊŸJΣrU…Sc)—´iúÅ®yÏr±Œå1•F #¦ª°¥A#á0lè ³@øƒ\¾BøAL?à¤"xt±UäåרÄ^¶”Ë—êF©®Æ\™_•QóÅ販‘¤äiP§”—o¥¦âõÒä¨CÈÐÁ¿Òǯ|ÁCäŒHÕòô±—?Rɱ‘œ©nØwàˆ?ëÖxób}¬)ÅvÖ4œ5uÞXòPöÇ?þÑí±ÇÉCÄ’ q³NÇÈÔb7PYüd±«8Ê{ÎUl¡bqmi£–ì²Ä3¯NÞ•Ûm ¬äíhWI,ÚºáhÖ\ãÜÈByí²øî:L/f4Pz Y)à褓NrÛl³o:ë1*GĨ#ÿL+fC¿øÅ/ܶÛnëùtìy gDëáAÜ‹lµÕVnîܹ®¾~ñTµb€£645—‘Œ­†¨bý4F?UC•œézª±MûÊšgº&kMAì;M ¯ïÅöCVÿYõò/Œ$¼ë®»|¥Ž]Ž×ã?Þëd™þUÉu¬Òó¨¥XpN w¦Ô±ÖV¸æTK¶•–Urœ•ºÆVâCí+wý“^˜–:>CÈÇÔ"í iª]pvÁ¨ ð HX©ÉT®<)˜„FCÈFþ6a+œ;nÊÙÐCRžTºÈÅ«œ¼ˆúñÊUÞ,ÅIVÂ9 «"•áK F¦rltcÀI<©ôð¡ŽÈ¾°gƒ×F^#а“²°]iN؈ϽÆÒ›l²‰>zôh? —¡Ç뮫Ñm…‹có% ãæ‘aºèòf›b†ñróÇZ Oæ"‹C>ùä“î©§ž²&;¯óÓŸþÔó¼Åa ¾©ƒÅûÂ…E¯¹æÿ¥é‘rqå­$Dè¼óÎK.ˆ•¶3/ØÅ F¸Pð’ÚNà6úᆂ‡z*ÕÚáÛç%8bo¡ù²S68–x{ûüóϻ믿Þ¹¥TÈ+=oå'¯öy€#¾BÃ:U,°Ï9ɦº±ð6Ãþ‹Q¥qáë;ï¼³wuñÅû:ÆŒãÿ§¸`S'Óˆ¨“¯áˆÒÀÑ[o½åB»Ù³gûéHŒ`ídOʨL×^{mÿ`Ê_b* 7çü?ò?)ªö)ïþkk`Eý­$å‡ý8vìX7bÄÿðÏú/LEbúk•hòö¯Òý§þpÎþîw¿óY¦dqÎf¡¼vY|wF^p¾C¬'´çž{z¾ÔÃ7玦iqžð’@Ät o~ó›>{ðÁ»ýë_þšÏ=ƒ:ÓׂðR~4MªpÖÇbÏL}«†¸ï ¿HË:N|-®ªäÿ“û0þ¿ø€Äïÿ{–_îÍV\qEÿ?øè£ú2€´Ö¦ÿû¿ÿKþ—Yü[‹•·%pÄÿqžã…{à‹.ºÈ‡(-ر0ø•W^™ˆˆ}1@=R¦²3ª-Ë´²JÏ£¤)†óäì³Ïö×_ЏïþÇ?þ‘Òª.ËqÇyºÖZkù¥¸þ½øâ‹þÿšûêcôÔ®»îêGñþù^Ÿ>òB‡ø2}“ÿ|Ö^â^ ‹ÿ×ôZ–y¯ÅzXêø,¦e1ÕD GŒ„ƒø¯‹@ŽŽpx<”i3Öca™0t$á¿rt á øç  RAªOí‘/ù•éz£r?WBªŒFÁÓ *VÕR–ö­†+5•Äà†b£rÕ<-CŸrPˆv W d/Ÿä¡P.;¥‹‘—FÝL¿Ü¨s!ãæ¤% ¿ªÆZº ò¶…²í·ß¾¨9e>ø `=}Ñ$\×€‡?†¸Èq‘çbÏW´¸A<€q^˜Ã?~.NÜœhdA¥íÌ q!ã«/Kz;ýNhÇŸJ#nRy` Ÿ>®5qŒîµ×^É<Ø@Z!¬“ÏׇŸ°gþ=k&hñØPž6Ó~¾J“¦<ç->òÚ©þJ#.–ãÇOb$?JzÓ‹Õæ‰K±+oTYë$MÜÐ2r ÊjÇ1Ähp4ö|톇]MW@–&þ·Â¶Tó¿TÉþ㩚ã3ÝÖγˆ0£ZŠ%‹MkýšjÏ?úSéþã«QÚ—ë­·žÀÁ/Eø"WH< Oš4É‹òÚ…þ:N b_q½æz•ŽˆM¸6ÓƒÎ=÷\¿Ÿõµ:Fë0ˆÿV¾Œ¦i6ÏÜ#ˆ¸‡À“äúŸi 8⸻ýöÛý½ ÷c ¼Ì{O zI/¿ürÇz}À vkZ¶ðSéÿ'&q‚vß}w?MŒ5”Ò¨Íÿ ÀxkQø?ÀÇt=(ŽZû:÷xá[ëuÔQÉKÏ0^áèAäßùÎwüO¡<×Àð:ÏçG)Ês•òÅ~>ì°Ã|1‹´ŸuÖY¥T+–ó’˜)|1XÊûÀüÔSOõ*œß‡~x¢ÎñA;‰×8/‰*¹þ•;w[:>U_LcjvŽ˜ª&œœp‡‡"0Rp d”Aàè“gƒ‡³E ®äS~±•>Üh 8òhú)÷¿Téþã\Øa=¥øðø,¥ÓšòO<1yA,©ñÚk¯yp‡ÿr6b©é>Õœô#ÏþÈB\›xPƒòÚe©§£èôèÑÃŒƒ´(5Ÿ,‡JG”…둉óŠ3œWPþÜrË-~4rêgÔ 3/X°ÀßÛèá”ÑËŒ^mÃ)RK§vZòò'Üwø®†˜RÇT)#(x¦¸HžNóü†Àà—FbsÞ°h5÷f¢°ÿ’Õ*å?‘ÿFÎ1ˆ !8Ga­q÷y%Ç ç®^j0B†‘äið±8##ÓDßù?ñ¢”û‘bTÍy”ö÷Õ¯~Õ/6\@.çF-ˆ—¼²ÜÛC˜f»`ŽZjgK_I™Ÿ%½eºßêÅÕGaãþ÷¿ÿ¹#<²êiÜ1°Òˆ3øbÓVx´€¸¡âA¢=œ#álFÍ ƒXgƒóG`WÞó6¯oDÓOVàˆ›J⪌‰²Î>€ÂESóÆ%€TSý¸ágä_¿¡=ì+¾Ì¤)°•Ø1uAëªà‡ÿ!Mçeä£Ñ<8=:p”å©ÒýÇHÞãS1lË`އeˆãÐ($Ž)â΃TÍù—wÿq<ë¼eêÀHJ}ñKmæë`š’×N¾:CÊ5\£±.»ì²`@>룶Øb‹fEá×Ô( G1²“8CÜ¿0ÂbfFwAª;R™È qðW{?à5ýà_ –äLϬ*6.ïÿg©ÖbT$ý «¦œ—µTç Ë Aƒ¼(k¤S 8R9i­®Óy¦L1ZŽÿ ¦õ1Š5|1‚2j7×F¦‰ó‚óâ^›ã¡ÔñUíy¤ºYã‹k (÷\“'OVqÕi8Å”¾xà~Jqè˜sеÎ18vìØdÄ:|‘õ+µŽ2bÃ4Wþ Xߌå.ð Uzý+ß,ǧ¯0þÄÔ0í ­g]/àæ ,p&”‘‡(¿@G¸‡ìTÚ™Zþ Gvy;”$WÀ.$C·ñaªQ´¾‘)5ÖÛ¨ò-†Y‰Æ°áœJi,<©AãÑÁ¯|“Jv@:ÒW™©'öØq'‚®êG­‡mì$P•#§=è’Jn¬Ï‡©t¤OYESÕ˜õăsï5}à í'+pÄ!Ö(ЃmxópÄÛ|A\|yƒ¾}BŽtt1JÒ<¼ð¢ÓÄxHKS8*×ÎRš´ß–òKr;[êw[”eޏáfÁJnš¸qeä7.á0qÚÊÛ7ÀMŽÅZ7i¬a>v‹ùgt‡ÞÌ{û˜~pЃtÞó6¯]ºíY#úF!€ý÷ß?ù@Æ¾Ñ #£­ØgPÞ¸¤ F7id¢wl?<`r³ÏC€P+±Ã>«®SCûY§*=2+p”å©û¯’ãS1kË”‡MÖª‚XÉr%ý«fÿ)&¼Ø˜ƒÆ+(bi^»b¾:Š,œzÎ:aÝø²G<Üò 9zôèf]\Ñ…ÿŸ½;»n-ç¾¢2%M’PïÁ'ʼn u’Òé1U(CÈ|ê•!S…R†½Mƈ"%Q2„„LÉЩT’!9rätPÈœþþ뻞óÛç~Ö»öÞk­½ŸçÙÏ{îëóYûž®ëº¯ûÚk¸×ïÖÞÞ^¸ˆgfóÆùƒÌøð² J?CžÁ¥8ê˜~Ø´Ø6i´%ýúL@ª (¤Î¹÷ϲïEWfZE¯ç °,ÏžmìãÝBËýÜg}9ñ¿~]ú†]fûs”ÏéMÎýZ}Rä>oÖšÙfn™%§%õgV•ežy:–}„oÓë(z„–Ù]xá…]– æ ´n“ÊYTý/Î Õã™ B|èÜCf)…øÔ ŒA%ƒKö¶ÓwØÆóOcÏÏØSÃêmyà˜£»´í€98àWµ‡›W0‘„p”t€;äÅÉÏÀ#-ß!_¨!J]xá±#õ•|Ñ•<¾¤—FIGÇ~î’ß-)>+¿ Òi Ðy©¼.âÉœÄ@²1}9Êt›½à'}‰Ó…J9qùt¢ðÈ„iÇ$àHÇêAzP+ÞtSºËÍ[»Ìög pô¦7½©»Á›iØè˜›*­óï‚"³(,]ñ 3ÃB'óüóÏïx÷ööº‡±—dE¬’8Zf—޲‚l Œ¯\²´LnJþØW/FÀ‘½&œoCT‚–6Ø#lîu;W®o×XàH‡5ûCèp÷¯enû;…ÌFvçú¥€ŒZ˽ÐÔã%ßK; 4UÎ(¨k¾¼Æ—ušÇGcïKÛøÿÆžŸƒŽá§œ…¦z¾±)¹ó>ä¯2kJû6ùÿbÃ\h®\êݵІþÀ#ù® ³)ÊÙWë€#/s®•Ì6sÝêØë,TÎ,ºà‚ ºÙ~Ê\ÓÀf Ñ©S§:öÌP¢Ïì!dv?x²ü( ä÷B_Ö…ÙfÈÊ6!>²¼)3£ë©O}ê “çÜ?µ§Ž,ɶ%@|-—„ž>=ðŒ­ËBƒ5–qæ°7Ÿ%zýú—ÉË?Œçô&ç‹gµsLvˆÌ燧¾*7â_•·éuTêά˜sùå—/–°ç<p´çß6Îϲ=5^=0ÅÇ Ùã(O¹V5qXFÿÐJt¾õ­o=«Yc€£±÷¥mücÏϳrD–ž™A’åeµ–øQ¹×WY.>¥}›ü©w.4W.õîZX.c[ Û—ÄÍYù4;0ÁL;¡ûšM†‘g¹#÷fÀr¹ü7׊ý}Ð,õÊ=4÷Ieå,$À–s ¨à¨ÜãǵìãåÀ‚}ؼÌ™ XÏR^uð[fV͹Z&[>ß <ØÛ¨Oå’ŸÓ§·XldœMÀõÓ.¾øâI QigÙŽMŸÓ›œ/lÒ6`(päƒ>èƒ:3ŽÎ_ƒOf˺ÇlˆßeLøÙô:JU–(û"@Öõå<0Ëi›¤ÔG€á,;_UG Ýýîwï¶µ(#ËK-ÙFùÿmúüÛöù¹ªµ¬z`ÈÇ A¾áÀGŽ;ð…Äá ‹„H™¤£K>¹` âod„)—Ÿ¸ÐAŽ›à$òbyy‘>²ò£§®'Âc©4€LiƒËŠñ"y%©ÏÁxò9䥑Ñ+DÊè“æpuÉ‹“Úh·lMˆRVÊ‹Ë&ŸN:¢›M“6Ƕî<7ô¡O³–/ZËö82Šg4oy8Ø“ ¢ç¡a„ÏÈ—¶ £UFm"èóö>Ù]{uöÌLùâ‚Ë•ÀÑ;£CGcèK$)ša•N^™/~®ÚÙoçQ§·éTÙ 98è/ÑLÙÔpì‹«iü¾„LÓpÚ¯¯œ¶„°$sîu;W®oÓXàH'PGW˯Éõuš¥àeÆÈÿ\¿”P6åí×sæÌ53‡€£±rîgÙÇÂh«ÙC¤-þëU›c½/mãÿ{~µå¨òøÖyo!`BY·ÁÏ¥!À~lû§79_úvéÃ:/ËýËål›ô'6¹ŽJ{žR]?C« úíššvM溵O‘D먎ò>áÜÉŒ£òY˜ó8ÀÑ&Ï¿Ã8?×µµ–Wô=pÌÀÑ[{à°px™†_Èâ}(“'.%_HNè0ó(/å™ÁÔfueBü±ƒÞ`!òÕÓ@©S\>$ŽÔ—<¼Ò)k£Ë)BË9–” MI+ÍpF—FªCÃÄ£C?ÒðžF—ÎÝ¥Ít)&Ÿ\É«ÌL$6…?v)yMF„ŒH  ÿ¥pùr+?7zñ9€Œ‡L:€t ¬¯× ôp/Éž!åFÝ:YFíWÓ'#5Ù„´,›cg)?'~.Û9ÇÛ”Ùp¤a9T¾Úa´ÐRªmP^\R—_ ëë6³ƒŒN.[éeLg¶ìbîu;W®«¼ø •£§™)P¨Œnâ—£Ž¼Ô¼¨ÝC ÕݧrCÖmGÛøÿÆžŸý¶WÚÇ|qÏ`‚¶P£õäm]›ü©o.4W.õîZh¦Î²Ïqtì3Ê3ð¨µ.°eVPx…‘Ü÷òÿ»Ö Úäÿ+y¯~õ«Y~ô£v¹Ú“åÔ©ÕÀQ9Ô2úl¶½PºåÈo|ãnÉNÔfoÎý“޼p‹Gú'üà^‹.¼ð³–ôv~Êç”/òž>}zÞMPs€u›Ïéòz/+s¾”üCqËŸ,Ùb/Óµ=´}Ál?o“ë(˲ý¿žÚÌ×K¹¡w¿Î¹i_ƒÍÆót×éÚ8Úäùwç纶Öò꾎82㎫ !Xƒ¼òR†O9Üáš}höåñ+ î ‘¼ÔÕ-øR_x”!ù:È•!Œ¤Ô^!b™ÑDùJrT,O#Ð& J™üÄ…dã é”Ñ“tÀi Ž2†|$¤Cyli£ÝÌ#H ñŠ#²ât¢”É{I—3ò'Åbï:éT˜Î­Ú8ê¶´œ~\©ïnw»ÛÍf½|f/ËXLO·¡72Òf¤ÑTø’Ž8:—í,}{ñò<ê±ÉæÕ^0CC iʦ†FöŒ /J}:›ùL½óÚ^G}²göVH'wîu;W®oÓXà¨Üca숤ºæúå(#r࣑gÔù²·ŠNµ¥WÈ2HhÎ}iÿß”ó3¶îBÈÎËEYª§OŸ>Ë´)íÛäÿKÅs ¹r©÷¤…«ö8èd¶ÒCúÐîSÞýö™i™Y9ö.³L¨\º‚BWÈÀ€åˆ^ìQÖG@®lT½ ÔO=ÛÍ¢³98ʳ`îýspdB_ ¹ÌôÜ„ôͼæg` ¯b.Û:`J=Û~NÏ=_ÖÙlðÓ××û>‡E«®£ÔYøšù•/@¦|[!`J_ ³ÿÓ?Y¥àhîóï°ÎÏUí¬eÕCØà¾3:`@qÙDpeÂ2Ü"y™Z¶Ž¯Ô_âôÂ(ÈÁ."ßF»º¥a%å¬$eˆ$$Û·Aº¯³ÍZNÆ£Uœ#rœÅhºÒ¨èÅ+^¦Ûd§ƒ\Êð!éÒ9òÒPüÑ'ć8 ENHÆ„òbCê$Þ6Ú•OZªfÓÂlÐX®õ7ba3@ –´)pä±Áo¨]´nÜç{CW^yå½ÊŽN‰}Œ°x˜XV†|¹ÉÈe¹„aÎ Zl˜žëvÎñÉ6eÆG:2: åÔíØ ³cÏ Ì|³y¨Q¾mQ ô Ví+`Zv^„íG`ùM9Ry»ÛÝn±Yªe¤:ä6dŸ{ÝΕëûf,pTŽÄÒaŸ“r‰`ôúOìEâ³»h®_Ž8²ìEG¹_šJ€îe^PCåWÜä͹/mãÿ›r~Æö£ /6,÷2[~h!õ—#ÜËf‚Liß&ÿ_lš Í•K½'-\õÂ{úôéÆW%ÑЋ·ûƒk+›W»ÎóuÿwÈý±8*—œ™lZ›Îœ9ÓÁxí©eÓêMÈL}ª¡sš^›eÛ+ù*£å^žs€#_óücò•9_›Û„Ê™•¶#ð%Ë1tÔÏé¹ç˪¶8—žþô§/f÷èã"3ê-Ct{Ö³žµX*;Ä»,oÕuû–ÉÐ%—\²ø(MÊ·–ƒ¤fÕú"ÚªÙÛeÿ8ï®1KÕæ>ÿ枟ÛôSÕU=À;Á8€=0ˆàIHRæH~]€;dÁ"’"yÀŸ’”Á1„ô’%_ºÔAú…©'vµY‹|ñȉ" §PŒ¸0B…1,ºä)×8!’·ÿ¤í’l™O݈ñ¤•Ewê¥K½øä åáÈ$Œ¬/©‰÷ðÐqi{Œ&3-¬K=ç9ÏéFòt¸nq‹[${æF/c΋Mía²¯K>Ãiä^§##øF M3Gö 2zŸ2Ílêal‘Œì›Iâk(¡9vFvjxm°sªO6áס0Êœ%etùï}ù„q–=H€ŠöÕ˜¹b ¤iÔö¼pÓ¶éùmnsì]ç\GÎG¶EFÃuÜÐX¢CååÀR£É%SÚ Fjsî:罨xÁl)Ì9¯=ÙOkîu;W®ï›G^ÒÊ} t˜û/!娹—!/-¾‚hW³ÍÔžy9ÌWÎæúå¨#_Ñd†,ŵ$™%À)fO%ñ9÷¥müSÎOv%Ýö¶·í <ûÙÏn^üâw×Ù¤îóž™uR:”6Niß&ÿ_êœ Í•K½'-\õÂë?5£(äe×lg÷A÷ý€ìÇS.‰ñ\¸äZ÷ì¾ð/(d†ÍË^¶?®æe?›cl²‘ûe‘¤Ìt evSÒsB×­û·Á3™€žÎiö{&Ù°;_“ëÏÌœsÿ,#û'i£ý7³²Cú~™Y•¼9¡ÙêÙ£O»²aò®\ÛžGýœž{¾3Î9lÏMƒ7@=Ïhÿ«8ê÷=ûí÷±Ï€AÛe Sxúáªë(¼å (· (™òm†žÕ€3³œƒ&ö%îÉ<ãµ[ßÇ9˜ë2ïc£¹Ï¿¹çç6ýTuUðÀGÀX|CsÈô1(CÊñ’Á“´úxD7CÂH’^^ËNþ˜µÍêrã· ÁCW§ßæ×%•# yà™¥‘™G¯|å+»—}º>•K˜Œú{‰Ä7Çξî1ék‹c|±-/ @в³µJw6`¶ìÑl$_è¢Ë.»¬[îeä÷0Ȥú3;"uèÛäÕKRy›Ýgô×ç™Kòa`2}š{ÝΕ+ëgOFÈËü|ý¤ÌKÜ‹¾iìê/É4ð…N#ã%Mõ‹=ÑŒÞ#` «O–QpøÖýÌÿ0WŽn3aÜ+³\Vž}QüŸ¾"WŽv–/½sïKÛøÿ¦žŸÚtä:ç/Ë=2+°_¯¯wò7/£)í›ûÿ¥îr¶Ì”Ù)såRïI ³oY¢´Ã ûò¨Í4²T¥?£‘< Èl?›h—äÚ*•³œ•›œe¥%¿Yú>úÏ{ÞóωmЭnu«îžD^Fîf]±aˆ¦Ü?WGW\qE·| ’¾ÜP}SòÌ$ñìCe?ò¸žÓSÏ—>pT¶ˆä u¾xºŽÊÍÎÍZÒGÍ×ÿÖɦ|Ýu„Ïó-ËÍ~µÿ°ÉŒj_q3¬Ÿ_’¾¾:³³¼g˜yå<0Hœ~PÚU¾KXFéžàzÌ,î9Ï¿¹çgÙ†¯؆Ž8²÷Œ‹ ná “´°ñÒíè§KÀ ?žè€QH«K½â°ŒÔ×F»<¡Ç’_cI#TÃRpF^ m£‹tÀÆZ*&Œ3芬0 R»RG€Ÿ²¡‘úÄaÂROtø¤LêL›P€«ÉÀѾxÓÍ,2ÝÔÔY/\ÛêTD «ŽÚ€#_à³Ù©—IÀ†MUuÚŽ‚_®)#rF“íÛŠÐÒ@€vhxË[ÞÒ]Cû–-³glûŽãÿ[fóµ=ß (€ÅŒM/‰–ùºoŽVÝ'òZï¼±lùòË/Ÿüb~˜þ·G“ÙÝBËë€TîŽ1K–ÆÞ?Kàè¾÷½ïbYœµUÏŸÃlû*ÝÇñœžz¾6̞Ϟ·îG:üw«Î˲Ýöt2Ó”®×¼æ5ƒ{0–ü'1dó,´º@ÿ…–ííµIû¶ñüÛ¤þ*[=0Ç;Á)‚CˆÃ¤ób"¯äQ& ›$ 0‚OÈS&MWô‘Çl%z“’EÊ"+?<)+1º#+s‰¼üµ¤¢±ågTˆèˆÁ)‹Q¥ñøÓÈ„äðLŠ®ä à ÜáäEm´#å‘&ÎQH¹#õЩ,ùm´K '-U#P©z z z z z z z zàä{ Žæ~yôä{¡¶ z z z`·q °ƒA Ÿx@!ºÄâaWä¥#—ÙEmVWžúb+¾ ñN¥êêêêêêêk™*pt-ûÃks«ªN„Ž8²T-` &àM@!>¼ª=` 0 X„0 td*"¼äK /y¬"2xÅ剗à)‡sHG^ˆ”‹ QìØO-ù ó’âÙ`ÊÌTÊ(ñ8HZ< L=`˜ü4€Þ8BôIC¢?ù%.?ºåeÊ!^2‘k£?9åøcÏì¥j­ŽJÕÕÕÕÕÕÕÕ'Ô8:¡\5»z zàœöÀGpø„CÎÄ6B˜ƒ8‡ôS“À>É+õÐG~tE¦Í:€³Äx†¸zB‘¡CyìÀãH:|mÖr*/çÚ/)I9Yyˆ1Ñ>iùÒ‰3X:üò, ù2ToäáGá+uá—vˆ(j£]^t`ªÀÏTª¨¨¨¨¨¨¸–yàAzPóÀ>°kµ/—Õž~×27׿VTTLòÀŽGyØ¿¸cpÈC‡7 üÁ@2C)X‰0P° y(a@žàÉßçÚ·,=x/ñvÊ/e£¯Í^M1l5×~éo*q$Ç#Œ3ìà‰œxôG^¨QilY.®<º?}~éÔ¥œ.ráíÇñ_Ú•ªªªªªªªª®e°ñ³´ÑÔ¯v]Ë\U›[=P=P=pdØà–à€i,"’=Be}Ù6k9EérŽkJ›† 52Oƒ…á‰QIãe¤0`M]Ì  >Do(å²®è ¯2:„‘i£]ž°’Èà©›cóL¥êêêêêêêêêêêêêcöÀGÁ;Êmð €0x‘0<¼A^dÅ•åh£ •+ÀÛ¨ð‡^:ÊN2©W¾4’‡ð¢ŒaoŒ’NEâ)/ U.O¹<2Ÿp8*2á Ôuz¤ÉÅq¼Ñ×Fö¤Lž¸#ö’ Ŷ µÎ¨T=P=P=P=P=P=P=P=P=P=P=P=P=pÜØà( ÌVá/y0IÊ"×FrÁLJð'qòð ºðI§NùH:ØI—qõOl!ÞÄéŠvxHêH}òWR _Étu!Þ©8F Ó!`‡ø0,NâtáŽ6º˜…$/¶©‹žÔM’H¹49!0ˆn!¹èÀŸüÔøêRµÖ •ªªªªªªªªªªªªªŽÛÇ ÝùêöÃ0Ì4‚ˆ#x’[H9¼!eøË|e° ¡àH>¢?†´xtÏP_ø‚»ƒq¤LZ>¹Ô[ê–Ÿö´ÑåDh*E& HD¼”Çè¤Ëz”1< À#-”‡„qlfá ˆ”Ffo#²ô:ÄÃWæÉOY],WÃ*p´ï‡ú[=P=P=P=P=P=P=P=P=P=P=P=P=p¬8fàèŽmãáÁ%ÙøZ:ða`ŽÄÃG˜ò6ºK=äàB²âø‘<8HtI¾ØÑF»¸PyôcI¾p©h,¨Q)b€@¥K¾ðÈK£JùÑ!Œ=É—GBÊ,"eê¡Ø{„!2)—'MVˆÖ¥jûþ¨¿ÕÕÕÕÕÕÕÕÕÕÕÕÕÇêŽà°  ŠL)¿\ަ<²Ê’ŽmBMYÊ“n‹ºrúK>ù8â…JÕÕÕÕÕÕÕÕÕÕÕÕÕÇìcŽ.¸ºù° ð® ”N@§veÒòØ _tá¡¥FÀ$ùÊs”iq2%ðòÁT’'ToYg›\OÆc˰’èòÅb¸r2xPi¬4½ild“Vž¼€<òPì–TIÇâ‘7«H~)Ûµ‹þKÛc«ôøÍû¾ïû6W]uUó¾p«º«²êêÃñÀÜëvHî‚ .hÞýÝßý,C¯¸âŠæÿðÏÊ?i7»ÙÍš;ßyßÀ_ÿõ_oþë¿þë¤5aaïÐÿ·(¬‘Cõ€käüóÏïÎ%ÿÃ?ÿó?7W^yeóº×½®ù™Ÿù™¥uÏ•[ªp ®së4ïò.ïÒ¼Û»½[sƒÜ ùÇüÇæŸþ韚ÿ÷ÿúݯƒÆÎ•;¨e·So÷vo×ùå¦7½ió?ÿó?Í›ßüææ?þã?vÛè‘Ö”ÿoŽùßnt£5×½îu›·¼å-Í¿þë¿v}ã‘î92¶9í;2ã±¢uÏ¿k«_ÑåÍ=ïyÏæ³?û³›¿øÅÍüÈfUtŸÄçæ1G:¹p Ì !ŒámíÜÁCp_ÊÚh*•ò€Bxäd‹ȫCYŽ6ÚéÂ.Ì| Ö! ŠÎ”IÇnåÑ+L™ü•„q,¥üÀ¤1H)cX@›4TãJž6Ù5œ Ê8OˆèäteôS½â±'õ„_~@‘2ùH:<‘è§Gšî—·ÇVé[¾å[šÿøït~ÔG}ÔÚŽßV+¯Êªfxà½Þ뽚‡=ìa£%_úÒ—6?õS?5šÿ$0νn‡ä~â'~¢9ï¼óÎjö+^ñŠæ+¾â+ÎÊ?iîoÚ>÷s?·yýë_Òš°°wèÿ[ÖÈ¡yà3?ó3›‡>ô¡ƒúÿæoþ¦Q>Dså†twž—²ÛÜæ6ÍÇ~ìÇ6÷¹Ï}šÞð†LýÆoüFóØÇ>¶yÛÛôS÷i®\äûá{¼Ç{4øÃ»ì—¿üåͳŸýì,^ü¿é›¾é@{€7À¾?þã?n.½ôÒ6`ž™x×w}×æË¿üË›ûÞ÷¾giV¿æ5¯i¾ê«¾ê¬²]Ï8êÿo®?æÚùÎïüÎÍCòæc>æcñ>ýÉŸüIó„'<¡¹ì²ËúE³Òoÿöoß]#×»žnýj2ÐñË¿üËÓÜö­®ád•=ÿª_ï?t®þÖoýV“sÕóÌóî°é¤>78²96‚À•q借`p…CâxÉ‹Ã#¤ñfÏ#iùÊñ9¢/z¢o?ŠnØ“:¢[Yâx¤ÉÓ+-©d,¥²ð„¡CeÊ£OYIÊÂ_òÊclt o åÈS€¬tòÈ%0)6Èwä "OÒ)W_ò·¾TmèFÜÖW©z`g=ð¡ú¡ÍSžò”Ñö½èE/jùÈGŽæ_Åø˜Ç<¦YþüÏÿ¼?.š{ÝÉ=îqknw»Û-šr‹[Ü¢‹Wàhá’‰ ý¥q»r~–6m3~íûèþèæ‰O|â¢^$ÿìÏþ¬qÜáwhÞøÆ76]tÑ¢<‘¹r‘ßµð&7¹IóK¿ôKkÍòbñ…_ø…Í[ßúÖŽw®ÜPEf…<íiOk>à> +þßùˆþS§N5?ù“?™ä`xùå—7_ú¥_ÚüË¿üË`ùÔL3¯~îç~®y§wz§¥¢€+ ÛI££þÿæúg®ïùžïÙüìÏþìÚj/¯~õ«×ò­cx‡wx‡FŸd ™ÉøßñëÜö©ç¤ð =ÿª_ïßýê¯þj7»T-Ÿø‰ŸØÍ,=¼›æ$?78²T ~àèƒFÁ0<Â`øQÀùÑUê£L¢N! ‰ã%›:¤£·.ä#«<ºR·<”ºöSk~#´†­+Æëˆ‘BiF1"J:º¶,@é ' ’P‡ÞðÓ]âH`$Œ=Ò(üä£O˜|2É·Ô|ŽZ'Tºv{Èáåa,ýÂ/üBóøÇ?~,ûJ>¹[Þò–Í_þå_6Ÿ÷yŸ·’÷0 ‡:Pcê#÷›¿ù›Ý PŽÆxôhyÖý»r~–WŽº}€ £þ.¾øâ®#]¶O‰Ö\¹Rï®Åó‚fÖŽY>/{ÙËš¿ÿû¿o,ÕÙÀÐh7úú¯ÿúæ·û·»ø\¹N¸÷ãžkVOhpd Ý ^ð‚n騭o}ëæC>äC"Úÿ‡¿ÄÈw}×w5ù‘ÙIüÃ?üC·tñw÷w›ÿû¿ÿk¼L|ðpóŽïøŽÍ·~ë·Nк¬GýÿÍmõ\;Í`ú™-§¯ø4CíV·ºUóµ_ûµÍû¿ÿûw&9—>ù“?¹ûOçÚH®ýíßþíRuîwÏzÖ³ºò¹í[ªü =ÿª_÷´•‰Ùxúƒ¯|å+µ²“þÜ÷¹]scgΕ;,¿mC¯N½Žñ_üÅ_œ”ÑÿIŸôIÍ7|Ã7tUýâ/þbc&#š+× ?^äV–¦„ÖG?ýÓ?Ý|çw~gØ;à™Ï|æ"}{Ücã=Ï,ã°ÿ²$íS?õS»%q‹JNxä¨ÿ¿¹îÚÄNÿáÿ·w£ƒdé0É~^h3.Jà¨~¬ý`j“öÔtrSCϿꗓûö-?éÏÍŽqSóÀ„WÀ"äã !GYŽ6Úñ—eÁ<ðH¢›^$ B?œCŠ òPê‹=ѽÑÞNhÝÏ5=„uœû婌Qâ¡âžPY_w Oز,ä7H&å©C~?¿ò8(ÄùqDä£S•ù‘KøÒ}–íý݈Çhw³î²Ž‘;jžjçQ{üøëÓɳ¤BÍR{N”{nlbá6^\½ø‰Þ„æ^·cä6Žæ^s›ø%/“C~]µÇÑ*¹MþŸu²s}´îÿÛÆù¹Îöm—OñÅQ·ïö·¿}óC?ôC]“"®14WnŒî]å±lï~à:óüOYb³ÎÞ1r6+þñÿñƬ¡’¦GdÍVÍÒ\3˜ÌÝ„ì¹@öO³Ú&4åzè׳‰l_רô¶ÿ¿±õNåcçNç4Yt¿ûݯ[š:Ä—¼uϱ¹ÀQô/ ç¶o™¾UùSγeÏØ):b˺ç_øÊð¨ü2§=¥çr|Ù9Ðoó6ž›ë®¿~ÛL3pd©Zp /ÀÀ Œ!`‘¼¼|À'ðK;Ä‘|,"ùPtF/Ùð‹ËG¥ž”—²©oŸ{ß²±Eý‘ ¯²µÖ2¶ o›ôIÅŒIåò”‡’ u>@IDATNyYâäÕ…W\¾´\âm´‹g&QiOÊä)LÒt9¢?uÌ^ªæ¦v·»Ý­¹Ë]îÒíÑbÚ´)çw¼cöÓjšrsl;Úg·›é´x?ÿó?¿qq“7%Û^¦¥ñqcsÈßû½ßk~ÿ÷¿5½éx¾ò+¿²‹‘³IqˆnuØ´Øìi¹¾TS’Nãƒüàÿ½ßû½ `ªsÁ.KÊ‚ÏU;K¿ŸÄ¸‡Æ·û·wç2û𰵦ÌyqµNÜ(´/{ٟɹdÔÞ´ßç<ç9ÍßýÝß-µoêuEsåÈÏŽìñ_ðß]“ÿöoÿÖMm¶ñ¶Ma‡hª_L™þôOÿôNÕþàvu\xá…Ý}Ê[Ö㫳ôi8ú«¿ú«¦”û÷ÿ÷n9ÒýØ+m6"ýq÷q͇ø‡7§N꾺ã+R–/%ÝÝ'C›Þ—æþsÎÏØ|T!`×ÿ¸··×¼Ïû¼O·!­ý_,zík_ÛØCh@3·}Sÿ¿øÂ5ûÝßýÝ]Ò©±SõçÊ¥Þ“ZÖ“ëÓ&Ð6®Ccä¾ì˾¬»¿Ðgÿ"_ùAs€#Ë ?á>¡“·É·¯žmBú=åiï}ï{w_䚢sÊýS?Ìýëÿ÷›ïùžïé!Ü›ôÍÞïýÞ¯»ÚÐVÙþçN1cï¶ÿ¿YFŒcg_ ÇLC3ìÙeªe}HÇ?úÑî–²›Õ¶l9ýaGsÚ×o華óÎuóaöaóÕóÏžOî×ú3úYf~Æg|F7óîû¾ïû:~×®@²å›îùöœÔ2ÀçþÚÿêàÜçßý‡é—)×miÛ²öéO¸fÍ\ä¿þ@Ø\¹²îuqÿÏýïÿ³Ø|eð‡ø‡ÏÊOÆÜþYä…sž›Sû‘e}ÛŽïpC€Mèˆ;”!`Œx@!ñ€<°ˆÄÉŠÓ‡Ç!OH|"ùB|Hœ]ùÒ(òâtÁãˆNñ’·MŽ'‚S‰¡ O¥â1†.i<¨äÑ€+?Gøâ€ÈÒ‰?é„qDYgÀ#:‘2ü‘‘ŸYEòR.T–¼—´ñɤ£îAæ&·ŠJàÈ^—\rIÇþ¨G=ª{!4MwˆÈýÚ¯ýZ÷pµD¾hRnbìáòE_ôE¸—{@@Ȇƒ@'#äL§°œ!RÞø=œtJsCj粇~WùŠ7ì'?ùÉç¼+\p"Š<ð¾ú«¿º³Õ¾ßömß¶‘ÝÎÑÏùœÏYìÝñ%_ò%>ç_f"”ø|}ù {ëïí‘Í\K^qŸkö"c…>͹né˜+—ú§G–OúÓ>Šž„€ÞþfµsüR@6ß5“ìÓ>íÓRÍ"Ô¡u’ÐX9Ÿ^6[ ^’=0€B>;¾ŒÜ·J[6¹/MùÿÜ—69?—µç°òm"lvÆ*_ÚlÚÈ>Úôú£cêÿçkaù/ït§;u/8ôñE®’¼ ÿõ_ÿu—5W®ÔwRãåùÐð‚íåg‘sïtAÎ|Zz*pä¼ûùŸÿù®¯¢±··· ,ùLµO…£?ú£?j¾îë¾®û”ûù©÷Ofö×û¬Ïú¬ny =”úä%Ô}0~XtÿßaØ:ÆÎ~½x¾ù›¿¹é”9Μ9Óg[¤£ås~Ùì¤8®zþ{öè Ì¥9í[—AâG<âÝgÙWÉ¸æ½øÇGOzÒ“º¯ÕEÆ}ÁùÈOžq!ƒ@òBSžëúó‡é—©×í¦íÛ¦_bËPx÷»ß½ûŠ`¿  e¯£e4¶ŸÕïŸmòÜœÓ\fÿ6ò8ºSÛx&à›J™—›ŒrøÞà‘KY)ײ-À|ÊÈ!il%ù±Ž‘<¼Á=”çºå'l£Lê‘^IÇ’Š”«”±âÂÁ0<ôF·0ùøÒ€ð„?e-ËBžÜu¯NãsàsØÐÚŸt½ö +eì&¿vé2 Oø•]3eGjAc¿ÿû¿±)¤›·QS#7¿ùÍ» s_•Õ¸è½Xšþ™MÉÝúˆøˆn¶PFËQB:4ÒÌpOÓiuoA^}BM‘ëo¤®ÓlS@w÷*ý+®¸¢Yö7VGWÿ¬»/MýÿtºæžŸ¥]GÿÆoüÆnŸõñ…™¯{Ýë:pǽÜÁ—Yî³Éõ§Ž9ÿ0†<›Ôh®Ü˜zv™ÇµáákäEØÌ¿u4Fîú׿~7+ø‡œ–ƒ½ä%ûchS€#çÒcûØÅàOùß­³u]ù½îu¯dŸ~Ž´Ø™ü~8çþYGÀ/@rÝØ¬<˪ä鯔{<ÉÛÖÿ·-û¢gŒxõgõ!ÜSÍ*õ’l¦â[3j˾gWPü8çɆ ”0íS •eÀ#üÏxÆ3:ð1ƒ£%ÏP|lû†d×åä5ð£o¼?˜5t饗v{BYòi@ٵߎVé¦Ç³•×ðÔçߪþüaúeÎu«­sÛ7WNSɹï\Ýóž÷ìö•›MéŸÍ}nÎíG¦]‡3pt—¶M0üàªö€1IŸ@I'„g òâdƒgà‘–ï/T¥.¼.îØ‘úJ¾èJH_Ò‰K£¤£c?wÉo„–Ÿ•_iŒ´hˆ¼TÞFñäGN b ÙоeºÍ^ð‹“¾Ä÷ï’ûv”:äK£ðȄʕMŽþð‡wËdZÙîeÇÒrDz5ÃC€Œ /M7½êª«º/›dÔËË“™C¦J£»Þõ®ÝF§ÅúÿQ8Œ z0âuó÷¢é2Za4—Ë ¯¶XÊPÒT;W=hJ½ËâçºËÚ½ëù:x–±x9p^ÙzÃÞ°±Ù:D–¾yH¡ÌŒZ¶âEht¨¼(£Ë.»¬»FÊ)Ùf͸n}6\?é,νnçÊuF\ý38Ò©äs4<Œ…LYØÃÖ%ËÍrçú¥€R‡¥~¦t­õytöø¯|'K`§ÈY¾ë%Ñã>”å¼–Ü™ý¨ƒòÂ|ÞyçŽVÝ?s_šúÿy±ž{~¦ Gætº‘óhT’sŠß½X M®¿¹ÿŸó9×­¥?#”Ϋ’ÕY ;W®ÔwÒâ|cÙöMozÓÎtŸ5w>®£±rú ™ýe™D½2åKgêôⓎ€ÀÙì /r>ñ‰OÜÊl£ÔKÿCò$»Ðò|`ÕÐr¸¹÷Ï8Jeöá1+Ò}¤Ü¤\¹AŠMPROÂÃüÿRÇ6±vª«<×úu›Ýú¦7½©Ÿ} í<¥Xp>ä¾^2.ŽJýÏΡó¦ä›Ò¾RnlÜÈ| P[øÀvKŠKy×”½Çº{{{‹Gxžò”§tKýžÿüç/Dø0af’­3lwA/šúüò/=‡é—¹×í&íÛ–_Ø0•L<Ð漏1ý³¹Ï͹ýÈØvá1Gö8Òq‚X†J–Ñ?ðÈò VBæ ÒF;<"iüð 2‰”yYJ::Ë x£Cœ¼‡4yõïw÷mk“¯p%©l,©4ã¤Kƒ‚Ò8|1N˜F$?z”%žzÒHeꢲ<éØÙGtí÷`®™¤£‹œ49Gx'GÖ»p‘+kï³|£ËlÆGfy€äŶ켎Œ6Ð…Uýåc£1÷¥müSÎÏøì(C ƒ½¥ž ¦Ü“§´o“ÿ/>1° äô郳Ã3ΕÒµ«y–}¹Þ| yÞ{¾§°Ìî±råRwû’Ö(ŽŽ–Ù@——ómP€Š~þRe@!uν–}/ºì™}¸¤=W€eyölc'zC‡ýÿ¥žMñv¦³Ù 6Üìf7[ ¥Ì}Ÿ£«È3Ï@'ÀrÙuàÿñ,4Ø…ßÌÀK¹ÌMfMœ]¶äsjûVÙ½¬¬œEe¹Z¾¸Œß,­3gÎtÅf”:÷ã­) ÂÆ .ÙÛNßaÏ?u¶_æ^·sÛ7W.þÞ4œ ퟕöMynÎíG–õm;~ÌÀÑÛöÀ€-0Žvà ‰Ã€3 Ûh'#ErÁ@Ä^e S.¸ÐAŽ¡Ì'‘›è‘YéÈÊO¼®'Âc©4€LiƒËŠñ"y%©Ï‘†Ñá—FF¯)£OšÃÕ%/Nj£Ý²5!JY)/._˜|:éˆn6½¬=FÿAzPÇïâ/7o’1À‘‘7x3BsS¥uþ]0™1d…¥+^àͰÐé;ÿüó;Þ½½½nO#/É^*u²J:sæÌYëhM‹õz—ÀÑ;£Ã¨œY)«È›ÿ"Sòž«v–m<)ñ‘ì5Õ{Ù¶öŒ}qõâ`4ÙkÂù6D%iiƒ=Âæ^·såúvŽu;Xé_Ë:Èöw ˜Í™ë—2j©S]î…¦/ù:(ÐT9£ ®ùò_Öi½/mãÿ{~v:†ŸršêùƦäÎ{K‘×Ñ”ömòÿÅŽ)ÙÈçÊ•:v9î¥×ìŸS§NufÚ£Í~0ë@À±rö—~˜9àZ4{£œí58b*/èöÃ(ÉìiçÓ¶‰ÍöÙËÌÅèêSŸz`Ãä9÷Oí)#û)Ù ï÷rIèÀ3¶. âÿ[V÷”ü±véôÜ2+À ä¸àùûÄ÷C3S¾Ihf«ºÊ%×¥îMÚWêYׇw_Fž³÷¸Ç=ûÂ¥Ž8zÌcÓÍ6âÏ|0çòË/_,aÏyàhÏ¿£ðËÜëvnûæÊù_6}Ï¡cp4¥¦ŽÐØçæ&ýëÔuá1G¾tG€ x„¦¿/ÄûP&O\>J¾œÐaæQ°‰Ì`j³º2! þØAo°ùêƒi Ô).Ÿ GêK^锵Ñå¡åKʆ¦$•f8£K#Õ!aâÑ!Žix O£KgÈ‹îÒfº” “O®äUf&›Â;„Ž”¿¼&K8|I);^Q28óâòË"öqyÚÆ¢™Âkd°tšâm£í,Oc§5¶Ë^*JàhŒiwÿ¥ä÷C7²Œr–eçªeOBÜR›VeÒyÄí;,ûâZŽJÙdžîCd”=Ñ{³0æ^·såúvŽÊMëû:†Ò¾Èâ…0³”¦ú¥€€ƒýŠÕ ¼ÚÛÛëª4WβÃì£D§¯êôi p4ö¾´ÿoìùÙoÇQ¥-=³·L–ÿ•õÚ|Ú—ˆÊ½¾Êrñ)íÛäÿK½c;²áO8W.ò»ê´ÛÐ6›A{^_|ñÅgý6L‘³¤Þ¦ï¡”¶ß‡/¹"³,ÓBîÿfö¡ÏRµrײ”/æöaó2{d `=KyÕ¡™…9çþi™l^¸éó¼³ÿNŸÊ¥-§OŸ)××S¦êÿëƒ`¥ câS죯\ÂfoÐrÿ—1òSyÊÿ·\:=Ûn_ôöC}x >ÔfÙyŸ¯L—À‘M– 6—ÀÑã÷¸Æ’m”v8ÚôùwT~™{ÝÎmß\9>Þô=‡Ž9ÀÑ”þ™:BcŸ››ô¯S×a„Ç ™qG€U…¬A^y)çîpͬ}yüÊ‚{…‚G$/uµE ¾ÔeH¾ƒre#)õ‡WˆØGf4Q>…RA€ËÓT@)“Ÿ¸l$2z’ø# ÔQæÏ„t(-m´›yÄ©#!^qDVœN”2y/érFþ˜V›N‹ºå}*_´ÌÈC»d¬Û·Vy8Ø“ b¤ÐCß‘//™âk”Ò¨³M}Þþ¯xŵìÕÙ33!ä‹ :ªC4ÕÎè°âЗHR.ôÐK'¯Ì?Wíì·s×Ó:þe–ͪÛfƾ¸Z¾ñÜç>·«Ú4í§}[Êå@K2ç^·såú6ŽtB"àjù5¹¾N³¼ÌùŸë—Ê&¹ýzΜ¹fÆâp4VÎý Œ¶e"mñ_{yóšs_ÚÆÿ7öüŒÇò­óÞÂíoû³L0Øà¹4ØmŸçÇ&ÿ_ŒÛ‘ ¹r‘ßÕÐg`È’,4v¦ÑT9@ y*Y2ŸkÉ–ÀQt•#ø@§¼§|›¡0³.³¹0°ÛKšsÿ^ç…›Ž|pD¼${˸ÆÐ2p©ä_?Êÿ¯Üp]ýò©vöå‡ÒþC| ù`KÉòÜÜ<ƒaê yîe‹‰Ãh_êé‡úÇéw¿à/hÌ ZG%p”÷ 6gÆQù.‘ó8ÀÑ&Ï¿£ôËÜëvnûæÊù¯6}Ï¡cp4¶ŸÕ_Í1ö¹¹IÿZ›‹v8‚u¸Q 0 Œ8l!B™°Œ“M¾0 SíøJýÊáBzÅéòòm´«[VRÎJR†È#!Ù¾ Ò}mÖr"0–ҕDzœÅhºÒ¨èÅ'^¦Ûd'O.eÑ']:oŠ?ú„øg¡È É8P^lHäÂÛF»òIKÕŒš‰‚þw‰«Ê‹OVnôâs^|a¾´´ L(ë¶?@>Sï¼¶×QŸl¸iÙ&2ãÕ¯~õbƒgyS®ÛM®wu…ÆGåÞRcG$Õ1×/G "uÞMGý—/³tª-½B–G@BsîKÛøÿ¦œŸ±uB~tþÛË™¥ ”èÓ”ömòÿ¥Þ¹Ð\¹Ô»‹¡>ƒ¸öÒøªÍ²%ä¥ýsåJýøØ=ކf²Qõ*P¿_ç&i³è|ñåY0÷þ¹8ÒÓC®3=7¡£þÿæÚzv²€cSh€¥¯Ó„íÏ”˜kóÜ-oyË{oeÐ÷°Ú7dƒ<໾@fÿ§²Œ_þ&ÀÑÜçßQûeîu;·}såVýOSÊv8bÿÜ~ä”¶OåÝàk€oˆÃ `9²$LRŽ— ž¤å‰Çh£ øBáÛzaÒ¼t„Ô«œ\òñÉC±SÂ#/ö•u+¤(,ìeRŒTHN:qi†Æ¸6zO ƃ?uÇ`2‰G$ßäÑáCýò¥Å‘—çí|røöß\öã«×‹µÌ%Ù¬24–(#R6‘-7ý#·)pä±ÁoÈ’5ûY ú j|N7tå•WØk |àèŒÚ‡ÀÈ“›¦é–È—›L3/—0ÌyA‹ sÂsÝÎ9>9.ûwYÿ.¹ä’Åæì‡iO ô Ví§dZv^„íÃdùMÙá¼Ýín·Ø,Õ2R/b6dŸ{ÝΕëûk,päÒÃÛ)²ïH9Å>zu@m4ê³»h®_Ž8²„EG¹_š2Ž€îe§NêÒ~ʯ¸IϹ/mãÿ›r~²ó(ÉùbÃr/³å‡bC9½l&È”ömòÿŦ¹Ð\¹Ô»‹a9ÃβôG=êQ£Ìœ+·Jù\àpæÌ™¦ßžZ6­Þ„Üÿô©†Îizm–mï%d‰“e~žs€£;ÜáÝžyìAxÀš×¾öµ]|îÏQÿGmgfö”ýÊÒVøDf½›q2DfÔ[†è>ö¬g=k±T¶Ï ˆv -Éó¬´çafîÛüÜì;tÿCß¶~º$5«Öfá™ýÔç•.ûÇyŸp}ŒYª6÷ù·-¿Œýÿæ^·sÛ7Wnèÿ™“·«ÀÑÜ~䌕Ùà(æ–ÀQ–ˆèÁ{€^yùð $e‰§¬*Çç ƒ¼8ýÁAÚh—'Ľxƒ{ˆ'[¢³-ZMÇRxU¬Â²2:bDø¤Ü('‡È‰—z<…Ó@òÑA^<ºÚèèGÑ%^Bä ቮðI‡ö{¿ªf¤Â~¡ç<ç9ÝF€/·¸Å-’½s£—1çÅÇf™ö0 Ù×%Ÿá4r¯³™|—>úÑîXM-7zŸ²Ó§¯ÙÌÑÍÜ>"Ù·TÁ×PBsìŒìÔðÚ`çTŸ'¿e|ä#;lòžÎùaÚäK^:îh,Ñ¡òr` »Ñd€’½Â Fèsî:罨xÁÐ14C*ç|¹iêÜëv®\ß_Žl<[îk`aÿ%¤}ó2¤Óë+ˆ6q5›ñ /ìÀ3â|ål®_Ž8*_øÈR\K!u䳸ô]öT’7ç¾´ÿoÊùYÚ~ñÛÞö¶Ý@€g?ûÙÝ(¾kÈlR÷yÏ‹ìS:”¶Miß&ÿ_êœ Í•K½»šý˜½Úüg«öÊìž0Wn•ÆG›ìcäþcY¤)3:BÎ'_]Û„\·îßÌdzò@‹„Á33Qfæœûg Ù?Iúofe‡ôý2³*ys£þÿæØHf®¾ðë^a¾ÿÇ—Hù]tѤU@œ½x„ Úzföɾ€Îƒ.¶p° ÌL&çŠÍŒos›Ût"®@”/Å¢¹íë„gþxV?ýéOïöñ£Â‰Aû™u $óŒ×n}ç `å}b,p4÷ù·-¿Œýÿ´mÎu;·}s娹 pD×CúÐ…J÷M«BBsûg‘NynÎíG–õm;~ÌÀÑm{à À›Ap G¤…H>ü"G?]NøñEŒBZ]ÁA`©¯våByÊð#adºŒ«ÔÅÆ!¾}Ž%¿Æ’F¨(†¥2àŒ¼ÚFé€3Œ½~{ã º"+Lƒ”Ǯԡ¡©¿vå‘úÄaÂROtØà!u&M(ÀÕËö“ã~ݰídiÍa¬¿ßÛÛëŠs£—˜óâc*¶)Ù¡þƒÓň2SĨŒ‘±r_£¡Í*ÍŒzÒ“žµf6̱s¡hBäÚbç—;«ÙG€¿|Y­ß>³_tœó²VîE1ç¾´ÿoêùÙoÓa¦­«ÃK”¯€fïš’Jû6ùÿR甎ld„såJ»·Õ˜/+Åîô'æÊEÏP88’•ç¥ük¾ækº¯E-ã›°anIfõïùfR{Éí¤dêý³ŽÊ:Ëø2àµä?êÿo¬]}¾¹v†2«':ÍüÍ€Oò Šdï¬ä•¡ål™é%ß,¥W½êU%K:_úLαt=4·}‘Ÿºç>ãÏX|hg™ûmy®ÏŽæ>ÿ¶å—±ÿ_Ú?õºÓ>ƒmž(Qsåbç¦a •º|X_84·ŸyáÔçæœ~dYß¶ã;Á)¸ˆÃ¤;H^É£L6H`Ÿ§Lš®è#'Ž7ØJô&-$‹”EV~xRVb,tGV¶yùkIEc)ÊÉŠ'²UÏÀ42!9ñ‰O\ÌŽH:{6·6#®<‡Íî3úëóÌ%y° 0™>ͽnçÊ•õ³'#äe~¾~Ræ%îEß4võ—dæð…N#ã%Mõ‹=ѲôXÈê“óÁyÁ·îgþ‡¹rt› ã^™å²òì‹âÿô¹r´³ü’ÈÜûÒ6þ¿©ç§6y©â/Kš3ªÞ¯××;ù›—Ñ”öÍýÿR·P¾tˆ¦ÌN™+—zw14£ÀÒ•1Tö'æÊ­ª'û[Ø:¿™ÁYVš<¡Yú>ú6;¾ì²ËÊâÙñ[ÝêVÝ=ˆ¼ŒÜGl¢)÷ÏUÀÑW\Ñ-Û6»;}¹¡ú¦äõÿ7Ŷ’w®^>Ír_¢×¿þõ]¹}×VQ¹i2àIÞ'Û6è?øÂè9/-O2s­¤¹í+uÌ›Qí뵃õóKÒ×pñ¡÷ 3w\ÿ‰ÓÊõZ¾K˜½¥ÏàzÌ,î9Ï¿mùeìÿW¶}ÊuKn]û²Ÿ‘kðš+ùMBçj¾¢YêéÀb“~VôÎynNíG¦®Ã8ºcÛ¦` °˜F@i®t@y^¶†”£’‡œ4Â[ê§ ®A‡•ü‘mtˆ§ülI¹P±W"»–JuÌ©4FÅ1B˜¤qÒÉø|í(zñ‰kl€ òPøÄ Ñ%ž»"/¹Ì.j³ºòÔ[ñMŽ(CfÙLØÔY/\ÛêTìk¯¿Õ×>QvM‘3šlß.àÑ22Â2 <¦yÙXvîu;Wn™ícòùÈ™±Ž³½ÜoÖÝkæøeŒ=ÛâÑQ¤i›6é(§C¼­:úz¶ñÿM=?û6fÚÒšÞð†ÝsÉ@Ã[Þò–îzÚ÷c™cÛwÿß2›kþ¹ë›Üä&Ýìn¡¥¬@*÷ ÇÐ’¥¾'ÆÞ?Kàè¾÷½ïbYœµUÏŸ~}5}ì;tÞyçu_ôu_òÌÂy¦[N;æ~O‡™¦–E¾æ5¯ÜÃèš›î9yë[ߺûB ÐÊÀŒå?ž»JfayZ] ÿâÜ^¶·×&mØÆóojýSÿ¿è{݆_8Ô>ÏAÛ8–Íîž+WÖ}®Æw¡yÌÀ‘¥jc¼ˆ¼ (䯿ª=` 0 X„0 td*"¼äK /y¬"2xÅ剗à)‡sHG^ˆ”‹ QìØO-ù ó’âÙ`ÊÌTÊ(ñ8HZ< L=`˜ü4€Þ8BôIC¢?ù%.?ºåeÊ!^2‘k£?9åøcϤ¥j­\¥êêêêêêêêsÀ%pdVå°õh~mBõÀ‰ô€™möˆ\"›Emù,2#ÒL4W®®?GêŽàð ‡8œ‰m„0q„è¦&|’Wê¡üèŠL›ug‰-ð qõ„"C‡òØÇ‘tøÚ¬åT*^ε_R6’r²òc¢+|Òò¥g°tøå3X>†7re¨ÞÈ,ÂÂWêÂ/íPÔF»¼è ÀT#ž©T=P=P=P=P=P=P=p-ó@Ž®exmî9á`ÐÍo~óÆÞ²f½á oè@_3´mÂnãþÐýïÿÅrí¹rÑUãóÀŽGy4~pÆà‡%o@øƒd†R°a `òP€<Á/’¿ÏµoYz‚‹ˆ—ø ;ƒµ´ÑŽ¢/é¥a [ÊP ñ¦ò§Aâ!q<Â83ÀžÈ‰Gä…’Æ–åâÊ£ðÓç—N]Êé"Þ~ÿáïþÛVR©z z z z z z z z`·=Be}Ù6k9EérŽkJ›† 52Oƒ…á‰QIãe¤0`M]Ì  >Do(å²®è ¯2:„‘i£]ž°’Èà9xGÁU©z z z z z z z zàœ÷€ÍÞmÈ‹†¾ÚuÎ; 6°zà{À‡!ò± ©ÛDÿÍo~s÷áUÍš+·Jg-Û®v8 ÞPh“8€„Á‹„9àøà ò"+®,Gíh¨\~ØF@¥€?ôÒ‘P~t’I½ò¥‘<„oE` sxc”t*Oyi¨ri”xÊå‘Ñø4ÈÓš£"Þ€AmQ§Gš\lÇ}mtaOÊä‰;b/Rl«ÀQëŒJÕÕÕÕÕÕÕÕÕÕÕÕÕÇíŽúÀ `ñòÓ¤,rmt!̤'Ÿ  ŸtꔤƒtWÿIJáMœ®Èa‡‡¤ŽÔ'%Åð•LWâÍ‘Šc”0 v8 €Ãà$NÞèh£‹YHòb›ºèIÝô ù”K“ƒè’‹üÉO½Ñ¯.UkP©z z z z z z z z z z z z zà¸=pÌÀѯn? ÃL#˜8‚w ù°…”ÃR†¿ÌW›:`Žä#úƒaH‹Ggð õ…/¸‹0Gʤå“K½¥nùiO]N„¦RdÒ€€DôÈKyŒNº¬GÃÓ<ÒByHÇfVž€Hidö6"K¯C<|ežü”µÑÅr5<¨Gû~¨¿ÕÕÕÕÕÕÕÕÕÕÕÕÕÇêcŽîØ6~\" ‘¯å!¡fáH<|p„)o£ ¹ÔC>!$+ŽɃƒD™”á‹m´‹ •G_0–ä G‘ŠÆR€•"841Pºä ¼4ªä‘ÂØ“|iq$ä Ì"R¦^ÀŠ]±G"“ryÒd…(a]ª¶ïú[=P=P=P=P=P=P=P=P=P=P=P=P=p¬ØàŽ;ÚÀ#¤ÜÈG™U$?À²ÈÁ.€MÁ4èÀ‡¢CŸ#qzƒDŸtd‚¡¤Nað¡/™è”§~!J¸ŸZò˨±‡á'§‚ / RÆ8ü1(Æ&°eéÈ£!Bz£»´/€Pô¶l ÐJ^€¢8RZð?°yß÷}ßæª«®j^øÂ®åÃÜ\мû»¿{XáW\Ñüáþá"}R#7»ÙÍš;ßyÿK¥¿þë¿Þü×ý×ImJ3ôÿØÆœ0Ã]#çŸ~w.ùþùŸÿ¹¹òÊ+›×½îuÍÏüÌÏ,mÍ\¹¥ w À³á]Þå]šw{·wknpƒ4ÿøÿØüÓ?ýSóÿþßú~ÝÛ½ÝÛ57¾ñ››Þô¦Í[Þò–æþášÿû¿~—l¹£&œ´ûµÿÛyr£ݨ¹îu¯Ûýçÿú¯ÿÚ=»vÍÅ›œ×é/¹þå_þe'Û·kþžjϺçß&ÿßT[*õ@ßÇ YªæAêèƒFÁ0<Â`øQÀùÑUê£L"{zàÂ8^²©C:zÛèB>²Ê£+uËC©k?µæ7Bkغb¼Ž)”ГF%Ý [ÖŽ? t(3~È¢€:~eÑ>i€‘0öH£ð“>aòÉ$?{(Uà¨uJ¥ÃõÀ­nu«æ+¿ò+›øˆhÞñßqQ™% Å÷}ß÷5ÿýßYúº(>¶È{½×{5_ú¥_Ú¼ó;¿só¿ÿû¿Í7|Ã7ì”}‡á˜où–oi>þã?¾SýQõQ£^Ø0ÉýÄOüDsÞyçeæ+^ñŠæ+¾â+ÎÊ?iü¤Ýès?÷s›×¿þõ'­ {‡þ¿Eaš>ó3?³yèC:¨ÿoþæoåC4WnH×qçy)»ÍmnÓ|ìÇ~lsŸûܧ¹á oxÀ$/Ì¿ñ¿Ñ<ö±mÞö63ãá“?ù“;?¾Ã;¼Ã¢Ð³ðöä'?ùÀ}Ì}ýa{Ø‚o]ä¥/}ióS?õS_ò%_Ò±?ýéOo^óš×œ%z¯{Ý«k‡‚ßú­ßjžÿüçŸÅ3'Ã3ó³?û³E—Ù2ȼ"ó¤Ü¯=ò‡4ó1Ó=›ûMú“?ù“æ OxBsÙe—õ‹Ž4½éy @}Ä#ÑÜõ®wmÞéÞiaûŸýÙŸ5?ð?мüå« Œí¿Ì½‘¡çߦÿß9à–Ú„ñÀ1GYªÆÀø‚}xa+ÓÉ@|$ùI a™ULjLôl"œƒ ÃJZ^ù‰§\:¤~úà!e~ÊÏ )K”3°TœŠRFW –—r28¢.§Ä…á£# ‰ºÈ;ÄsHgF¹ðÉ+íêóp"“xÝã¨uF¥ÃóÀ‡}؇5OzÒ“šë]ïzøòð]gèC>äChô×ý×ÍWõW7ÿ÷x†ŒÐlôò ¾à š/ú¢/ZØFÌ‹ÍüÇŒÐ0å1yL²üùŸÿy#~\4ÔcËÜã÷¸æv·»ÝBü·¸E¯ÀÑÂ%;úÿJãvåü,mÚfü8Ú÷ÑýÑÍŸøÄE3¼ðz)tÜáwhÞøÆ76]tÑ¢<‘¹r‘ßµð&7¹IóK¿ôKkͤ}á~aóÖ·¾õï7}Ó75÷¾÷½ä• Àp.³>ôC?´yÊSžR²¬Œ¿èE/jùÈG6÷¸Ç=÷4tæÌ™³lÖ¹ÿ±û±®ØèyÃÞÐ¥7ýùŒÏøŒ¥`—s÷/xÁ¦Utm; ÷ë÷|Ï÷l~ögvm{ ú¼úÕ¯^ËwX ›œ×fÛýðÿpsë[ßz©y_÷u_×üîïþîYåSû/s¯‡³*>ÁCÏ¿Mþ¿ìŠjúz`€£FÁA>Á+`òñ‡È£,Gí°Œ²,˜žItÓ‹äãAòÔA?œCŠ òPê‹=ѽÑÞNhÝÏþ›ê:®kÊS£Ä5BÅ1<¡²¾îž°eYÈn4LÊS‡ü~~åq"PˆòãˆÈG§4*ó#—ð¥û,õ·z`û]rÉ%â_ù•_i¾ã;¾cÀ’>à> ¹øâ‹#_¦šÒ'}Ò±Íì1eÙKÎiŸ822~Ë[Þ²ùË¿üËæó>ïóúUYz¨5¦ò1r¿ù›¿Ù…8ãÑ£åY÷ÿíÊùyX^9êö™%cycf¸ÿýê¯þêæáé/Ñš+w@ñŽ%ò‚fæé³Ÿýìæe/{Y7x`)(ìx@£Ýèë¿þë›ßþíß^´ |éµ4íGôG›×¾öµÍ]îr—t{û·ßïþ˜¡xBÀ‘§=íi ë"¿ð ¿Ð<þñ_ ™bV’¶ m9±Í‹ƒçOèNwºSsÛÛÞ¶Kn»®Ô±«÷ë÷x÷h~îç~®›…æ¿ñ¿š‰fFó×~í×6ïÿþïß5Á2G3Ѧ]GÎ=¯ ¤ýøÿx·dœ­“o~ó››Oû´Okîw¿û-šÐŸé:§ÿ2÷zXqD†žsÿ¿sÀµ ;æcŽ,U Ngîn` BgyÊ|¿´CÉwx( "ŠÎè%~qù¨Ô“òR6õísïÛ@6¶¨?ráU¶–bÀZÆ–!àMxÓ€ë¶*MÅŒIåò”‡’NyYâäÕ…W\¾´\âm´‹g&QiOÊä)LÒt9¢?uÔ¥j­S*mß:AÏxÆ37>Ë Œuä,]ÓY™™ôœçøDò…øxtÑ'_E^œŽà/xÑ)^ò¶ÉñDp*1”á©T<ÆÐ%•<cåç_Y:ñ'0Ž(ë xD'R†?2òñĶ” •áI{ì¹Y÷GYw°žÕΞC–$?øƒ?¸ùÁüÁæþçš{ÞóžÝ2l7BGö:zêSŸÚm†úýßÿýÝ&Ãx‡ö³XRÍV²ËŽ——9¶Üÿþ÷_¼|ì:p¤S9ÊMqÎPjŒü¹M£¹×Ü&~!‹†üº 8Z%7ÆŸsyæúhÝÿwÔÀÊÜö—rS|qÔí»ýíoßüÐýPg.Pĵ1†æÊѽ«<–íÙÓùŸÌX =úÑîž+Òf—˜u2ëè;¿ó;»$²cèà…ÜóÀfÄ÷½ï}»gѲ¥jfˆ~ù—y§ÚòÂÏùœÏ9ô Œ÷ö®ÝÀѪÿѹâœAfçøOv‘–×ßüÍßÜØ+ õï ÎMÀ€ é+ÜÇ}\óŸÿùŸ€£mö_–][þ™rÏ^öŒ¢#æ¯{þ…¯ —ý%϶âsÚ¤îe>Ú–]UÏÑxà˜£;9•ÚHKgʾ4 <áOY˲'(‹ÏÏqýöð'„R.Ÿ=ø„Éo£]º Ã~eG¶TÍ—`²oƒ&û·ÛÜñŽwì^ÎuŠ :öz0-݈¢¯oýÞïý^óû¿ÿûlíxl²Œ^üâ7ö0Ñ­£2›ì•3ÕÎã»vÝÎoû¶okîv·»uKžõ¬guþtêòŸ õ¨Gu³}l(ªÃn/ _:Jò\zé¥Ý ƒºËQë]Ž,ÃøÔOýÔîkL–lXöbÔþ•¯|e7cËë–‘ŽˆÿÅËÕyç×]S–ЏCC›cÏ•£spdÉ ý¦Øâ\7k©›\‡6§UÏT¿øŠÜ§ú§í@Nu\xá…Ý}Ê[Ú,u–>íGõWu@Î×-Gr/*床®þ1r®ãÿáþáÍ©S§º¯ùŠ”e€S÷G÷ÉЦ×ûÜÿ凞´wJèÇÿ¸··×¼Ïû¼O÷reO2K=,_²‡Ð2€fnû¦þi¯ñ™¥‚€®Ù14WnŒî]å±ü(×çW}ÕWØصcFrûCfý|Â'|B—´©õýÑ¥hièðíßþíÝý“~†ýøÐpdiÐüÈtå> ðYŸõYGT8ÇmŽú3Žì'hãh}’ïùžïém:ÆâÇþKf¯Yæeyà͹_é9Ê<}  `Å^XžÙÇÕ7[×î¡óÚþDú´yáï÷9J€#ú³|ó0ú/«®‡Ô¿i¨Ïá:uÞzÞ{þÙ›ÊýZÆõg¢}¾|¹ÔGTð›ah@(l¯'÷|{‘é ~Ý_û{RÎ}þ µqèÿâ›Çúî#®[ms3kòýÞïýº~ˆþ±2@ahnÿ%òÂe~ÑQWü>4€Vê©ñÃñÀ1Gwi[spÀ®jC0‘„p”tÂ7äÅÉÏÀ#-ß!_¨!J]xá±#õ•|Ñ•<¾¤—FIGÇ~î’ß-)>+¿ Òi Ðy©¼.âÉœÄ@²1}9Êt›½à'}‰Ó…J9qùt¢ðÈgRÚq ò‚û©ÜïXà…Ðtâ!Rök¿ök]'À ˜eN¨Ü×ÀÃÅ&ÆÈCÎC×ÃwlG±ø™jçquNvÝNK ìU¡äØÛÛë¼mŸŠ—¼ä%8(ùà¡è 8¾ “ôÀ_s¤Y‡9GL{`£|­Çƒ93ʆú|}ù {ëï¿ë»¾«ÛªäKÜ ¯‹/¾¸[˜¼„^°ŸùÌgîãa8š+S_D<,MÙ¢'á÷~ï÷6?ù“?™dÎñK ,Í0°—DŸthÍ@$¡±r–Ø‹¢œ AÞ^^vm„ºŒG¥-›\ïSþ?>ßäü\ÖžÃÊ÷YîŸþéŸ^éK3²?ȦןvLýÿ|-,ÿ¥=j<ÇA‘?þã?îâùq/ô±4W.ºNrXžï^¨¼H{‰ ñM¹7”å–:ûŸóµ:_;4+hÌKëÛ}t |„úÀ‘¾†s._3SDå(Èstpô =¨yàØ™á•ûUi—ý˜¼h_~ùåK¿Ö6õ~]ê?ޏÿ—oT.ãê2vègÙy}£ݨ[ÉT#NC€•MþÐs0e›ö_V]©cnhØWãÌ._E®y€ù™3g:6×·¯ê…ÜØ©¯à2Ó]^hÊóo]~Ùÿ—ºæ„²¿%Úl33öûÌÑ^Shl?¤ß‰Þmú%:k¸]3pd vËÐB‰Ã2úy@8C°2ða06ÚáIã‡S H¤ÌËRÒÑ^üHèÀâäa&äÕO7""¿ŸZòæ%ŲU*•—˜IãðÅ8a‘üèQ–xêI#•©CˆÊò¤c (qÎABº…ÑÕF»4^Gx8bLÈMMGÅl”¬ßöâ ]$\yå•‹QG# f!„2*ãåÁæËȾn°s©|0”:–Ù¹îASêØf|×í!£3Ybà?2r†t´½8(:}útÚLÛÞHÇM›v¼–Ù_v—ñ”ùöçȘ¼Ô%ÖñG i/6“uÝ„øóOÿôO“ìfãè|:oN—ÙFön~ó›7ù‘¹è|•À‘Y4ÃÙŒ”otZÝ[ÐÏÿüÏwŸzŸ"— vÉ!f3˜² »—aŸW¾âŠ+ºMÒù¸± 8Ú×´ÿ»î¾4õÿó2l_—±TžŸce¶É÷ßø‹¾xá _ØÍXî8×| ÀC›\äçüåì:VÀpæÊ­ÒÊ\®¾F€ð$JÊ}Ž’—ÐueÄÞuµŽl6msmdtŸùïÿֿݧ8ò,3«'àßQƒ{{8ÒOôŸ¹W™­éÿÈ.׺>b^®óîB¸ê¼ÖŽ †ørœÙoèú׿~·ÔÒ œsÒ³8à 0³ûíÛ¤ÿ²îzè×5%mà¥oôCÌ2Û[ûlØm@ٵߎVÕCgúßùæáxŸúü[ÕŸ_õÿu•Íü)£rMç²ÕåL}€u–áN釔ýfnÓ/3›]ÅFxà˜£;·&€-0Žv\p‰_§,"yd’Ž.ù䤑8ÂaÊå'.tcÄ&8‰¼ØD>˜ þè—=mt=K¥dJ#\V¼Ǻ¦q©C}#ŸC^½B¤Œ>iW—¼8©vËÖ„(e¥¼¸|aòé¤#ºÙtd›cf¥èˆ]uÕUÝ´ý î^ž|R5\Þõ®wí0nš6Q峤ÙTÐÈ"ÞU7ÿÈ. §Ú¹I]Ël“¿ëvŽøF§?K ÌèxÓ›ÞÔ5φÙ:{ŽtþΜ9sÖ^c|q<›t¼VÙ£C¤S˜Ù4FCCËV¼{è#*/Êè²Ë.뮑rJ¶Y3®ä+ΪA@IDATm®ŸŒ¶ëH™ù…¼L™ V.£*§Â—ÀÑ\¹®¢«ÆG:•–T—Ë)ÊOS—›åÎõKÙñн6g7%ÞÌ-ŸGgÿÊÒ…¢)r–fß zŒšfY ‘eÀj6@¥Û óyç7 8ZuÿÌ}iêÿçEeîùÉþ£&À° -suNñ» ´Éõ7÷ÿs>纵ô `$€2_üêŒkY"5W.ºNbÈ7–6ßô¦7íÌ/_¢‡Úã™áÙѧòkjý²2íä(ºWÑÃÞP²XªV¸šW‚LeùaÄ÷ö*p¤ß—|}—}Œ~Ùq¦××å¬"3dzN;ÇÐã÷¸nö1À•I—QüÌí¿Œ¹Šj&GË%¦žQfÈYR\’“[ßúÖƒÎw×xÈWæ,I|þ󟟬®?,Äg ¾í.2ónêó/ÏÍ…ò«#ëþ¿>ÿ”t EÎ~]f%³§üH€rƒ„î9Sú!eÿ…Žmù…®J‡çcŽì_G€ x„¦ C%_ˆ¢Lžø~‡ëš|yä‚›™ 6qÍ(Í~y[ÔQxÈÄzƒ…ÈWL¥Nqùd8R_òðJ§¬.§-ç8XR64%i¬4Ã]©y qüHÃcx]:C^t—6Ó¥L˜|r%¯23‘ØþØ!t¤üåmüH¨t˜}PÈ‹myóôâj´!³StŒ öG3ƒ%£m|Ò|ªË4‡íÔ]·päaeôÜþ(€D¹¹ * ’Ž a,8l?.Ó?·ãµLß²ü){¬˜Ýa&²LÓrÍ’úÀK^¤í¯ÕDî}ï{ŸµŒa8š+WÚ$>8Ò¶|É>W]tÑâþ@—ÉtͶ²).šë—~ÇËì&@fîItëÀµÞ?/¨Säè0› •ûÔ°çÿ(i,p´îþé¾´ÿoÊùY¶ã¨â@{= /\f+N¹'Oiß&ÿ_üa`#³ NŸ>8+0WQñ˜QzŸûܧc7K)dæ¡A˜‡=ìaÝ~H€(}‡m<ÿÔ1æÿ‹-sÂò݇|&™~~tú~|`ÐaJ?¤ì¿lË/sÚZe¦yà˜#3Žà°ŠtTa òr´ÑE>ånÊöC Éǯ,¸P(xDòRW[´àK}áQ†ä;è W†0’Rx…ˆ}dFåS(0$•ËÓT@Ê•¡Ä…dã RÞ¤þHu”ù£ä#!Ñ'Dö.â€Ô‘¯8"+ÛR&o=QKÐo£r«Èc½}Ùá+³NÌ0Ó(¤\ÇÜTÍ|Æ=K–Ì¢°tEÇÎ ›ÑžþùïÞÞ^·ÜK²ŽbÖÿ•iãÜúæÊMõçQÛ 8ò_êe*t›XÚ"˰̨Éî!:4ó&çÉQ…s;^SíûâjÎ#²Óy3Dà”ñ»=Ât8]ÈT÷rÓå.³ýŽæÊEg±À‘óCÇVr-G”ýB€H/sýRv¼Ì0ãÑ9[’—|/í(ÐT9£ ®¿3g®™±¬Ó<8sÿTß6þ¿±çgé³£Œ—³ÐÔË76%wÞû2Ö:šÒ¾Mþ¿Ø1š+—zw=4ÓÁfÓ§NêL5˜`ßše  —9×Jf›¹nõ ìu*gn$¯ 3à$ßﳄwp¤Üýá(A ýÜû›c_Ûö8ò<П°|µ5´+ýˆ±ç5ÛÍFEÎ)ƒ!@£\™Aç|7Ëù8L–­uÅÏœþËØë¡¨fRTÞ}¹^]Wé—.STG9ßýïù`N¹WW˜GÛxþýÿ–Ù?&?v㵑¿-9ú÷½rIöéÓûSû!é¿lÃ/cÚUy6÷ÀG°˜Ð{ʈÃò‡P&,ãd“/ ÕF;¾R¿r¸„^qº`ÑÙF»º¥a%å¬$eˆ<’íÛ Ý×Ùf-'c) Py !ËYŒ¦+Š^|âeºMvò”EŸxé¼i(þèâCœ…"'$ã@By±Aè Þ6ÚåM^ªÖßgˆ¢!ÒÑÍòå%ÐÑÿÔ輑ËtОð„îî¾°bÿ Šš‚Œ,–€NåˆáQÙÙÑþÌ­o®ÜTµÙãÈcß"än”0Ë䙕d/_Ëp¾˜ S.ÝÁs4§ã5Çα/®ål{âdW¿NË ì!€ŒÞ{y²ôÊ×»P: ]¢øŽæÊj»èXà¨üZI_ÇPÚ9ccÔÌRšê—²ã奫¿Q±:½¤íííuÕš+ç~–}”èôõŸ>ŽÆÜ?éÝÆÿ7öüì·ã¨Ò–ž§³ü¯¬×æÓO{ÚÓìõU–‹Oiß&ÿ_ê Í•K½»ÅmhkO'dÿ»‹/¾ø¬—§² îk¾ÆŠ„Ž, ,—Ë—]+–dÚŸå% à¾ìk¬%pä~`ãÚÌvTfvŠ6¹oTàhØËå6{÷•ûaKnî”óÚ¾M>\ìudFIžñyŽ++g!õ7qWšÚ™r=¤Ž©¡—` >Ò?4Ch•ÀÑÝï~÷n°¹Žþ?{÷o[Wô"ÍʈÐÒÄÅÔ„¿ZRôj…™ÙÅBKŒ|#²HÒ2I-IѲ0»ÚE*ÈLP»g™eei"Æ¿,#¯ˆ”RŠæ DûÏïÜïoqÖY{ïµÖÞï9çñ|>sg<÷ñÌû³æÓë{^Ù`*]õüwÌú»lñ‹›Œã¹va|µìÞ{o-sýrÕ¼ìÆ6û÷_î‚‘‡bDuú’ð,Ñt[Ü¡SqH¢¾ )þŒ€§Ž¡e—~]´ÁÿÚü×BÚÒáéÁ‚Tp‡–-4|ƒÓ´ <útAt}¶;ðúxÙÎ/[hôÉ¡kÑÈWdª8µÖ§‘ðw—dØxÁcÀ nߌÿ£ Oí>&>:üKâß’‹ÀÉ¡O´;A:iø7Qå]¡¡ùüKé_gO°<÷¹Ï]?ÙÍî튳1œêïT½cóy»ã¬päŸ@ŸJ}ìc[ë?Ã&!5áñŸñŸ±-Žô;{áujŒ‡Þ¸z}ÓZÀcÚNwýŽ_T„ðµ:ûJó¹?éšþX é•ÂSõ²Y{háÈE ›4 ˆ8~M.[µþ©t1¥yj^Æ×Ì©° ãS&û G‡ê9ž)dÿ¶zºj‹uíâÑEdpÊþ~ëïÐí³8ïD+·¶{EêŠ cþl°}dÄ?t|žl»ÊúËß© Sõò{·¶Ö?†ÌÛ.{ÒˆŒë…&`®_Rkî<7{žªìewnUiù1v7æç=…™ìX8r<ðº›yçn pådßW̲q]­b¹¢è Œµ³üòÄQÇ—ñIôk=^'·´~ðƒ×)ئÀSŸúÔ[^¾]±»]ûC­ˆ3sl×¶ïæÑºçž…CóàTˆÙ×±×/Ç컾íû­yŠl¿—ÁX8êºDn{âh¼—¨Sáè*ç¿c×ße㸈_ÜdúàÏ®¼Wr›¿âÒ©×/WÉËn\³ÿfà.()ƨ5¨oÀÕ ÔZz% à“¥C¦>ÜÁY ²¡ ’#“_µ } Ù³ü‚,À/>½èäÐ@>дâ}ãï… ïeîÒÓ×hÁ-è-2L†|¾ ˜Nxöº ±¡àÃ?º>¼"}4Kò ºtzäôüâêÍ*v=?§Üø¸8ì°(z¿×IÖÉvs†Œþ޼CñSâ<ÔöuÊÝíq*Xør†§Àüƒìߥ'>ñ‰ë›57Ï.t=¢í5ÿþzbänc/¼N»׋.äÙöd zàsÝç½.:ÞXxíËcí}ÁŽnÿÚÁƒ±(…ÖÚ©zÙ­=ôFdœô¼'²Y{•¼œzáuŠžtÿÕ2‡pƒÉÆ.Œ¤^Gáè:Öß¡ÛçîXîTßÇ|qϼ×X‚óæN;t|l]eýÇ© Sõò{·¶ãñÊS"÷Þ{ïö&ù¼˜Çɾ§.ü‰ä¸×ú·¯Eû¾4þE_áÚ-9WU°vüç½brÞN¥»ÆQ0Ïþó×BI¶ÆWPLÈï ¬œ_ü⯤‹Î7‡¯GÛw >Îwçs®zÄ~ìv=_ÆxÃ_óš×lIþ\ë3ö ˆÍw´¸9æúåØýa×ס}_ƒmbïó º»¶®R8ºÊùïØõ·÷1ýË GÖõܱƟN®ŸO¹q¼»J^Ž×”½zî’ÂQ G½"V¡‡ŒÚC¤dÕÐÕ+<¼ðx»->9 ôáìWYЕ¦%“]²Õ=àõ‹%› ëb x($Ë1‡£36 "9ý 7øô=øhÇD ~6èóµ ÛBd >„èU"“­äôƒž†:úUµ ÛžRèxÞóžwÓ òø…¢ñÄX,×ñÓ)qæÿv¶w{œå7ѰùxÜÈíN|;suНqû:ï_ Sìîê˜oÈ—–Î+&Œòæè3õæ8’Û]01¨×6Aþ7Á3èߪµ³ü¸ñh|/¢W8:U/Ûµ‡ÞˆŒs,ú$§æåÔ ¯SõÌÅä‰ÐÅßÚY~Ì]á¢Ú«WÀk3ö›à”ýý:Öß1Ûg±Þ ­<ÚþŸñŒg¬áxJõÞ{ï½%´cÆw•õ—ãS @§êå÷nl{<™lOƒù:â!7ún˜ÝØOüÄO\?å½;¾þ¸@7w™gc¡ÜN}ñ.þn»¯pDf,ôêÛ–<½t‚mÚ«qÀ ç8 ¯o)¾ÇŽñÕ»‡?üáë“™?þo,ùĸɖ=©ã ’\6‡Îš¬kþ9u»_!’bm=0>¯ËÚ_€k,sîƒc®_ŽÝöù;„¦8æZÀú]Ÿ\¤{•ÂÑ©ç¿S×ßE㸈wYáÈŸ„î…€sP…ãS¯CNÍËEc˜¼û'w¸pdÒ8uÅ› Aê– :õµ]ý¢e·?œÈ“ˆ…>_ÕA,ò· +_‹†GhÓY ÷ýðvÀ9ÓAÞ'wŸøþ†Â¡`‚+°œ¹òG+ÐÝö+ÎÖÄÕÚ’ÁVºÚ„_\ù0Ðü/èÊOWѧ„iG]2Ùðwk>£‰ T¸º« Gæ»ibeAÿ.Žÿžá½á oؘéªpÊ ÚU}ž¢·ÇéâÆ?³nàÌqÔDç§ŒõNèsáu•øÆBÏîÅþ®]ew#,·^¿/ŒÿøÇ¯sºÐó:š1Õ›d¶OÂûj§À€²LÄ9N*Š^áèT=6F8´pd›QêUƒÝÔ³éÔ\>» NÍË©^§êy­À…'Õu3çI€Ç<æ1+ÏÏøýSö÷ëXÇlŸâ¼`{1á§‹éñC Å0þÃíƒ }Ö:¾ö˜ñ]eýåóÔЩzù½Û±ðâµt4 €¾* ök¼ŽÑDÙö×ñ‰#ó½¹I‡œ›Î+Ñ7Çâsžóè æië•ÆhǶΙŽÛ»à†ûe/{ÙÆv ¼Âä•ïÀùäûÀëá=!ÇÞ~án_­ÃÿŽïøŽõu=ø.z¼ÞÕ»¿û '`ß+§èÏ|æ3·_¹{Õ«^µ-¢áÝN8u»¿Ú(^ço_ Æ'Ê.ß1×/ÇîÅsJëIâ>ìá©ZSœWübÿ*…£Sϧ®¿Ý|x#ÂÓ‘ö×/þâ/>÷¸pQáȈÍYéZ øŠž¯éS¯CNÍËêtþÜÖ Ü…#u uWKÐWÜh£ ž¾ÚD }#õ 4¼^qË=8Ùj+Ù­¯¥ ðÒEO&_dȳ.܉$}ôK£C!ã ]xýx5/ÀYK ÛÉf¯~²Ž’¬¶ +ž¾6\¢Y ÿhìk£/誃~W¿ªf²Lsæué3œþ¹w±Ù?øþ‰ñïUá”´«ú’î"KåMÁ?ôn€mÞä¿nš½ÒéŸóöE¥æóÏ«yD‚—¿üåë?ð.DùÈGFÞ¶ŽNÕÛºéFÄEð8¯'»Jg|êHQÌEÓ+^ñŠuÂtOE=íiO[‹gnûJÈ©y9õÂëT½ñÆÆx½Šëû€Izw¡9•ÐOÙ߯cý³}îÆ÷=EçßSí»¡ö´}Ȥã.Ú/ì×`üÓaŒë˜ñ]eýåóÔЩzù½?6W›uvÞ|-bo;&X§ž( <5é8èá|Ò| û^‰ñº»Â ðQ€¯üʯÌÔÞö¢Â‘s‡ ¹{ÊÓ“.>@0ž/ö½€hþŸwí£•Ç7ÝŽÀ±Ô¹aüã`­mÿá _¸‚'P^ùʳÿ}¯zýrìþ° âĹú%/yÉ:uøÓļDŠ»® mãÎ}¶o×"»ëã=dŽ£Sϧ®¿ÝtøƒÎ8‚}¯â…#Ç’×¾öµë“àÆ«8¸†óâàÔëSó’ßÙÞ¾ ÜáÂÑS–‘ªµ5‹Z…>€W„AÓ·T"F6õY2ñÑàêlhÁ(Ÿ¾šFþ࣠±Ä×òA¯vAW )”„Kœ2œczáÚÐàô£)øŒƒZº+Œúpƒ­°C hþ.žŽD„ÓIÿÆ‹Ç+[Ú»ºpô°‡=lýŠÚç »^'ÿHÿ ªê_Æ‹¯q⽫ڽnýBœNð !.L€‹isølò“žô¤õ2Ndûž¸îœgO¢gÏ“é¶;Oü\øÊ–›_s³œ.+¬¹Añ´J¢}: 5žÔ«H'ǾØâ•¸}`ž¯2ÜsÏ=+{¼@;Eo×G…£]ºËÇ £ø¾†ãkƒ—A…#r§äåÔ ¯SõÜÄ)æõeµÝñyúÅ…s9_E8e?u½q»}Žº÷7^áè2?ÝÌ3¾«¬¿b<µtª^~ïÆÖewÕë—c÷‡1–SpÇ\O ÷ÁŽól˜"ÀyÝõ÷ÿC G§žÿ®²þƱø#£'…Ð=ößø£ÈŠ…£[˜÷öýñqêuÈ!yQ€wÎãX÷…3›Û”;\8òªZmÅ›ŠBê#®–Ž~:Új#NöÆÉmé,NúºFŸ,¾Z|,.|:úlÕ:ð|޹𹃳]£9¼éõA¾Ø@o ¸¾¢NÉ>tºÉ×¢) éK ÛÙ€£'«±È%ÃÎm{Um¼È?äÝfA;Ày Á?1Š&†a¬œr8ꞇŸçy¶îOú%NëаuîŸÁ]ðX¿w¶+rìòoGß·âÆx’¿Èï¡7_dcäÉ‹§šz:"ž!“¼ºIÿYö”Ÿ÷¸Ç%º¶þ½ód]ðOŸÉ²ÇÏ–›#I±Â<^qðd“·]°µ>NÕý‹§›ª‘Þ×OFZ¸í=ÆÎÿž²ëõ¹•ëáT=6= #çãÍÉέO_‘ÿíozOÝ߯cý»}–»û»µ¿Ê—Wšw¿Ð˜o_ï”o9>Žß©ë/ßãÓ2»sï$³¯=UoŸ­»…æ‰ÇÒC`÷¸äfÙñaßë‡ìyÒÈ+»O4â5ÿ\!ÂÄÖ›WÛ8ïØÿèG?zã+–Íß2¾Šz‘í}›WÙ®Ù{Ìc³>ºû”˜s¢Òø>ù«^¿»?ðyUðDµíØŸÁ®Gp­oBpëÒõŠ'wìÿþ$î:¨y Ç{ O™9&˜÷«§¸O9ÿ]uý5–ñc œ =о»pQáHÚ•Þ®èš,ý«\‡\–—æAâSÁnÂÉÀ]P8R›P+¨UçhoY±³Z¼úrÅ!-º ÜÑë«QÀÙÃgŽžÏÝÂXg)5 øhŸ À~q±ÔOn!£áó¥Î8Ϩ–n…Ád+9}týðŠ4É£ 8üd£-¿úô+úɱ´£\Áhäg ~Û G‹¯ oÃðo½'^õ¨Gm\Üz¯ý[¾å[öÞ¬¿­¦É ‰*ý#çU8óv]4©¸œÊ'yòúœ'‹øðš˜BÉîÅÈy¹?Uï<{‡ÐåÃ?_¾`äÂË6#îËb>%/‡Äs]2.”Ҍ͘\(wA|]>ví\Çú;vûÜáþì{êÖqú£áû¿ÿû×ýÁö¡pèøîÄú;t okrn@=Aã‰M7C^óuÜT8ºì8q7çJaÌùÒ1Â\MÆ¥Ðîõ±CŽöÅ,òtȹדbÚcûØulÆh\n®­s¯©’›ÑÞÝŠwŒ1]ƒqz=ÞSbû wë‰Ë“UÎ…® ]¿8žú¤ÞEþ®ãüw‘ý}<Ûª'…íË^9=ï\4Ž>â#>b-’9‡yÝô¢ë¿}>¥íË ßMÂ~§Ÿ2Ë(O.YE¦bƒt8 ÿŠ›?333333333333·%cáèþü‚oƒñäž'1ý ·<}ýûÿï_Y¦–ð4ü„;“»¤p¤^¡Ö ¦ hŠ@-:µ O ‘ÚèÙÒÆÇlÐÕÆOfìWÃHoß9übK–Íp2üdsA/†Š/KÝà’ç,Z…•­±8³tW( |} ­ÐÞ¥D{*Aóå³ôȱ3yþ²,äã'?鉿%9| ó‰£% fffffffffffnWnwáH1èxÄÆÜ–ž,zÝë^·> åÉn“Ì_§þ¨ú¨ _ó¾]9z[õsޤ^A‘¦âÏ›\ ¡â ~2ê u‡hZP¡.ººD¶ë«oT! ÇÓªmÐMoA·P|ÕCèXŠG‹W, z9P:’Í©V²œóŠ0ä *²‚,!Z€–ýtÒ§+)Ùˆ¿¶t¸{ü“©¥›­Q·ÕÂ^íˆá¶MŽÍé„™™™™™™™™™™™·õ ܉‘W`/s+™Ä|ÂËÀ]P8RãP‹PkPOP´©@GK†¨OÇ’¼6;ñÒj¿vä~ÑÕ8**Uüá¿Z‰]»«‹Î È)"œ,ãpKŽê³3НÂã£Ñ3x D¯j– ½±pT?»lÀ+Á‹g” O.½l£ÏÂÑ’„ 33333333333·+Ï~ö³7÷q·ºóGó9ÞŸ`Þ%O=íiOÛëÆGG¾à ¾`ãÃîlî‚ÂQÅ!‰P·°¨-X*ÄD×t¸šCré©WTüÁNGlé’U«Ù"7º…-öAø:[xèdá—®ËȶpAiÇ*Æ F€g‹{úä£-èJÃËÙ’F^´âðzŸž*Ê>™ìG_H+Ÿ róU5™0303030303030303030303p›2`"tf·sòs”è#&ðö•Ý7¾ñë;nÓЧ›K2p‡ G°„WAF}CÝ BKÅ­ZB_U#_±‰û[€µ‹È ¾‚Nƒ!d-}ŸñÈ[Š^¡(:ž….Ÿó‰£% fffffî– ü»÷ïN å?ðOÒ›J333333wOîpáèÿ]2¡–¡VPÝÁÕôÕÐàŠCN^¿ZC¼d«•Tåµ›|¥“/õŒŠTÕ=ò™¿±O&l|õýÚ½? &N 8šVUÖàŸ¬–­2ÆA>=‹ÂNŽŸÎh+Zòd%þX8³°ŸÍY8Z’1af`f`f`f`fànÉÀ,Ý-kbÆ1303030303pû3p‡ Gᅩ¸Z†zÂy‹zƒ'Š\Q¦âQ…#vðê“a-@³¨[¤Oh÷ál¨gTˆªæB¿zK4-û|fkA/‡1Èˤ+²l6ª| Ä HŸNE›1Xrì–œQ·bšA¥¿ +$«mž#:ú–ˆèÇc/ûèúŠM¯X– 333333wI*úѱòwÉ0g333333{2p‡ G&ÇV€Q¯P3¨UW¨P´ kíAᆠ¨h£o‰W?©(´ «nýôÕ2ªiä“l|5Œ^}CÙ`?}týÑFvµñÈ]…¯3x«0cpx7Ê,ÝuàbH‡ @£ÇçÈËŸ"Ï(Cn”‡³EèÓ Ògƒ®¾˜_µ,ffffffî’ [:Vþ.æ cf`f`f`f`f`f`OîpáèÉ÷…¤n ¡8£¶W?¨¾QÑ&^Oÿ “INF?™l°ðè©ShºF¶WÂò3ÊÃÉ~ôóG?|A·5–dÐ.†…œ%߀ ?{x ™º+‹/XN~~âkw´ld¯ä,¤› EdZÈTÊ6[é¢ÁÙþúe™030303030303p—dàØBбòwÉ0g333333{2p‡ Gæ8R'°(âh`*ìèW@B‡£©E„“ÕOwAWé%«&ØR§ÈF2£ê„Ø·€tÓÓè )¢@Ö µúÑ ëã Žl€¦ß€¢+ì°G³€|á¥;•ÐÉW}²èõáh»}ôŠR³p´$cÂÌÀÌÀÌÀÌÀÌÀ±øy?ïçm>øƒ?xóMßôM›¯ÿúë;[:VþØqNù™™™™™™Û—;\82ÇQ…Ÿ G¯öÑëgújjÕDô£-èJ¯¯U—È®> ÎŽ:€“†N–>¨GO-xül/¤µ&’Ý‘Ž·2º—¹CTpƒmÅCc3ÚdÅ2`ÔagWÏ@ÈXðÈ€Ñ6ž¾ÂÈœ¿ÑG2Ù‡[ÈÑý÷Ër2|ȇ|Èæ§ý´Ÿ¶Õÿ7ÿæßl~à~`Û?yÇw|ÇÍ»¾ë»®j¯}ík7?ù“†:áÔ <òù„'z}8= ð]e“£—]rúÉ“‰Æ‡~uÝÆP-$ÅÃȆ> {P>r&PxÉ*ðZ¼dµ ÀkÑ’×–œôÐÈ–d|ÅЫfxè=1¤EK·8رèãEO^ Nþªš ü¯ýÚ¯Ýü”ŸÂÍü?ð6_÷u_W÷¨–½¿øÿâæ½ßû½×›„ýÐÝü†ßð6ïõ^ïµù‰Ÿø‰Í§}Ú§m~üÇÛ†o˜~ò“Ÿ¼ùèþèÍÿù?ÿgóŸñ›þá¾Á|ÆöåóðﺌüÑ?úG7¿òWþÊ5®_òK~ÉA7ˆ„÷éý­¿õ·6}ìcoã7|Ã7lžûÜçÞB äɸÁoý­¿uómßöm´!lãÝ·þ¶Ìûy ì§ý2¾}ë]ìïùžï¹Qøùµ¿ö×nÞéÞé¦4øá«¿ú«7ŸýÙŸ½yó›} öV`ã:ÏG·z¸™¢°õÄ'>qóQõQ›§<å)›7¼á ›/ù’/Ù|çw~çŠ÷w÷Í GöŽ-+d8S|f`f`f`f`f`fà6fà޼ª¦æ¡~Ü€WÈ[5 P}DMè-šbAzÉ.¤›|à³W]#½äè©iÔf¿8à@_죽ôŠqW‡Þ-Pnaì!äh´ s8ͱA$'¨Š2 ºÊÇ·Teq4 {dÅØ² +^,|ULbƒ\ýløâ<Oöè}ø•ž­ÉK^²yÜãÇÞ ÏxÆ36¯{ÝëêÕþÒ_úK7Ÿû¹Ÿ»êü¥¿ô—6/}éK×BÒû¼Ïû¬´_ñ+~Åæ‡~è‡n±ù›~ÓoÚ|Ò'}ÒJÿu¿î×m¾ç{¾ç™·E¾|Žyø¬Ïú¬µÈò_ÿëÝÀï컑<$–}zŸó9Ÿ³yüã¿Uä#¹â³p´MÉ]ƒì[cp×½}^¶?Œ¾ˆøe|ûÖûÏü™?só_ñ—¦ý»¾ë»6¿í·ý¶ÍþèÎóÑ^ Qê×ÿú_¿ñ' ü<8ôkhçé[:Vþ<¿“>30303030303pç3pŽªu¨m=Š5<}<8Zµ 4‹~ü]åôAúðQWmà‡Ó©nž 42´ê!d,êxðdô8 x,”ˆœ ¢`ØÒ'3šl~G96âÓÍfþ´ôÐA-Û§²‹—<š¥¢[F}üW,ËÉðÉŸüÉëSAxÚÓžvî?ÂÉìkò‡lþÁ?øë?Ío|ãW›o}ë[gáh_² —ÏQõoÿí¿½yÔ£µùÖoýÖÍÇ|ÌÇŒ¬ÛŠï»‘<$€CôþÅ¿ø›·û·ßÌÂÑ!½½2—­¿ëÜ>Ùnïè¯×Ûi|ûÖ{…£7½éM›—½ìe›W¾ò•ë^ÍTûí¿ý·oŸl}þóŸ¿ùš¯ùš½ ¼®óÑ^ã÷ͯù5ëÓ¯ûdœ³þŸÿçì´; Gû24i333333‡dàŽžr_Œ 2êêž:R?@Ó¯€'] "=t ˆ†¯XªƒdK‹O§vAW9²jZ@V<ÙÏZ2Ù¨]XÛ1À/ЇÇ%H0‚¤¶ Œ »UÀZrõtÅõƒdFý±àS‚Øñ$ÿøäÓ%ׂZ4rúÅ·à+=q䵂?ô‡þÐjÈ<öa¶âÇþüîßý»7Ï|æ3WµOø„OØN2êÕµùÄѱÙÜlÎËçhé:oÌG»Çâûn$±qˆÞ,’É;#sÙú»ÎíóýáÎdáz¼>Æ·o½{ÝÙÒûoÿm﫪c±æþøñdá>¸®óÑ>ÛhŠBŽ)~ðÙÿ6Ÿ÷yŸ·Òž÷¼ç­s}æg~ææxÄF!ì?ÿçÿ|ž™ƒèÇ>At¬üAAL¡™™™™™™;’;\82¡ªâNO½eÁÕÔ=Ô*üÀ-ÑÕ)ê/è*§FYýtÐé©M UáC_ÝBѪzÆ‚®t-YÀFñíöã'Kþ\Hé\yÇÁ¼ 58h`éi{èhK¹âÑòaÉ'9òékéd_ Ò…Ó¯°4ê):é»âÕrW*=éIOÚ|á~ájÌÄØðþÁ?æç]Þå]6_þå_¾ª¼â¯Ø|â'~âVýº GnPN™pûT½í@äÇÈŽ>àås”½Žs¯j˜[ê*°ïFò{‡è]µptêz¸J^zýe_^/šãè"½CòyªÌ©9ºlý]ÇöiL‡îãøOÓUlœêó”ñ‰óçm[ÇØ(7—­÷äÆÖŸÎÀvñ¢½hdoñë8míA|AíK¿ôKWŽ'cªæäØ{5I333333WÊÀ.y⨢‘¢MWw Dð >pK2ê£äÜ<¶,è úéÓƒ«uЯ֑íQ—M@'ðê(ôÙ¡ Ød/…„/\8 FãlpÅÁA6ØA~´7âÅ”l6µx#éÞd§WÖÐÓC+YÙÀ¯ 4âW*=üáßüãüÙ[ Hí¯ýµ?ôÇ Ib»Q6GÑ÷~ï÷nÕ¯Z8òužßøãæž{îÙ¸è‡wx‡uòlóú|ó7óú eÅ…}ðsîÏÝ|ìÇ~ìÆ„Í¾Ôõ¿ÿ÷ÿ^_y2óüÿñ2ÏzÖ³Vúßü›sóßÿû_'Mõ$• ¾}}îþÏÿ¹ù/ÿå¿l¼n1°®çÈeùeO¹1Ѓ´ÎïáË^ïû¾ï»¾æ)W¿úÕ›—¿üå›ÿñ?þÇèâ&ÜMæSŸúÔÍ/þÅ¿x[I>¼šbbÙ`ß䨧ê±yJáèØõÎϱyñ9Û%øËù/¯Û–סö—ñ‰ÔQ~ÕóÑ®½Ý¾ýÅØàG~äGÖIý­¿ùUµÝLÍþÌÀÌÀÌÀÌÀÌÀÌÀU2p‡ G&ÇVKPÏPÀ× 1cÆâPµ ¼ølÀñ²µ +®yÕCªµÔ’ËŽšFv²©æ‘6ðÉãgc´½/ʇÂ1„Vrô^àl²Èê“£Ã-p‹$£e(e{·¿ÏúâÌ6[¯Z–“Á‹'Ü8_4÷ÄyÆ ]_üâoþê_ý«7‰^¥pä«7ždòIçóÀ—o>ò#?ò¶õ%/yÉv^]?ûgÿìö"ž¯Á™Ô|ê§~êZp:ïÕ½±@r•8ó]{Q>½fñ[~ËoÙŽé9ÏyΪ¦`÷WþÊ_ÉĶõùúñö^¿øSêOÝ4úVxAÌïñ‚¼`ýúÑH‡»1ÿ¢/ú¢õæx—7öǼ\E/›ÇŽNYï§äe,™ì÷#>â#nš+¬ø|µI! ªçëy¾º¦P9ÂÏù9?g- ]´O(ùšapêvMÿ˜õ®Ðp•í³xÇö¢ý!¹S÷¿«äEÑôS>åS6OúÓ coë+c}%òñ1|Œ?Eá¾ð…k<úOÿéÍïû}¿o›¯[Ú­Û@ñ-8f½ôôÇvÌ3ÿr¢¨µ®z>Úgs¤™KÊ1Ŷ þÞßû{›où–oY'ÊVèuü».8öÕ³cå¯+Îigf`f`f`f`f`fàú3p‡ Gïw߈ªk( UR›@׺ ‚ã©+À- ™j hp²Õ0àÝðz²©~>«…e¿~>³“ýèÕFØãÖ¿2t¡ÐÀä¨ "Ÿ]5žõðJZ²Zû|â Y PÔÉW¶ÐòÇÿø_ñcôL6ÿÇþØ[õü(x‚É“-ÀÍ÷×ý×o^ÿúׯ“¤Ë½ââE…£Uñ¾ŸË¶ëcןO°Ÿº}Žq…_¶?$wêþwêþ®¸¡ðüä'?y Áþà)¯é:þù* ‚³õuQáèÐñëo,•£}­¸­c0¾¦|ìz¿¨pd[µÍÊP˜Q ¹N=]ds䙨ûwþÎß9’V|ŽnIÉ$Ì Ì Ì Ì Ì Ì œ˜;\8ò:ˆ:ZB…u´èj ~È47ódú¨§F‘Y¶ÐÈe#š–.:YKvµé/è–î´x“'›_ì Œ9HŽ ; ;Dì(mü]id²iì4(E|vÑA-<»ð‡,‹„Æg³ÈÁñŠA³¼RçNÀ8¡ë'}Ò'múÇtŒå*O¹qV žêP4Á •7@šWdº¹þ3æÏ¬’ø^á+ؤuß$¯w¼å-oÙüžßó{¶_/ $§ÄYÝ|ýÉ?ù'·ÿ–{=&P¬Ø…6E àFÖ 6ðûïú]¿ë¦'!ä øJ›×óš£Ç¼WŠ‚@‘â÷þÞß{ÓkTçÍyrªÞê辟C G§®÷Só2€Š×«~^ òäÖ#ùÈu;´}úùoþÍ¿y;FÏk€ú¡ºê±ãé‘^ ôª¥×<™¸Aìc{PáèíúØõ÷SêO=yûl c{Ùþì©ûß©û»×M¢ M>îã>nãÕÙðýèGo¾íÛ¾m»|ø¡ã;Öß=÷ܳ}∟/ø‚/X_GþGÿèé®`?öï _øÂõÕS¯ß8v½ŸW8R8ýâ/þâÍÏúY?kµûwþÎßY·µs+ Ím4‚ɰ­¯{^t><ôëlÇÊ_GŒÓÆÌÀÌÀÌÀÌÀÌÀÌÀý“» pTÁFkQ§P›€«!ô´G|< Us@³‰· Û¾ú:>›dÙ…£kñ,ìñOF­„ýxùÊ/^8|ýtô/ ‡B(ôb ²–]‹~.èd±oÔkñ²ÅOƒÔƯH¤žÝ±¯V’“ÍÞ7ºÝ0NèzѧүR8rÓa.àU¬—¾ô¥ÎBÎÓHž´ MÏzÖ³nºqsÓ ”§n>üÃ?|•õ³{#ù•_ù•ë b”ó G§Ä¹uzrh>G½c摹¿ãwüŽu–ÑÖná¥BùRD€‚ˆ›­^»J_áèT½lÖZ8:u½Ÿš—ݧڼÞض"þ¿þ×ÿúÆÓžâûàþàuHÇèQè¦ÓS$æ¡¶[…<ëc„C G‡l××±þŽÙ>ÇqÀÙNÝÿNÝß=™âé*àuµý¯ÿõŠósÌøŽõg[Sž*34ðTTà‰ÅGEtÅt…/ÇÊëXï|x•Òöÿ®ïú®«Kcpœ÷b¹Sí=÷ܳù´Oû´uÞ¼b0ǘ8Ç\Å;¶=¶t¬ü±ñLù™™™™™™Û—»¤p¤x£fPÝ¢›‡ÚxÉIP… :põ @6^õ Å8 £V¡mYÐm½ƒ>y<înŸ½l’±€äÎzü¦x€è*b°œ„à-ƒhäÑÉ)Ô€odA-œÞ¨ËyE"¶|  áÑM>9mô]ý“Ý]*ĦI·MlòàøÝlñ§=íiÛ×{Ü`ögö–»y~‚ñÉ¡ñF’/þ=ià»÷´"V7A§Ä™Mí1ùõ½1÷ôƒWQ€W }{XŸüÉŸ¼²L+ÏžBxö³Ÿ½Ò¼®6Nºœ}…£Sõ²Y{háè”õî)žSó2€<½ñ1ó1·<©àé°ú Z‡RèX=‘ÛÎ*>W¬8¤ptèv}ëïÐí³u]{ìþpêþwÊþî•Lûpøå¿ü—oÅY{ÌøNñ7Ž>ë³>k}ÚˆÏûoÿíÚw|Çwl_Ù­^áè:Ö»˜ëî1yÌêï«¿ú«7äü‘K‹þ—åíþà¿ýÛ¿ý:I¸§ºFðÁû\Çø‘w(~l!èXùCã˜r333333·?wAáh|¢G¡H=B­¡M¡â~…"2õkÕ-v ?äØÐâåƒ,ûêñtò ¾–|>«sèG[ЭŸdñ.…œ]*¸0 ‚ž~¸~£ã£ëkÉôl¡2#N†nvjÙ‘„’G}A·>ÐAþàéf/vnûGã„®&T5±êypHáÈd­Ï{ÞóVã¼C^ÍúâOl_Ï}|Ý×}ÝÆà¼^0Âøõž‘~î‹:½î3ÞHú'ZÑâ8%ÎÑî1ùõ½1ŸÆ1'ޝí¯A™»xZÀS^^½òõ.àu+¯]í¾ÂÑ©z»¶-²ÞÝ4ötÚ±y @ kÿé?ý§ÝÐ×¢å=÷ܳÒ+ªçµÃæQbóGôGoñwHáèÐíú:Öß¡Ûçî@ŽÝNÝÿNÙß](^7ú½öº;†‹úÇŒïcáè—ý²_¶¿ÇÂÑç|Î笯éŠq·ptÕõ®Hí|ð„'Hî¬wÁ/ÁC!Ù‚±QP£=²}‹(ÖdOÿÁËÒ€“[H[9¾Ò©ÐƒŸlm2ù­Ê¦ß¹dõɱñêe¹m0Nèê©_“ºhNO©4ÏX¤'+ GdÜðød´ùwÞë½ÞkT[q“ñzª¨yŽÜy  _[‰Ã“ÍçÑMøx#éKDÿþßÿûAúbôØ8³vl>ÓÓzcîu‘/û²/[U½‚ÑSE£-¸<›(Bü¹?÷ç6æ%i¾(7¤û&O—ÿ{î¹gÕë ®SõV#ÃÏ¡…£SÖ»×OÍËX29»¹lvÁ =%´¯pt¨žmK¡Ø×<]µŒÅº¾hrìC·ëëX‡nŸãXNÝNÙÿNÙßá/ü…ÛB¹§R<Ñs ;¾Sü…£öGù鉣qØ-]e½óax…1p7?i4®³ G^9|Ýë^·ÇΫ^ó;õ©£c AÇÊc˜øÌÀÌÀÌÀÌÀÌÀÌÀÝ•;\8¿ª¦€£.aQCPÇðèú#Ž×“HxtØQ›È™l¨QÀ+-èZ§È&HG # “]´x|¢2/[+ᲟŒ]&‡o N?gŠ.càádH®âPƒÁilà‘ :ôÙ‹žmýü*:éƒR¿‚=¯·iGûÑ*\ÝÖÂÑ8¡«¢K¯ú,1î…ñ†ÞE¸¯Ní‚É–}ÆœW\Â3/ˆÂ’WSÜ€ãØ£­CŸ®ÈÎ)7’éŽí!q&l>ÓÓvc>¾z2òÃ=YÓÓSßùß¹~Ž;ÞØÊ½§€¯y™tØ}ú½§Vû~Æ¢R7ª§ê¶á‡ŽNYïWÉËí,™tÚJÀüR|ï‚â¬"-¸ŽÂÑu¬¿C·Ïq,WÙ²sèþwÊþî«„_ú¥_ºº'=Ï÷eí±ã;ÅßU GWYïãñÃï½÷Þ[¾²yY~î¿ÂQó0i{ºo÷Œcâ;¶t¬ü1±LÙ™™™™™™Û›» pTÁHA C«vW_×4Å"µºo¤©[Äc/; ºÊêïú¡ÃNÅ¥Ñ_úÕOðøczdÂÙh,è—‚`…ŒsÖ èÂëãÎÉ 8šÀd-½dµÙíÑÇSÜaC¢‘ƒ×yðlã—d:xé.è—þm+º¾æ5¯Ù|üÇü¥ÿÆ>ÿùÏ_ŸJ°'‹¾ù›¿zŒ¯³ù·Ú'­/¯¦xéÏxÆ*æuµ{ï½wÅÇ/ûDÀ)7’§ÆIï”|ŽþÌ7ôîïþîçFYó²˜/˜ãÈ\G»`òes8ùµŽ›àÍHŠÂË—|É—l'ÝF¯ptª^¶k-ºÞOÍËí,yâAÖk@ÀGãS~¾<¨˜a¿ßó=ß³X×ÎòsÊv}ëï˜íS¬WÝoíEÇ 2§äEÁÚ6i~,Ð~²v.ù9e|§ø»JáèÔõîXà£æ7ò¤§¯ž7Ý%iºíì±p¤pîK”xÄ#Ö8ð¾ÿû¿ÿ¤˜Ž-+RPSif`f`f`f`f`fà¶dàŽÞgdµ…õu­¾\hõ㣑'7Ê 'W?Y57óZÿ¬wî>ôø È»¶8j“£{)0~(ä”áÓ ×6Èðø‚>»S; ¸ÂQüJ,~6´ù\Ðg«f ¬DÀÅ¢æé¢mä³…v[ G^CPàq³>ò#?róÝßýÝ+~Ñ×qžûÜç®"÷ïþÝu΢QÞ?ê/{ÙËÖ1E%Å%àÄ„Ân˜Ç ªÓÿ‰ÿöoÿöíKôÄ ®¾æk¾&µmë†ÌÛ|†:8åFòÔ8OÍg±jÇBÏeÿŒ{=¥BÛW|ÅW¬¯÷¯`<þñ_çŒb×ëhnü~ìÇ~lÔ¶OÂù—ùæó>ïóˆlü௟Ö~ÿ÷ÿµßO…#“អ—ÚC G§®÷Sór» GæZq&ÝöJP |ñ‹_¼yÌc³öýŒ_qÓ?e»¾ŽõwÌöyêþpêþwj^èy’± æR}¡ÌkMÁ©ã;ÅÀÄL¸@IDATßU G§®÷ñ‰·ù/ÿåú…Á‹òq§xžt¬t,냎Ì#拃}<ÁDò^Å>Ž-+j\Sof`f`f`f`f`fàþÏÀ.ý¢e„ Dê ÚŠ7ÒÆ ªC¨;€tôÓWÈFVqˆ>ˆŸ­Ÿ~±¨_¾à频­f’~6ÝÜÃÏnòoÖ[Èû!áýÜ›©ƒÁÍi…˜¨/X:£ä ">KýxdÑ:@—=O#6‰ÄBg”%c äµøhâåç—…üý ã„®ž$赘˜¾Ç{¼Ç拾苶b&Nõt„¢„O?›ŒÙÀ¿½^>cîßoOV(,™“æ#zçw~çužž'?Ù¼_›ÍnAj|úDq„W¼âë$Ø&ŠöĆ"ŠâQ_»bç”ìSã<5Ÿâ |ÉË%@nÉÜÈ>ô¡Ý<ýéO_onÝ$ýäOþäZ`ðD@O¥¸I2¹¸¢œyT>ýÓ?}ûD‹9‹È¯§™·$xùË_¾ùÖoýÖõkk|ä##oÛ G§êm ݇T8òYñq>™7½éM·<½vÊzWx9%/·»pôÌg>sã5§À~äÓáÏyÎs¶¯ÆÓ6§ü”íú:Öß1Ûç©ûéûß©y¡g›ñQE P§xçËd^%TÔvŒ±˜ëNûÇú³¾ð…/¤º}Páê9ŽN]ïã£/šTºcúX¸^ƒ½ ?ž¨TÐþøp^ñåCëlÎûÓaWî¼þ±… cåÏó;é333333w>w¸pô¾KÔ *ºhzªôÙpý^õI^kQçÀ¯V² +è<2dAtuŠüÆÃ¯n’O­zF¨Q–<Õgȶ×®<[€Ò¡ ðŒjé¢ÁdK‹× Â+ä$O6¸|:-hðìjÉ&¿ +Ÿ rµ£›ü§“mt4ýÿ°,÷+(°x|_AÂчø‡ßôªÌeÎÇy=Γõ…')‚nëŸ×z­Í×¾ú÷8¹ýØ]_¥«^{]…£óìGã¼j>³iÅ5¯ÀœnŠš8ÜM“÷ÊÓ>…_Rln8þ¼·Ìcóð‡?ü–ɱOÕÛõQáh—î¦ÏSo»pÊz?%/·»pdž#®æ^Ù·›a7¿åÄ>ÚS0§Ž®cýº}ÚO=¾\å8qJ^Ê»±½ô¥/ÝN}·ýùÍ[ßúÖ“Ç—½cüÙžO-ºÞÍ;wÌæ*07¾ÛÕŽ…£‹|zꨉó/’»ˆwl!èXù‹|OÞÌÀÌÀÌÀÌÀÌÀÌÀÍÀe…#of4õÁ±‘º¶ô°}×nîÏÆ×¿þõºŽ*æR¿ÐWk°4PQ ŒHžL5dñ<¾´øÕLØC‹—\±iÃÕ9â² y…ìÕ?·eäPØ'Ë9( JNk!ƒ/Yú%oA·E2ÑÓ×O—,È&^rZö“§ Ç/¹¯è8™Wݧ³4÷Œ…ŸOýÔOÝxáXxÖ³žµ¾ÒÑÆ¾½:eíñh¯BùÛkïùžï™øM­¯ž¹a6áó>PPò‰hÿx» 7ùók_ûÚ-k¼ =tî’S⼎|´Wu>÷s?wÓÓWÑPŒïE/zÑMyõ”'ˆ÷¸Ç%º¶žš0É8]?“e?å)Oٲ̑$÷¾œô)Ÿò)ëëž4p£\ÑéT½­“OO¤tOwئöÁ±ëcóòÔ§>uûÚåy¯mÚWpäV^lß§ê‰Ñ“vrî)¾À¶o}úzàøÔÇøÅ¶S¶kö¯cý²}š+ÍUàØãË)ûßêhù95/é+¤Â'|ÂúJæîqM!ÛažÞ{ö³Ÿ}òøò¥=ÔŸýÔ+¥öG¯­u\m>¯ñØÖ-_ýêWoŸ^»®­ÎQ%5ºøxž.蓎v!>ÈŽ9’eW„)I ºR"èµ6 N¿A±YrØÉõZ¹ì4P|Iñzé76:ôhZýâ‡Óý†ûèKsÿ€i½–á©7ò݈ëÍ“zÔ£Ö¢…§nLjíóÇŠ‰WßéÞi}²Åb²R¯þðÿðEj+O¯§ùôºWF<…á¡âÆ¥Ž84ÎëÊçšqZGžL›7¼á æÕ,Ö…Ü{Šì‰l=YćüyâçОª7ŽïXüÔõ~J^Ží*ò  i¶i۲ũûã¡q\Çú»hû¼®ýáÐýïÐq*çõOëDÂ~dŒó²]×øŠç2É]µ½Žõ~Õn‡~sùçî/xÁµ¹<¶t¬üµ: Í Ì Ì Ì Ì Ì \{.*¹‡2¿ìcûØ“üšß×<½ŠEÁP8RÛP8R;€[Ô ôß¼,jŠC]m¡›q89ô–ÝÊá‘Åc'ÛZýì,èj‡½|–ßê lá2ºÉ®>y6ø:( É–­`r.¼²_»V:;%¨‘É>9ýôòƒžLÚ"‹†ÿ·ËFóvËFô >af`f`f`f`f`fàgàØBбòwxxÓýÌÀÌÀÌÀÌÀÌÀÌÀ¸¨pDÍSGO|â7~ô£/°r+Ë4ßôMßtÓÓF¤ŽÞñßñG¾ë»¾K⃖¥âšš:¨^ѧ¾:@G«îÀ&ÀÇW“ÈY ~5Œ|fwamk$»¼ì‘Éœ­|äýB^(¸0ɶGðZ|A(Æh`A:Ùèlè§G á%.y€Ÿº§‰ø×Ž6²}aocd÷Ë–×@Þqyåè]0&Ì Ì Ì Ì Ì Ì ÜÙ [:VþÎŽnzŸ˜˜˜˜˜¸(—ŽšjÂÜ”æ1=Ì1ûÊW¾rŸ6BP8Z¦,ùîe˜\º½,ê j ž4ª†± k½OaG-!~4²]°§^áHß‚—î‚®€^ Ÿ[䪻Ä'‹F&ûZm2@¥˜VÂy?»'7ÒÓÑr<:‹G¾bš¥àðNW!g¤/Ý› 9x–äÂÙcà Ím¤VËx‰Ò·à OÑ?s™óâý–ù{ÞíŒ=gfffffîdŽ-+'Ç6}Ï Ì Ì Ì Ì Ì \œË G´+ù`Ç»½Û»ÝôêÙhÜò$Ñ:?0|·hDö¾WÕ^·|UÍüÇø>ýêc‘H=àÁÕ*Âè[F^…2èÉÒõáÕ:Èál¨ahGœŽ6ÏžVD âŸõ.ùƒ¸Dtubpa8²ä¼@d}2hF-ðø ºBºZöée—lq£7W>H¾–l:ðtŒ…nýÝüúå+8Ÿ´Lý0 333333w6Ç‚Ž•¿³£›Þgfffff.ÊÀ!…#ú A_µU¹êðdzò¨š žZ„…tÚX@⟼%ûÑéYØá÷ý–'Ž>uÙ¸Þ~ù’Ï»-ý 333333w0‚Ž á?ðU™ò333333wY-v$í‹Î+%»^·¼ôÃ?ôC?ôÇÚ«—Em(Ψ#¼eYWGˆ7¾¦†¦¶Q­¼¢Nº{ȇ/èjMÍ£ZKtþÉ‚±VB® ûégC‹ðÙ¡S» ƒ >]‹^ $Tðø ª@“YX+O?=E!-ùb(ölE_D¶òxŠB€~"-ymvtóNËò—*åï˜OIÇ„™™™™™;›Y8º³ùŸÞgffffîdŽ-±V<º¬X4ê<èAú¾^²Ð¾|Yþ÷²¨_¨h+äh£ã©;(u|õp-ym…z4ìnA']2ɳehê$ù«ŽÂ<]ûè£MôK¡ /\Srvå`*°l¨¾`É€úá †ýÙàÉDK¿EךçˆÿlðAÏ裓a]]_q í}–åéyÈC~Ùòi¾G-ý 33333333333333·9W)êò¡¬7¼éMoúÚŸø‰Ÿø‡‹îÿ·,êê Új!Z5…7/‹zHŽ_-•ÂéT;ÁK7[hô«[à[ðkñzõmAW ’#ÄÓWó`§zÈÈÃß cp{b ¢ÀÑJ¹š¹ 4äàZ‹Ø£Þ>Ÿ¼Z¼VMB_ÍB;Ú«Æ.>ý|.èŠkñгÅ6Èÿ`Èñ! d ŒãI¨ŸíZ>²§H {ˆÞÀtÅñв=ÊÁAñÔ'Ë_-<}P,ÚæPHî½ü½—âч-E¤Ÿ»lHozë[ßúS–öáËû’ï°JΟ™™™™™™™™™™™™™™kÏÀuŽ–ùŽ~dùrÚ›–@ߺÜÓ?üÍo~ó÷,oõjÚ7.tõOü¨Àià=ý³ kíA¿„ô¢éÃ-ÑÕèçcA·¼|U·`^p¶ò• -ºð5âúÿÙ¥s)dèRÁE€ñš|A7¨èœW|×Ç/àì¡Ñ‹à+ÞÀ³—¶’×ê§“høùKf!­t¶~ØRDcyØÒ÷û–÷]Ú÷\–Ÿ·,Y– 33333333333333Œ ˜›è Ëò_–åë—å;—å[—åû–¨¨!XÔ‘@u… >xÉ¡U¯Pǰ°C&¹ìꇧÇd=y´£|zÉ6}öjÉekAW>z:£´l.èMþ‹{Ô'Š ž=´Îÿü´èÙÓ—70ÆZÞµÅÄÞ¨¿«£¿r°—¹CÜMçô;è¶Ru4E 0Û€òŸ-rüXè²E¦%9}8~E›xh€ ŠB%šLñà£kA2øðdñ“]ЭήŽßý.<Z¸¢K÷›héÀ- œÝhètÐâ…ëãóÐÙFïÞ5™]ÿÆDÞF<}ô1vtœ?¶F{äôµòüø._¬hGC?F±Àr:ÃNýÑ&Z D§Û‚/p@Þ`Gp ˆN,°ä+›«ðŽ ]ü’N7ûlÖmÒmã‘E³Œ²ú mºµÑØmcÈFã×&¯Åoüxź Ûœ‘d³Ž×‚ØkÇBÓÏoí¨'‡'öìè—Ã=×?^6àÓÿÌÿÜþnìsÿ›ÇŸŽ»µóø{cÿèÜ1Ï?óü;¯?æõ×¼þt}ãúÿ¬7¯¿m–ŽpçÐ9>ï?N»ÿpÍ^nåq÷þO7ÿä€üãù¯Ó:ŠWK.Úî™óSŸ º¥õ›-<Ÿ=þÓÑgK_ ÈæW=£,™ìd?ÿùÕf+¼ñïó“MvâÓã·üëã‘­%=4¶@6áÅ¿2p©à} %H°%M`l4¼  x·¿ˆ¬ÁîÊf ^Q„>ù°x’†|€_‚యݕ+†Q¶x³ÏwþéÿÔe¹Ìvó·¨lã Ïg}²ôœO€F¶ñ‹Ë†V~ð÷å?z6ë/â«Íèú€Ï€=þ‹‰cFÏÿÈGg¯%½ècÛÊÁƒé¿LœåyÌïÌÿÜþæþ7?óø;Ï?óü{ó¿²]×tí¡¯7vûβÉÞ8ãÎëŸyýuckpœ×_7ö£yý9¯?/ºþÄ똺ïx;ÃÉñ±c{ö¿xdÝó¶_ö™z}@> [¿v”ˇֲËC£×6¿ ëØøDW“fº y{½Kûù'âÕæwü|[œÿ¶èw9E‡6Çþ®lv•‹!KÝʕ̂,±Ú¥QxÉ'cÐ ^K–\ñ4 òŸ~¶è×/±ñÒ]DVè Úæ4Ê><]~[ÐèXÈÔò¯?ú_º[á»þéñŸ¾ñþøÚúp²å Φ¾–\;‚88=2£ßìà§?Æžò£:–üçs!­>µøÇø§øžþ×Tl×ÿÌÿÜþæþwãø7?óø;Ï?óü;¯?n\g]1Ì믮Eçõç¼þž÷‡ßÿuüؽÿR°9æþÃu*÷?6ÑÝ7wÌÖZ?É-èÈãU,"C_K¯ñã‘o}kó™¼Ö8èàe;_ìÓ àdò1¶tZ~È[رDç·¸ó¯O7¹]uég>™lÂélüâI†úµ zŒÁªY £|ãAõ‚«Ÿ.ùd÷áÑjñÕŽ¶˜w[²ã|>ü¢ÙH‹mLÞÒ]L .2ÅJAý&‡‘ÿVR¶ÆqÑÝÍEüâÖO—ü> +F²éÕ¶Ñ5¦èdónLp-¹tŠq!­|m@žìèŸ|~öùÏîô#Çå½væÿFnæö×Þ6÷?ûÇúóø3¿óüsvlè¼1Ï¿7.Ž?ºæ(?Ž–èðyýs–“yý7¯Çýd^-‡ûÀqb}¹z[<ÿûEã·ÝìÞÿÕO·c®>¨•ÓxZN-96Ý#ÃÓËO…6ÐðÁ(În|²é>zþ“ç#ÿ£žñïóŸ=¾àZ¶@´ì “ù ¯%Ûöç—1e'ðlE_H‡AÁ&}CŠ^Î8\AÒäñóΆ­ßÀÑó¥EÏö賂y¶Ç>½ü²'—èh%™ÝbI— zþÑ÷ùÏ6»áÙXHÛ±æŸø£mþ£§§ßÆ[m0pÀ.Àð²™ýÆN¦œ“kƒôø§´ìXFÿü\äa¯:lNÿ7ò<óc{˜ÛßíÂþ2÷¿³ÌãÏ<þÎó#Â<ÿÎëyý5¯?çõ÷¼ÿ¸;÷~çÝÿáwO ãý_×vdàZ¶}¯fÑA׺O°t/ή%¼b[tPŽ_¿§§¬x8>û¿Øà?[;9m¾të:ÛÙ×’/×»þñ²kÌpñµƒÅ[H[9´«øKñ±•éæß¶0·¿¹ÿÍãÏÙ1¶cã²[l“hóø{vÎ,/Î'‡žÿæùgžçõÇëÛŽ1óúk^Íë¯þõ§ý¸ã›Ö:ïÿŽ9ÿu^]Ll¯?Ðv¯?øìü;úï˜Â§8ܺïÍFñ-¤õž³X+àáZ¾y}~:¼ëÇ1²o^þÑÇ{奻ꠋ•>È'ý]ÿìâø»ö÷¿<ЧÇ'¼û´x ºõ‹6æ_ǼÑM^Û’Mq ézKwKÏFºÚâ'G/@oCGK7?tóƒ^¿–Ž%;Ù8Ä¿¤· Óÿ’ƒÖWù+Ïò3ó?·¿¶‡Ú¹ÿÍãÏ<þÞ8ÌóÏë†Î!Îò7‚yþ½±ÝÈIÛμþ¹± už‘Ÿyý1¯?Újí3–®ãÛ‡:öt=‹æñç,~ïÖ㯸Z­gñž·ÿã·þéçr í!=þÐ’×¢|q¡ƒ];qðÒË= MË_zt@ôdóƒßú"²O—<ÿxôгGOúûÆO?hüÉ¡ÃArÙb/@Û•‹wi›â¥‚;DÇÀ²URj©ÂUÃ@ ‚£³ÆD²U¿Áj[$9[ô“%ǯ˜ÓÆI—N1.èÈ‚âØµ—2ÙÍ?Þ®¼˜’ËfþÙ²;Êf/›dÙxìe_¿ñ“·ãO®¦ƒOo\’Ñ¢ƒÑÝäÙ-¿hx`”Ï¿6Û—ùgkúŸùŸÛŸ½éæýiîóø3¿gÛÀ<ÿÌóï¼þ8»¾œ×_óús^ßz½4ï?ÎîÿîÏû/çáC?ÉuŸÝµL׵ݫº‡Ä ÆûÙlUTéžÙwß½$^6Û&²ÃwË([L {òtGÙì£á1ÃãkGÿbÞõ¯Ÿ-›»QvÌ?:?å‹_|zlâ[Ð@}-(Ƴ޿9;P|+Fo¨¾A(AxtmGGñvñliA-?’`ÓÇ‹¾ [ÚhÐC·¤+¶]\?ÚØæ‡Q½cA·>’Ñ‚Ñwý‘Wœcì䊧 "~{µùËVýÑΈOÿ7ò^ÎfþoìCã¶2·¿³ýîëáj=ötÜ©íxÓ¾TÜŽF|æñ§m¥vçñ·k¸ñX1Ï?óüÓ=A×Ëwj;ßt,©?nG#>Ï?óüÓ¶R{·ÚF/;þá·½7–tÛæí7#ŽÐÑgn!›LyõöVoô=êîâúÑÆ¶‚;Ù\ÐmÁhÔC'´x£t0ò’GÏ/<ÀgÃ9¨ñ‹ãOãG‹®Ý¥/¤Õ>;£OôKÁS  wuwí‘kðxA¢Ãñ h·à±Vˆ>ÊŒ´üÕâ…3,Z>³¥ô=AÔJ%› … x:ðlŽò y…ü§¯ŸÜè/9JòQ52=-½äÆœ±ƒg!ùhúÉ¢±i1–p2ùGa´—ülî“oŒäöùGŸþgþçöw¶ Ìýoæñ÷Æ?ˆÎÁ<ÿܸ֘çß³ëÛÆ¼þ8»¶š×_)ÎÚ®7£Ö××`^ÎëÏyýyÿ\¶ÙÏöíh»ûŸBŒû_ôöÕ]ûûZ²£|v“Ñ·ˆgWι´8ñ²\ºúÙ«X4Úd'Y-yv£Õ×xô@±égôôµèés>ØÅ àÉ é—+ýxÅ¥?ÆFæ`ÈØÁ ƒ ]Á5¸×Z€ÀÛ8 š‚– -›øÙN6úh;¿wò¿¨o `ÛÄÑùŠVÁhŒ€ÿÆB&ÿøûü“éd‹ßÆšM2Ù$wÑøö6Gä@þµ}´Í~ñØ(wý£5n6ôËó‚®cÐÏ[ð±ÏÇ蟊qúŸùŸÛßÙþ4÷¿yü™Çßyþ™çß×,®!æõÇÍ×[óúëæ|ÌëÏyý=ï?–åŽ wúþKÝûiwïÿ*Òàuÿ§¯Æ•-8yÐX»/Ý•ó‘¯ @xp÷xlèƒì’øl£ÃÇ>™ñ~vénï‘ÓÑÒǸë/ÿÙÇCžŒ–-иÑÉ&®må?½…½µ? ZIG)- "=vJ"Út>JÞ.¯~¶´£=8™V2ß%dAWÙ’—<<Û£=:x ;õɵ¤›ŒÂ‹1ˆ#É&³Ï2c~ÈíHG#øuù·qñ‘Þ‚®P?»Å>Ú;~¼ì'£?úè߸óŽÔgijAnúŸùŸÛßÜÿæñçÆq`(F:`ÔGùl‚ŠKñк'F£×øõáÙ‡þ—î üâi-âË?¿pm¶Š%ûéèÃñá »g½) ts:Í^k£üQŽ'ÑÑ T¬)ælg½f_[œlÀÉWxŠÏV G6ú‚®:ÚìŸtôáøúpöÐoþ²£_ìpKãÏÖBZà7ym°½ É—«ü{þéᡃóü7N²£ÿÖáª<ü\Å¿ÜüéÿúŸù?Û'϶’¿sû»qŒéØÓ~®½hÿŸûßíhÎr1¿óø»ïúcžæù纯?çùgžÊÀ<ÿ^ÿù—Åë¾þ÷Yö»þîü`=ZÆûL< qiwíÑë8£èBGŸ ø¨O^‹Œ>Řt8›Ñõ³¿Ï±$›®¶øÒc«xÑÄ–^q?Ý] ‹²¡¾2†Ÿq,a`†Ž‰;Lã†]ƒˆe«`´% Þã/¤UÇ8{Éjë/èÖ>¼Á§7ú¡ZdàdÂ[)ÉäŸ~öÐ-ù[Е_?ÿõñɃìŒ6GÿdÈ¿¶ñ.è6ŸÑ‹{xú[[䦯¼t·tøQP G)í. qIJo° YI€d£iÇ~2ZôìF/ˆ²I&\[lðôÓc'Ú¨S<ñÉ$7¶t,­œäÑØà§nô]a´—ßÑ^rµÙÐÏæ¸q¢'ƒ?æ/#Ž–Ž6ÿèû úèŸ^­Cü';¶ÓÿÙº-Ïåµ6úÌÿÙvÛ>^~Ú–æöwùþ_®Ævîsÿ³ïtœi¿ª>?óøã¸1¿gÛAûGÇÒyþ™çŸË®¿ÛVÆvžçù÷n;ÿvÎwŒ ?äüï @ÐmlðÝí^ßö}A·þò‹ÈXºÇŽ×>Ä~¢g;ùìå·ó™>™ä£þÙÉ’1Îü,@‡-}m÷óù[H+d»6ÿÅK([Z>ñYîYïÈߌ©v“¸¢ˆ jG›O_¿E¿Ïk^@ßâkh%„íì,èVÞJé«ilìé§]‹¿Xô›‹^Œsô¿ˆl‹Bùç«dŸœqém>Åa¹lü‹Èj—,`#;Æ/îâ\ÐÆ~qzñÙ”c¼âij°;ntd³±ë¿õ´ˆ¬}]ÿY®¦ÿ™ÿ¹ýÍýoæñwžÎŽóü;¯?æõ×¼þœ×ßóþãn¾ÿrïÖýçe÷ŽgÝ;Ž÷îw¯ÿõ;þUtíÞšlåßuCyZÐÕOñtŸJ§¥{N²p>ðº/‡Ógüg#ù· rdãyþ³Eä¿8µtñíÿûÆŸt?ÿ þYïÖûïè'µ99IyGÉ ´Äe=ˆ¯5P­’IoAo)à“K¦~É¢^"›ZnmþñèD×ÒI_Œø ™læ/ü<ÿdcInwü|´¯ Šƒ Y2 þYïì·1ùÇÉÇèŸÌh#ÛÚ|Žx²ûügú?ËÝÌÿí{nsÿëø0?7Žßóø;Ï?S;FÚ?Æsîˆ';Ï¿²u붃6¯?æõ‡k×yý5¯¿;3Á¼þ8Ëé×_ ‡œܳîîr?æÿ,’³}”Mû*ÐvþC¯ÀÂ^þG™dÙÎFþÓí\‰?Æ'ƒŸîØæ­X’Ío-[-£42 ÛõÑÈ–S}2ÙÌ?ðd“YHçêâ c€'YÙÉVƒ*IñJîȇ7à]\»éòOnÔ#«ßŠÎ6âiþÙ$Ã&€§oôßG>œm6áì2£ÿ|íúG'oéæ¿Ø§ÿ³\ùÏüÏíoîgÇyü™Çßyþ¹qçß×fóúc½¼Z¯ÕÆë¿yýu¶¿Ìëϳíc¼¾ìš'|äÏëÏyý}ÝØÞÚæömxû¶?O/µ›ÖqÎâZY?™ì,¤-Ëÿ®œþÈÓ]þAÇÕ®Íó_}`´C>?éòQ¬ÅCfÔƒó9ú‡:x£ 4ýŽÿ| ewA·öÑ㡇“‡~0rUhð×X^ÐíT’ðAƒÑ‚žÚ)®Ñ~¾ÈçÝl¡gG ÐÈW‰§3ꦇÆ×è¿>0¾ìgC‹ÞãeñGÿ6ŒèÅ\[|ùXDog1oüA¿¸´åΦ…ÿèé°òÏNPìú£ÿìŸçô3ýÏüÏíoîóø3¿ãyažÎΩóü{vµ1¯?Î®ßæõWWŸ7®Ýçõç¼þž÷7Δû/ç¶‹îÿì×»ç?´ kýŽ‹£M8:9P^\g¸Þá䆜g²‘®>:Ù`”‰¦EÏf÷Çü‚]ÿ£ ñŒþ‹™Œ¥óß‚®€–ýûHëX³É\KÎXò¯müñô£ç{!]v›½µëch+:ˆ–¾FK—M ^ÅžÝ& ¯¤Õâ쥛_?óø;Ï?·žÿ{AçÑyþ=ËG×7CÇë ñËÛ¼þ8;çŒy*wò5¿óø;¿o;Ç_ëú*û¿ãÈÆîqe÷øëøB&¹Ñ?Y|÷½ÝÿFË~ø‚nïsÙËvÇxrè`äñiqÿ¡MwAWÈ¿bÐ>ÿÉãËèƒL‹¥˜é‚ü«eÀÉÑ×òAÀAöÏzWøåäº@p  ÁÔ7èÕ ðÆB:{£~É_È[zzh%”N>J˜>~1Ö6~<Éfï]ùbD§S\ù×?Ï?ÝÆ“_m¶²Ý…ž|~ôÙÇèð^‰CÃ'ðø êç·/Û£ÿäó¯¿Ï¿ñÛIÀyþÙÏ^~kéMÿg¹›ù¿±µ½Ìíïlÿ˜ûßmÃ1£ãß<þÌãï<ÿØ#æùw^ì¿þ›×_óú³ë©®»k7æõ÷¼þvì¼îû×hãõ»m®í®Ö¶·oû ™ôÇØò ôÈtþ‡£Ñ©p³ +m´g_ ƒº¶¦S’ßþ“zþ€@þáäøä²‡¶k+ÿÉ臓O_lx|÷ú7~yH¿¾Ù¾24¸+Z dK­´jPðÚ]û%‰ž…Lzd- $ä¢/èò‹gc_A‡~ÉׂúÙ(öüèþÅfùÒ'— ¼Ñ.<;dÙËýl-¬íø£‘ǃž¯üjG`¿§øÊ3{h@[^Ù° åƒì>ÿ y…}þѦÿ™ÿ¹ýíKsÿ;;®ÌãÏÙö0¿gyp™çŸyþ×7®%çõ×Ù1a^žmóú{ÞüßrÿÕýÀ¾ë׎}»Ç?ç†qüäÆ{_òúdx÷ŸGÈå¿ëÐ|¡gcŒ ¬¥ Z@Ÿ|ºèh»v]ãc'ÙìhGzº y+›ÿäèÚÝ>šñ'ßýG±¦»ˆœ àt 7k²—M‚f ‹Å³Þ¾•“¬A<@c ß ÷ÙmŲ6êÓa­ükÇ$ç!¯>ðÉSñб¤¯½Èÿ¨Ÿ½bËΚ>»€Žþ8~2ÙÕ*¬©|ó‚n7œô‹_Ÿÿ€Ž~ãg»øÈŒúŶëŸîUý·~¦ÿ™ÿ¹ýݺÿÛ?æþwv,œÇŸ›ÿóø;Ï?óü{µëŸyýqã˜2Ï¿óü»{ÿ1¯?æõW÷fW½þR¬ ºÿìøë^w÷ø£?ndÜ—Štýƒèþµí /]üÑzE¦Æ_ÔÍÿoY–ü/èjC{™qñwÞøÙ0ÞÆŸv‹owüéh“kül]½n à´%Í£/è6 %^R$OJF6è•X46Ç$ëçG›2pö$›í’†7Þè?Z }:Å4úÇÅ–îeþ;]²£ÿÆ“ÿÆ/<ñ‚|†kÇxÉòC®øõáÚbÈôükÙÛõOâŸÌô?óo›±ÌíoîgæñgÙ!ޱr2¿7Î-óüscÛèü?Ï¿öšÛH¸v÷úg^Ìë¯yý9¯?çõçí½þvî:æ²ýo<Ç×?Ï»6Ò²Y ó!98À‡wŽd/}týq[¨ß/ú@Ì@lxtòò9â‡ø§ÏOK¶£—7vÑŠ?ÿMŒdÙã…Ó˾~2 z5`ø:Á€J»p€Þ µ ¿TL§~Ë¢²MVºìûð춬ì / ºêýg/}m“p“M^l y}x>ÿá£ÿhÙÓÏF1ÇË?ÛÑ´@ìÆ¤Ï†ÔŽ¶ø×ø£ ýò=êd¿íóO~úŸù· µÝÕŽÛÒÜþæþ7?ËNrß~2¿g¹pn™çŸyþ×g×sã9s^]ßÎëϳcå¼þ¾q?)#¶‹yÿñÀ¸ÿªb½9ßï^ÿ´mÇ¿î™ÛÿéÂÓíž"]v[ÈvN“Õ»6Š'{Z6µî¯µÁKòxù[cmåŸLÀž¥XkÓè?š¤§Ÿ_ôì—ËúxW‚_ÉÈŽrÁ-A£ýÿíœÛvÜ:®E_Îÿÿp¿Íb¦s«|Iª;™C! âB.UíÞòáAðE@‘ÔÙTZävÚb޾òŒ%üër<ØEÞÀ˜<åõg`h¶‡‰[Úpo´Ó¿|t ä±=úúUN?È@ŽõC !¯ ú<íÄŸñÔ¡åŸu0§¿øGž'ÿá_üáÇYèü9¦üSþ¥F@ÆBõç¬ÝÖÿêo÷î_Ý?»¯:Ñ÷Ǫ™ÆƒßY{-•ÿŒû76?ëŸ:vïþk›õß>sþàOÿ̹ÇÙgÿè2çƒ~Ж>;X/?ú «ZÆâ‰>ä˜v·Ò¶íôO¢Îô…͹NýÁ‡Ïþ´Ëø—ã&Ìæè³!í vŒ!çè¿Ï?ËÂŽv}qÈ :ØAÂ'/CÆÈ@ÊMÿÎa=H?ι~lé‡9u´‹îôOŸ9di%í1v-Ú‚g ;§<2ú‡ÇXÛòÝ/cúŽñï´ÇXÚ:Xÿñ-dßò¤¿ü‡ñwž‡Î_ù§ü»jDõg…êo÷î_+'tÿ<¿Söû÷ï¾?¾û÷ßïÝÌ…{ü“!æþ­›æ lKò83ôýÕ¿ºŽ½‹ «ÿ¯kØôïXûê3ažó»d˜CžG®ÞüýCZd”§Õs}ý»GçÒº€‡F°Ë37ãØ–9~¤,61/Ðô™‡÷Z€aYì¨C‹Ï$_®-sØÁ†úêíÀ#«úè!‹¯Ïúw]Ú›þs/võáþõ£vÜ?²Ì»Ÿ¹ƒ}#÷Í6´ƒ õd[Èëãè¾ì½9§_[ì]ùׯmþÏ÷þ+¾ˆ3â£ø{}ÆÀLÈS¿×ؘwlË?åßêÏŠr…dݵ­þV½/vÿèþAý„ºuÿ$æ7qï#÷Oð°Î  9¶õ_÷â{Ðô ¿Õ'MÿÚ<Ø7Â'zðé£ç÷/­Äüô,<ìkCÿSÿ³þ•§k¥/þîßõÒ"æä»>ÖãÚ™“f_ÞO·8xaÛMჾcûúg<åé Äѽ$ç€K€ÈÒ'ò>í£–] ´¯ÞôcûŒ!xÚp<çݳ~máúF_»ÚbØÇþÝ2sè"£ÆÓ¿}ô§ÿcøBWþ±—ÿð/þÖ1éüyªüsæÿòoõ§úÛý£ûתÝ?»ûÍ1c¡ïãû‹ZøÖûçGrå$â„BŸXá¡Ïi“>6üþVÆïUíL›ô±qwµ%ë<öèc_}mzÿuŒ 6Ý—>ëvž>ºÖŠ£{#ô$d‘s?Œ!íÓŸòŒF8~iÛų!7ç-ó´Ì–<`,HG÷LåÐ`eÑ¥é_€á;7}è_Þ”ŸkD—±r¬àÔsëe/ð\£|ZmØwŸÚpL‹,¤Žë`nÚVÖ=bKÿÌAÎivwü‘GÆöè~Ø?²Pþÿø[g´ó·rBùçij•«?ÕßóÎÄyðÞaë9áÌxwaîêþƒ,Ôý£ûG÷î~Ï‘º€Â™Cé?ëþA~þÌùc-È_åù´~—ÎüŸ1ºcíÝ&~ü£ †ÌóC“¼)MI¾rúW†–9‰1k þ¨ïóðlåíõßñ´9ýϽч´»FklÿaíÜàÃŒþ0d1LJwSŒ!æ|œæ‘óGä 0‘?•Ô2Óæú˜ºÍ¹mÂÇ&²ú×ÞôïËGŸ¿dzÏÿ!òÊßÓw­ôñ‹O[xø™8"µu澦-ù÷äðå­tÏ?ü‰¿ºúÁ÷{þñ¡}mä4…ÿuü¿òÏYÌåß³¦TΚj\Ì8¡ÂT`!6Ý?@cQ÷îWßÝ¿ºyÿò›™Zb±®˜SåÏ1fÊ9w•Õ÷û×øã¿C4ãÓZ´«-¤ä ä&1¯ZôæþG>~ùþŸóÓÿ1õâï-ÿØÔ¯}uõÃîá`=–pðlrcnžÖMÛ²ä&¹yx"ˆ³E‡±…í´‹}dztl]ÓÁº‘¾mwÿêaÏ=h[L±I_þ\·úèÂG¹IŒµÅZ§¾>i‘›t o„Mhú§éÝ+ÿîý˜¾Ù¿ò½ü‡ñwž3Î Ôù[8”V,?˿՟êo÷î_ëÎä=¶ûçª «b®»¿ÿýÓ÷Ç÷þþòÛñ­óÏÜÕ÷¯?Úhû•g=íjCò¯ô¡mæ d¥ùýœùÊ–yýÓW—Ö¾|ïØ™ùŸu «ÿ£{£Ç¡£Œvh™×?­>î«>㇒ {¨ÑÍš›¤/´³*k‚G+HŒyYcÈõ3F–¿²/¾dZ×rto}dy˜³¯žëp<ýâ/¤Ü{þ±£-|Íþô¯=[åh%ô'1¾òÛú<ðvÿú€¯_Zd£•îùç@¸}å?ü‹¿ÎùÀœPþ9óêŽEùwÕêOõ·ûÇ™'ºyû\ùáwÎîŸ'ÖÚîßÝ¿¿ÊýÛš¾ßyˆUx<óþ㺕÷ÌS&1ì~þáÿ´²èëgú¤É›þ‘—vÿŒyðoŸ=MÿŒ÷ý#‹¯¹ÿcøâŸyçl§•\·2®_¾rk'3ºÒ‡›aÚ ±Qç}IðèË?º·>ceà!'ðŒv æµ£Æò‘sÞ—4çé¹N¥„ ›èéÿèÞxúw=òÑô¯]|º7û®‹ÖÇ_Y±.v¦üñ¸/æ$øÚÜý#sÏ?s?ã½üƒÂ¢ð/þ:ë,”ÖY0ÿƒJùwÕ4ÎXxVÀ¦ú³0øLý·ê/(,ªþžgªü»°(ÿž1Qý©þxYÉ»ÐgóïÒZÿîõ[Wß¿ä5æüþf’ë`lþÓ.þ±-0‡þi™“ôÉX¾xÃsü؇¹–ÉwÿÓßôìÓÈ=ÍÁÃëËÀ'„l}ˆ—ÀNk@ ÇXàÝ|x´ú¡Ýý¬!Çü/ðl§äÔq=úÖŸ{™ëÀÖ Ll@ÚšþÕCîSêÈ׿öôïzhßóϼû¿òGøÇŽë:º/ûÏøëltþÎüKÎá)ÿ”«?ç„;x+8ü·êõ·ûG÷¯U_83Þ¥»vÿìþùë÷Ok’w6ÎùÆïÏÖk=­¶äYû[ÿÌiòÔAšþËGŽ¾çŸ¹yÿ&&´E¹+ÿÚ£EßXbÌÚàÍß?° 1Ï3×7ý#7eCG¾uä?¼Nn|3èËecúeƒl–±HçøQx†?ÀA—ù¬`ZHô§ÆØÃ†¶ èMÿô?êÿ}Ùç=ÿð±ÇšxèëŸVÿ¬ÿ˜þçúÜ?²ûþ™ƒôO;÷ÏØ¹·ücãþÝ#ëÌÿý÷þÅ_çïñù¯üSþ¯þ½¾]Ý?ª?ÕŸêOõçÑßÕßï]y¿òýyïýû-Hmöû{ææç·+ßÿÊúÖõ)'o~³HŸþ@äÚøÖ†ôï<¶öý³Öé=H¿¶oùǦ¿E MZüºÆýûß=ÀGîéäžîèp€/ëß3'8Ω7ÁB9H•£õlçôO‹žrÓ¿à3áGyõ‘wÞdŠþàÓ§u¬,ú>Wþ‘›zÇðÅòêNÿðÛêoŽÕµýŒtžåÿ0}[?í½ýç?ü‹¿çœÿÎ_ù‡:•¯ëoõ§úSý©þxwö^MÎ$.Û~æþò/(T¾Sý]oì9ñï_çWõw÷ï÷·:`é÷7<Ï®­ßèÌqþàCÓcì0_]ÏëÁz9ÿô÷ó¯mè;äÉGÒ?s’òú§•öü#ÿ©í\ÜSý0>‹¯›<_ ¢ð|v°yþ 7m¸çi± !‡D_=l»&[d°¥îô íO;®]m¾çß5øâ¯ö=ýÐ";ýã šþ™Wnîaß¿vwÿÚ¢…æþµ+þÏðO×ÿð/þÎÐù{~þ+ÿ”«?+ª¿Õßêoõw~+<ûû£úû5ë/yàwÞ?‰è­üC,B³^Ãã»×–y×NßX¦•O‹-´ÛS‡ýó£Öüþen—ßý{fÑÿøG¶ÈhÏ5ÒB®‘u8_y×ï©„ÃßMn¿憙³ïº r­„ j¾€"ϼöl'Ÿ>¤úÈù’ìÓú £Ÿ]uOÿê`>óî×¹ƒõ2wÕ—7Û}ÿι®¹~ýÚNÿê9çXüá»vçhé_»ÓçìçŇ8…ñ×ù[9{Ï¿?“ÿÑùUÿè»—ü¿Ž¿ð/þ:ÿÍåŸ3g–ÏúO=¢–|¦þUÎXªþVçý·ûG÷îÝ?öïÏgÜ¿Ì5È?Ä,wèêû>þ|æ÷'{E_=㟖ohZ÷¯>cýÙw¬}ÀG]ú´ÎaÛýk‡yü";ý31% Ò—º‹û›þu‘¿ÉÝ+7ø˜«uÜ ÔÓ c^}téû2î­Ï9Ú)«?d!ç‘™/þÊ¿¾h!}à_6®ü8»×îÞ|þÿ•»æ•Áþìkã`¿:4®cÊþ¬lO;³Ÿÿ…MøoÏ#q"yvë¡4}A;º79@“Ѓ|‘Ȫ§ Ú>­¾ÑÕ¿¿lë>´ÛbÒ?¶vÿ®9åõÞ•ü¸×…>ü¹íéŸñ[þ±}Ô?²ù_ï,¿øëüq•åß•«?ÕßîëÎÄ],æýoe‹î_Ý?W¾|ïþO¼tÿîþMœ@ÿê÷yúèþ•½—½¯]¿©ËÙ›ç\~uÿÇk»ºÿÀÛiÏÌë‡ÖÚAŸyh÷?ײ$~ã¿,ì+‘àŠà1¦/ß±Á¸îE9Zƒdïû2ä[àmÑcŽ1­þôÏÿ,Zæxü¯¡Ý[ " !Ã~µTNL}ñŽÝ—zØÙý¬Wÿ-˜{þµñ–liŸþGücoþó¾W0¼Wïùð_˜Eñwæ ÎÙÕùïü•Ê¿ï×_ΉϽúKÎ)ÿ–?sÿ*ÿ–Ë¿å_òµ£ïŸ¯õýCMçÞlí§}¯þ£½õýAðûÿÞùÇ×[ßè}ä÷b [W÷ÿƒý²Nä ÖÝ/EÕ/µ¨c1¬Ëµ ÁÂ:} ú"”Uß1ºØ‚OË‹¦õ%Ý—yý+;mìþµá VöÊ?> tWÛþé#£MÆ¿â_ûôïûÈþóÏ|ìûÿu&Š¿U`ÌWù§ó×ù+ÿ”«ÿ»U«¿ÔÕîÝ?ÈÝ¿ÎoTîâAÞ?á?âûwæ_þâHW÷_üÁ§ýˆÖ«¬k§Å-gŸößzÿÈ蟾þîÍ-|û®_ÿ´_†\Ü—YÐ…øb×``p–þ3ïžà :|ûÚfš¿⇗¯Ìô,Ä<£Ìîßµ~Ô¿{¬Å?·Cúw½ïùŸërχ©Ëý#;÷ìAÐFþOÜŸ(z?þÄñ×ù+ÿœ5§ü[ý«þŸw«îÝ?¼ÃvÿêþÕýóß¾“~öû=êÉü>!žæ÷7ó­¿è²lò@ÓþÌ]SN=äwÿ¬SÖ?ä¾¹Ù/µ¨‹qmž Î—"ÀÌÁ÷eÐB!YíѪG_ÒcøÎÀ¬oú‡ï@úQö-ÿ‡Ú‡ýãS›Ó?öïùgîÙþÁ&ÿ×ï?ü‹¿Îß*ÌæÅ™ÔñËù¯üSþ­þT®î_Õßêoõ·úËÝ?¸m­oeîL|CBýþ½÷ýùÞý‹ùgãýÏæî¡ÐGöÜG÷O¾¼ç2æ~²óßÂß9ì|âE~h_®k¦…çËÞ÷ #Çã  ÕºÚuÞ—ªŒ>hÕ=º7›^Ri!×Fúg¬}mL»s}ê}Æ?‡úYÿ躾üŸï™w4ß“ï¼ÀÚ÷þ RüuþVð¯yDLÌ/ž¡y®w^½òßÊ='±¯òOù·ús~$x&h!óHùgáa~5‡Ì¼‚„óâVþ-ÿ#3NŒâ¥úSýùSõ‡8ü•øCwÆ5ñüVþ3~4þ•sÓ¾sïùGîË’EõË.ðÇÂX'/@¥…–Çñ¥(ÇËÔ¡ï ›2ØðEi9ýÊ›¿–ÂC†_7•UÛ® }HÆÚr=ÌAÚÐæÜ—:Kòmÿ3ØŸáß½ÜÛþ×û§ð_kÌ<"þµUü]çŸÎ_çRþ)ÿVª? `ͬþþúý[,»tÿð[˜àÞ5¿-«¿o×_¿•Á C1½÷ý‹2ï?d~¦þ±.|Æ?¾ö÷°^~`ÍûþÓB¶kôEÿ”ïB~±^_&Á †˜tÚùÒk=^2Äœväú1ø´Aë_› ëü•>¾]£ë8XŸò]èWü»6m}fÿêäÿçßø¯s`,Ï?bÖùëü ?SÊ?åŸî?wÿ+ÿÞ®ŸÝ?ʿ՟êï翟uÿðÛš ¥îÉ|_8¶îÙ";¿?ä+?õým«û÷ôMdÔw¬}[lMÿþÿKûnè’/„Vš/‚ýùB _¤ûöEÉGyÆ<èð æ/Çð…Ô•¡~P–þѧoPÝÛX»èéß5¼çùgøÿèþóþÅß:·œgˆ3A^ù•óßùûXþ-ÿ”Ê?åò€Tþ­þT»tÿúùïϯvÿœßé¬MúÕû¶®¾¿?º¾Ï‘½ª?Úõûʺæ”›6ö×&7ðµWùþêüå׎/ òÅò’ Ƽ, >²§²ÌOyçy¹ôgÀ¢ƒÿÿ6ŽîK@0¯>ü+ÿøSÛ?ëŸ5cÂý{þ•CçOø?ÜÞ(ÿá_üýþóßù+ÿ€@ù·ü[þ-ÿþîûgõ§úSýùþõ—wè÷ïg¾?WôÿÞýûÝN½“>{ÿ™ºÚxÄïÚú­?|‹Å^,ÒF˜âLj@ oûC|e"ˆ¢I@ OYìÓç×keàч°-†Óÿ¼ˆéÙßýë>6µýÿØÊøë|s:¯ó_ùgåÈòoõ§úÛý£û×ÊÝ?ýþßý»ï¾¿~ß÷—wYëø#Ο¿ Ÿï~ÿãyýs§œúôç÷Ç1ü^ÄÆþòEÍBòÐú"‘…˜^6ľ/Ú–_§ ý©‹uõk;ýû£Ò!þÊ?²ÓïÔÁ׳ýã#ÿç{ÿóã¡øëü•ž›ÿ˿՟êoõ×ûf÷îÔ¾)ºuÿêþµÎ5Ò3ñÈïïýþeö›Þ||¸¿‘ë``ÝV–1d«®ë^³ßø_ÿÆ[øÏÒ}y³EÈ-ß—Ê/vþÅ‘¸¨c7˜l‘£úcýì¶ä럖¤ð–çÐe-÷ü3ÿ'ýîó``ÜØ‚‹ýg¾üôþO¬Å<üOLŠ¿…±a\<*ÿb¯ówÆZçïü—Ä¢ó×ù#Ê?dË…Cù÷1÷ð¬þT¬5¶ž3jÏŸ¬?¬ãOúoÿ®?¿ÿáßûþ†Mêçÿ×ý5!ú¢yi&T_àÓ‡ û´ÎˆsŒùáI»ðåéß12Ê]ùç"lAþåXÿðéCØPG»ðõ—ÿ……x„ñç9éü­Xsœ¹„Rþ…3—W¼Tª¿Ý?VnèþÕýӚ齂Èð¾Ùý»û7±`<ôýñu¾?¼ÏÍû/ïÉ»0}ȱòòSgžmzþçÜÍèwÿ‡ýÍ4÷ç‹·åeú—F´ó}? Žî˯‰È;g £=‚M?G÷FŽm?ë_?êé_¿¶^ìõ“ÿðãÁÖ8òâCü@ÌÓ7†ÿâoa"nà6ž;ÛÎߊ!ãì€èFŽműø[u£ówÆIù§ü[ýYy´ú»òB÷îäï Ý¿º~ôþMÌxï´5Ž>zÿ$ ©ûÞ÷?r<ÞéÐý«ˆÍý d™ÅgÓÄ`¾tô&¡#©ïN‰¾ÓŸý}Œ/xÚr¾®Ä¼äåú-ÿÈ0¡ë˜>OþÃâ·øëü•ÎÜy‰qV¤òïú©þ¬ºJ<@³þ[oá;Ž÷œËØZ\þ­þT«¿Õßê¯ßnÔ¨ûÇÂçý‹³rUÁ‹z Ys­Ï³æ.‰õ/¹—Gûsnâ¯þÕýǵLÝ¿ª@ÿ"¹oZƒ€"å1&ÝxòÑ—‡€uþ1¦¯=úèBÚ Ïü[þÕÑ—º¬‘àu{ÀÃç1Àî§ý‹ºù?ß1¸†ñ×ù+ÿ”©ÿ½pUª¿Ý?ºuÿ\¹‘é]Þ;<¼·îÿÝ¿OÌúþøû¿?ü†5î½[yN{vèÌ;GRìAŸ=Ú@W_»m#óדþõ½³Aö/Ü ú3Pèóã| !ý9o kW›òçøP½ùÎ?HœŒ$úßð/þ:åòBù·úSý}]½gtÿ8?,ÀB\º-\À2Nè‹ ­D¿û׉M÷ÏîŸÝ?»’½š+kÿQ,¢¶ ‚ƒGbbbì<}ùêØ:7õóþÄÇ#ÄJñ ?ò…ñaž1‡Ü:þ‘ϸü³P3[±;Úòoù—øØc„X)ÿ‚Bù—=¦(f$þä×?[£õAòO‚é3§.}h&Zÿì™é9x;?ÿá_üuþÈ%äsãƒUþ„.åßX@ÕŸ3&ª¿g̈™}1“¯¬ºÈIÌùÀÛu´Å}mЗÔaì|ñÿ_,ÅFÜhÅ^<ÅÒ1mñ¿ 1fÄ̾˜ÉWV]ä$æ|àí:Úb޾6èKê0v¾øÿ/–b#n´b/žb阶ø_ˆ…Š3bf_Ìä+«.rs>ðvm1G_ô%u;ÿ/Å¿8Ô†À—@`h*“_dyäÑÎñ<ÈÊ"/Iž|—EnÚ}æòþÆÔŒâï<7û™H~ç¯üC,p~Œ âÃ8™çjö‘A9ž97Ç»MíÊgL?ÿáoLáPüÂAó\Í>sàÅù™ç ™9Þ1e’Ϙ~çïÄ|Äib>ûÈ„ñGœðÌØ˜cÏñ¢,}ùð:åŸä_â& ¿“£5¹Îdé­ W=Ó>ÖŽº»œ|õ«—ÿ³X‰ mø¿.`{\96ŽÄNþoûX½â¯ø#6Œã¨ó×ù3—öÍ#Ž÷¸‘¯œñ´Õ+ÿ”Ê?å_óù¢úsæÜòuÄ:³Ç|匧}¬^õgÕqª ŸCàÿèe‡= (3, 8): import importlib.metadata as importlib_metadata else: # For everyone else import importlib_metadata try: __version__ = importlib_metadata.version(__name__) except importlib_metadata.PackageNotFoundError: # pragma: no cover # package is not installed pass from typing import List from .ansi import ( Cursor, Bg, Fg, EightBitBg, EightBitFg, RgbBg, RgbFg, TextStyle, bg, # DEPRECATED: Use Bg fg, # DEPRECATED: Use Fg style, ) from .argparse_custom import ( Cmd2ArgumentParser, Cmd2AttributeWrapper, CompletionItem, register_argparse_argument_parameter, set_default_argument_parser_type, ) # Check if user has defined a module that sets a custom value for argparse_custom.DEFAULT_ARGUMENT_PARSER. # Do this before loading cmd2.Cmd class so its commands use the custom parser. import argparse cmd2_parser_module = getattr(argparse, 'cmd2_parser_module', None) if cmd2_parser_module is not None: import importlib importlib.import_module(cmd2_parser_module) from .argparse_completer import set_default_ap_completer_type from .cmd2 import Cmd from .command_definition import CommandSet, with_default_category from .constants import COMMAND_NAME, DEFAULT_SHORTCUTS from .decorators import with_argument_list, with_argparser, with_category, as_subcommand_to from .exceptions import Cmd2ArgparseError, CommandSetRegistrationError, CompletionError, SkipPostcommandHooks from . import plugin from .parsing import Statement from .py_bridge import CommandResult from .utils import categorize, CompletionMode, CustomCompletionSettings, Settable __all__: List[str] = [ 'COMMAND_NAME', 'DEFAULT_SHORTCUTS', # ANSI Exports 'Cursor', 'Bg', 'Fg', 'EightBitBg', 'EightBitFg', 'RgbBg', 'RgbFg', 'TextStyle', 'bg', # DEPRECATED: Use Bg 'fg', # DEPRECATED: Use Fg 'style', # Argparse Exports 'Cmd2ArgumentParser', 'Cmd2AttributeWrapper', 'CompletionItem', 'register_argparse_argument_parameter', 'set_default_argument_parser_type', 'set_default_ap_completer_type', # Cmd2 'Cmd', 'CommandResult', 'CommandSet', 'Statement', # Decorators 'with_argument_list', 'with_argparser', 'with_category', 'with_default_category', 'as_subcommand_to', # Exceptions 'Cmd2ArgparseError', 'CommandSetRegistrationError', 'CompletionError', 'SkipPostcommandHooks', # modules 'plugin', # Utilities 'categorize', 'CompletionMode', 'CustomCompletionSettings', 'Settable', ] cmd2-2.3.3/cmd2/ansi.py000066400000000000000000001003421416142110700144720ustar00rootroot00000000000000# coding=utf-8 """ Support for ANSI escape sequences which are used for things like applying style to text, setting the window title, and asynchronous alerts. """ import functools import re from enum import ( Enum, ) from typing import ( IO, Any, List, Optional, cast, ) from wcwidth import ( # type: ignore[import] wcswidth, ) ####################################################### # Common ANSI escape sequence constants ####################################################### CSI = '\033[' OSC = '\033]' BEL = '\a' class AllowStyle(Enum): """Values for ``cmd2.ansi.allow_style``""" ALWAYS = 'Always' # Always output ANSI style sequences NEVER = 'Never' # Remove ANSI style sequences from all output TERMINAL = 'Terminal' # Remove ANSI style sequences if the output is not going to the terminal def __str__(self) -> str: """Return value instead of enum name for printing in cmd2's set command""" return str(self.value) def __repr__(self) -> str: """Return quoted value instead of enum description for printing in cmd2's set command""" return repr(self.value) # Controls when ANSI style sequences are allowed in output allow_style = AllowStyle.TERMINAL """When using outside of a cmd2 app, set this variable to one of: - ``AllowStyle.ALWAYS`` - always output ANSI style sequences - ``AllowStyle.NEVER`` - remove ANSI style sequences from all output - ``AllowStyle.TERMINAL`` - remove ANSI style sequences if the output is not going to the terminal to control how ANSI style sequences are handled by ``style_aware_write()``. ``style_aware_write()`` is called by cmd2 methods like ``poutput()``, ``perror()``, ``pwarning()``, etc. The default is ``AllowStyle.TERMINAL``. """ # Regular expression to match ANSI style sequences (including 8-bit and 24-bit colors) ANSI_STYLE_RE = re.compile(r'\x1b\[[^m]*m') def strip_style(text: str) -> str: """ Strip ANSI style sequences from a string. :param text: string which may contain ANSI style sequences :return: the same string with any ANSI style sequences removed """ return ANSI_STYLE_RE.sub('', text) def style_aware_wcswidth(text: str) -> int: """ Wrap wcswidth to make it compatible with strings that contain ANSI style sequences. This is intended for single line strings. If text contains a newline, this function will return -1. For multiline strings, call widest_line() instead. :param text: the string being measured :return: The width of the string when printed to the terminal if no errors occur. If text contains characters with no absolute width (i.e. tabs), then this function returns -1. Replace tabs with spaces before calling this. """ # Strip ANSI style sequences since they cause wcswidth to return -1 return cast(int, wcswidth(strip_style(text))) def widest_line(text: str) -> int: """ Return the width of the widest line in a multiline string. This wraps style_aware_wcswidth() so it handles ANSI style sequences and has the same restrictions on non-printable characters. :param text: the string being measured :return: The width of the string when printed to the terminal if no errors occur. If text contains characters with no absolute width (i.e. tabs), then this function returns -1. Replace tabs with spaces before calling this. """ if not text: return 0 lines_widths = [style_aware_wcswidth(line) for line in text.splitlines()] if -1 in lines_widths: return -1 return max(lines_widths) def style_aware_write(fileobj: IO[str], msg: str) -> None: """ Write a string to a fileobject and strip its ANSI style sequences if required by allow_style setting :param fileobj: the file object being written to :param msg: the string being written """ if allow_style == AllowStyle.NEVER or (allow_style == AllowStyle.TERMINAL and not fileobj.isatty()): msg = strip_style(msg) fileobj.write(msg) #################################################################################### # Utility functions which create various ANSI sequences #################################################################################### def set_title(title: str) -> str: """ Generate a string that, when printed, sets a terminal's window title. :param title: new title for the window :return: the set title string """ return f"{OSC}2;{title}{BEL}" def clear_screen(clear_type: int = 2) -> str: """ Generate a string that, when printed, clears a terminal screen based on value of clear_type. :param clear_type: integer which specifies how to clear the screen (Defaults to 2) Possible values: 0 - clear from cursor to end of screen 1 - clear from cursor to beginning of the screen 2 - clear entire screen 3 - clear entire screen and delete all lines saved in the scrollback buffer :return: the clear screen string :raises: ValueError if clear_type is not a valid value """ if 0 <= clear_type <= 3: return f"{CSI}{clear_type}J" raise ValueError("clear_type must in an integer from 0 to 3") def clear_line(clear_type: int = 2) -> str: """ Generate a string that, when printed, clears a line based on value of clear_type. :param clear_type: integer which specifies how to clear the line (Defaults to 2) Possible values: 0 - clear from cursor to the end of the line 1 - clear from cursor to beginning of the line 2 - clear entire line :return: the clear line string :raises: ValueError if clear_type is not a valid value """ if 0 <= clear_type <= 2: return f"{CSI}{clear_type}K" raise ValueError("clear_type must in an integer from 0 to 2") #################################################################################### # Base classes which are not intended to be used directly #################################################################################### class AnsiSequence: """Base class to create ANSI sequence strings""" def __add__(self, other: Any) -> str: """ Support building an ANSI sequence string when self is the left operand e.g. Fg.LIGHT_MAGENTA + "hello" """ return str(self) + str(other) def __radd__(self, other: Any) -> str: """ Support building an ANSI sequence string when self is the right operand e.g. "hello" + Fg.RESET """ return str(other) + str(self) class FgColor(AnsiSequence): """Base class for ANSI Sequences which set foreground text color""" pass class BgColor(AnsiSequence): """Base class for ANSI Sequences which set background text color""" pass #################################################################################### # Implementations intended for direct use #################################################################################### # noinspection PyPep8Naming class Cursor: """Create ANSI sequences to alter the cursor position""" @staticmethod def UP(count: int = 1) -> str: """Move the cursor up a specified amount of lines (Defaults to 1)""" return f"{CSI}{count}A" @staticmethod def DOWN(count: int = 1) -> str: """Move the cursor down a specified amount of lines (Defaults to 1)""" return f"{CSI}{count}B" @staticmethod def FORWARD(count: int = 1) -> str: """Move the cursor forward a specified amount of lines (Defaults to 1)""" return f"{CSI}{count}C" @staticmethod def BACK(count: int = 1) -> str: """Move the cursor back a specified amount of lines (Defaults to 1)""" return f"{CSI}{count}D" @staticmethod def SET_POS(x: int, y: int) -> str: """Set the cursor position to coordinates which are 1-based""" return f"{CSI}{y};{x}H" class TextStyle(AnsiSequence, Enum): """Create text style ANSI sequences""" # Resets all styles and colors of text RESET_ALL = 0 INTENSITY_BOLD = 1 INTENSITY_DIM = 2 INTENSITY_NORMAL = 22 ITALIC_ENABLE = 3 ITALIC_DISABLE = 23 OVERLINE_ENABLE = 53 OVERLINE_DISABLE = 55 STRIKETHROUGH_ENABLE = 9 STRIKETHROUGH_DISABLE = 29 UNDERLINE_ENABLE = 4 UNDERLINE_DISABLE = 24 def __str__(self) -> str: """ Return ANSI text style sequence instead of enum name This is helpful when using a TextStyle in an f-string or format() call e.g. my_str = f"{TextStyle.UNDERLINE_ENABLE}hello{TextStyle.UNDERLINE_DISABLE}" """ return f"{CSI}{self.value}m" class Fg(FgColor, Enum): """ Create ANSI sequences for the 16 standard terminal foreground text colors. A terminal's color settings affect how these colors appear. To reset any foreground color, use Fg.RESET. """ BLACK = 30 RED = 31 GREEN = 32 YELLOW = 33 BLUE = 34 MAGENTA = 35 CYAN = 36 LIGHT_GRAY = 37 DARK_GRAY = 90 LIGHT_RED = 91 LIGHT_GREEN = 92 LIGHT_YELLOW = 93 LIGHT_BLUE = 94 LIGHT_MAGENTA = 95 LIGHT_CYAN = 96 WHITE = 97 RESET = 39 def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using an Fg in an f-string or format() call e.g. my_str = f"{Fg.BLUE}hello{Fg.RESET}" """ return f"{CSI}{self.value}m" class Bg(BgColor, Enum): """ Create ANSI sequences for the 16 standard terminal background text colors. A terminal's color settings affect how these colors appear. To reset any background color, use Bg.RESET. """ BLACK = 40 RED = 41 GREEN = 42 YELLOW = 44 BLUE = 44 MAGENTA = 45 CYAN = 46 LIGHT_GRAY = 47 DARK_GRAY = 100 LIGHT_RED = 101 LIGHT_GREEN = 102 LIGHT_YELLOW = 103 LIGHT_BLUE = 104 LIGHT_MAGENTA = 105 LIGHT_CYAN = 106 WHITE = 107 RESET = 49 def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using a Bg in an f-string or format() call e.g. my_str = f"{Bg.BLACK}hello{Bg.RESET}" """ return f"{CSI}{self.value}m" class EightBitFg(FgColor, Enum): """ Create ANSI sequences for 8-bit terminal foreground text colors. Most terminals support 8-bit/256-color mode. The first 16 colors correspond to the 16 colors from Fg and behave the same way. To reset any foreground color, including 8-bit, use Fg.RESET. """ BLACK = 0 RED = 1 GREEN = 2 YELLOW = 3 BLUE = 4 MAGENTA = 5 CYAN = 6 LIGHT_GRAY = 7 DARK_GRAY = 8 LIGHT_RED = 9 LIGHT_GREEN = 10 LIGHT_YELLOW = 11 LIGHT_BLUE = 12 LIGHT_MAGENTA = 13 LIGHT_CYAN = 14 WHITE = 15 GRAY_0 = 16 NAVY_BLUE = 17 DARK_BLUE = 18 BLUE_3A = 19 BLUE_3B = 20 BLUE_1 = 21 DARK_GREEN = 22 DEEP_SKY_BLUE_4A = 23 DEEP_SKY_BLUE_4B = 24 DEEP_SKY_BLUE_4C = 25 DODGER_BLUE_3 = 26 DODGER_BLUE_2 = 27 GREEN_4 = 28 SPRING_GREEN_4 = 29 TURQUOISE_4 = 30 DEEP_SKY_BLUE_3A = 31 DEEP_SKY_BLUE_3B = 32 DODGER_BLUE_1 = 33 GREEN_3A = 34 SPRING_GREEN_3A = 35 DARK_CYAN = 36 LIGHT_SEA_GREEN = 37 DEEP_SKY_BLUE_2 = 38 DEEP_SKY_BLUE_1 = 39 GREEN_3B = 40 SPRING_GREEN_3B = 41 SPRING_GREEN_2A = 42 CYAN_3 = 43 DARK_TURQUOISE = 44 TURQUOISE_2 = 45 GREEN_1 = 46 SPRING_GREEN_2B = 47 SPRING_GREEN_1 = 48 MEDIUM_SPRING_GREEN = 49 CYAN_2 = 50 CYAN_1 = 51 DARK_RED_1 = 52 DEEP_PINK_4A = 53 PURPLE_4A = 54 PURPLE_4B = 55 PURPLE_3 = 56 BLUE_VIOLET = 57 ORANGE_4A = 58 GRAY_37 = 59 MEDIUM_PURPLE_4 = 60 SLATE_BLUE_3A = 61 SLATE_BLUE_3B = 62 ROYAL_BLUE_1 = 63 CHARTREUSE_4 = 64 DARK_SEA_GREEN_4A = 65 PALE_TURQUOISE_4 = 66 STEEL_BLUE = 67 STEEL_BLUE_3 = 68 CORNFLOWER_BLUE = 69 CHARTREUSE_3A = 70 DARK_SEA_GREEN_4B = 71 CADET_BLUE_2 = 72 CADET_BLUE_1 = 73 SKY_BLUE_3 = 74 STEEL_BLUE_1A = 75 CHARTREUSE_3B = 76 PALE_GREEN_3A = 77 SEA_GREEN_3 = 78 AQUAMARINE_3 = 79 MEDIUM_TURQUOISE = 80 STEEL_BLUE_1B = 81 CHARTREUSE_2A = 82 SEA_GREEN_2 = 83 SEA_GREEN_1A = 84 SEA_GREEN_1B = 85 AQUAMARINE_1A = 86 DARK_SLATE_GRAY_2 = 87 DARK_RED_2 = 88 DEEP_PINK_4B = 89 DARK_MAGENTA_1 = 90 DARK_MAGENTA_2 = 91 DARK_VIOLET_1A = 92 PURPLE_1A = 93 ORANGE_4B = 94 LIGHT_PINK_4 = 95 PLUM_4 = 96 MEDIUM_PURPLE_3A = 97 MEDIUM_PURPLE_3B = 98 SLATE_BLUE_1 = 99 YELLOW_4A = 100 WHEAT_4 = 101 GRAY_53 = 102 LIGHT_SLATE_GRAY = 103 MEDIUM_PURPLE = 104 LIGHT_SLATE_BLUE = 105 YELLOW_4B = 106 DARK_OLIVE_GREEN_3A = 107 DARK_GREEN_SEA = 108 LIGHT_SKY_BLUE_3A = 109 LIGHT_SKY_BLUE_3B = 110 SKY_BLUE_2 = 111 CHARTREUSE_2B = 112 DARK_OLIVE_GREEN_3B = 113 PALE_GREEN_3B = 114 DARK_SEA_GREEN_3A = 115 DARK_SLATE_GRAY_3 = 116 SKY_BLUE_1 = 117 CHARTREUSE_1 = 118 LIGHT_GREEN_2 = 119 LIGHT_GREEN_3 = 120 PALE_GREEN_1A = 121 AQUAMARINE_1B = 122 DARK_SLATE_GRAY_1 = 123 RED_3A = 124 DEEP_PINK_4C = 125 MEDIUM_VIOLET_RED = 126 MAGENTA_3A = 127 DARK_VIOLET_1B = 128 PURPLE_1B = 129 DARK_ORANGE_3A = 130 INDIAN_RED_1A = 131 HOT_PINK_3A = 132 MEDIUM_ORCHID_3 = 133 MEDIUM_ORCHID = 134 MEDIUM_PURPLE_2A = 135 DARK_GOLDENROD = 136 LIGHT_SALMON_3A = 137 ROSY_BROWN = 138 GRAY_63 = 139 MEDIUM_PURPLE_2B = 140 MEDIUM_PURPLE_1 = 141 GOLD_3A = 142 DARK_KHAKI = 143 NAVAJO_WHITE_3 = 144 GRAY_69 = 145 LIGHT_STEEL_BLUE_3 = 146 LIGHT_STEEL_BLUE = 147 YELLOW_3A = 148 DARK_OLIVE_GREEN_3 = 149 DARK_SEA_GREEN_3B = 150 DARK_SEA_GREEN_2 = 151 LIGHT_CYAN_3 = 152 LIGHT_SKY_BLUE_1 = 153 GREEN_YELLOW = 154 DARK_OLIVE_GREEN_2 = 155 PALE_GREEN_1B = 156 DARK_SEA_GREEN_5B = 157 DARK_SEA_GREEN_5A = 158 PALE_TURQUOISE_1 = 159 RED_3B = 160 DEEP_PINK_3A = 161 DEEP_PINK_3B = 162 MAGENTA_3B = 163 MAGENTA_3C = 164 MAGENTA_2A = 165 DARK_ORANGE_3B = 166 INDIAN_RED_1B = 167 HOT_PINK_3B = 168 HOT_PINK_2 = 169 ORCHID = 170 MEDIUM_ORCHID_1A = 171 ORANGE_3 = 172 LIGHT_SALMON_3B = 173 LIGHT_PINK_3 = 174 PINK_3 = 175 PLUM_3 = 176 VIOLET = 177 GOLD_3B = 178 LIGHT_GOLDENROD_3 = 179 TAN = 180 MISTY_ROSE_3 = 181 THISTLE_3 = 182 PLUM_2 = 183 YELLOW_3B = 184 KHAKI_3 = 185 LIGHT_GOLDENROD_2A = 186 LIGHT_YELLOW_3 = 187 GRAY_84 = 188 LIGHT_STEEL_BLUE_1 = 189 YELLOW_2 = 190 DARK_OLIVE_GREEN_1A = 191 DARK_OLIVE_GREEN_1B = 192 DARK_SEA_GREEN_1 = 193 HONEYDEW_2 = 194 LIGHT_CYAN_1 = 195 RED_1 = 196 DEEP_PINK_2 = 197 DEEP_PINK_1A = 198 DEEP_PINK_1B = 199 MAGENTA_2B = 200 MAGENTA_1 = 201 ORANGE_RED_1 = 202 INDIAN_RED_1C = 203 INDIAN_RED_1D = 204 HOT_PINK_1A = 205 HOT_PINK_1B = 206 MEDIUM_ORCHID_1B = 207 DARK_ORANGE = 208 SALMON_1 = 209 LIGHT_CORAL = 210 PALE_VIOLET_RED_1 = 211 ORCHID_2 = 212 ORCHID_1 = 213 ORANGE_1 = 214 SANDY_BROWN = 215 LIGHT_SALMON_1 = 216 LIGHT_PINK_1 = 217 PINK_1 = 218 PLUM_1 = 219 GOLD_1 = 220 LIGHT_GOLDENROD_2B = 221 LIGHT_GOLDENROD_2C = 222 NAVAJO_WHITE_1 = 223 MISTY_ROSE1 = 224 THISTLE_1 = 225 YELLOW_1 = 226 LIGHT_GOLDENROD_1 = 227 KHAKI_1 = 228 WHEAT_1 = 229 CORNSILK_1 = 230 GRAY_100 = 231 GRAY_3 = 232 GRAY_7 = 233 GRAY_11 = 234 GRAY_15 = 235 GRAY_19 = 236 GRAY_23 = 237 GRAY_27 = 238 GRAY_30 = 239 GRAY_35 = 240 GRAY_39 = 241 GRAY_42 = 242 GRAY_46 = 243 GRAY_50 = 244 GRAY_54 = 245 GRAY_58 = 246 GRAY_62 = 247 GRAY_66 = 248 GRAY_70 = 249 GRAY_74 = 250 GRAY_78 = 251 GRAY_82 = 252 GRAY_85 = 253 GRAY_89 = 254 GRAY_93 = 255 def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using an EightBitFg in an f-string or format() call e.g. my_str = f"{EightBitFg.SLATE_BLUE_1}hello{Fg.RESET}" """ return f"{CSI}{38};5;{self.value}m" class EightBitBg(BgColor, Enum): """ Create ANSI sequences for 8-bit terminal background text colors. Most terminals support 8-bit/256-color mode. The first 16 colors correspond to the 16 colors from Bg and behave the same way. To reset any background color, including 8-bit, use Bg.RESET. """ BLACK = 0 RED = 1 GREEN = 2 YELLOW = 3 BLUE = 4 MAGENTA = 5 CYAN = 6 LIGHT_GRAY = 7 DARK_GRAY = 8 LIGHT_RED = 9 LIGHT_GREEN = 10 LIGHT_YELLOW = 11 LIGHT_BLUE = 12 LIGHT_MAGENTA = 13 LIGHT_CYAN = 14 WHITE = 15 GRAY_0 = 16 NAVY_BLUE = 17 DARK_BLUE = 18 BLUE_3A = 19 BLUE_3B = 20 BLUE_1 = 21 DARK_GREEN = 22 DEEP_SKY_BLUE_4A = 23 DEEP_SKY_BLUE_4B = 24 DEEP_SKY_BLUE_4C = 25 DODGER_BLUE_3 = 26 DODGER_BLUE_2 = 27 GREEN_4 = 28 SPRING_GREEN_4 = 29 TURQUOISE_4 = 30 DEEP_SKY_BLUE_3A = 31 DEEP_SKY_BLUE_3B = 32 DODGER_BLUE_1 = 33 GREEN_3A = 34 SPRING_GREEN_3A = 35 DARK_CYAN = 36 LIGHT_SEA_GREEN = 37 DEEP_SKY_BLUE_2 = 38 DEEP_SKY_BLUE_1 = 39 GREEN_3B = 40 SPRING_GREEN_3B = 41 SPRING_GREEN_2A = 42 CYAN_3 = 43 DARK_TURQUOISE = 44 TURQUOISE_2 = 45 GREEN_1 = 46 SPRING_GREEN_2B = 47 SPRING_GREEN_1 = 48 MEDIUM_SPRING_GREEN = 49 CYAN_2 = 50 CYAN_1 = 51 DARK_RED_1 = 52 DEEP_PINK_4A = 53 PURPLE_4A = 54 PURPLE_4B = 55 PURPLE_3 = 56 BLUE_VIOLET = 57 ORANGE_4A = 58 GRAY_37 = 59 MEDIUM_PURPLE_4 = 60 SLATE_BLUE_3A = 61 SLATE_BLUE_3B = 62 ROYAL_BLUE_1 = 63 CHARTREUSE_4 = 64 DARK_SEA_GREEN_4A = 65 PALE_TURQUOISE_4 = 66 STEEL_BLUE = 67 STEEL_BLUE_3 = 68 CORNFLOWER_BLUE = 69 CHARTREUSE_3A = 70 DARK_SEA_GREEN_4B = 71 CADET_BLUE_2 = 72 CADET_BLUE_1 = 73 SKY_BLUE_3 = 74 STEEL_BLUE_1A = 75 CHARTREUSE_3B = 76 PALE_GREEN_3A = 77 SEA_GREEN_3 = 78 AQUAMARINE_3 = 79 MEDIUM_TURQUOISE = 80 STEEL_BLUE_1B = 81 CHARTREUSE_2A = 82 SEA_GREEN_2 = 83 SEA_GREEN_1A = 84 SEA_GREEN_1B = 85 AQUAMARINE_1A = 86 DARK_SLATE_GRAY_2 = 87 DARK_RED_2 = 88 DEEP_PINK_4B = 89 DARK_MAGENTA_1 = 90 DARK_MAGENTA_2 = 91 DARK_VIOLET_1A = 92 PURPLE_1A = 93 ORANGE_4B = 94 LIGHT_PINK_4 = 95 PLUM_4 = 96 MEDIUM_PURPLE_3A = 97 MEDIUM_PURPLE_3B = 98 SLATE_BLUE_1 = 99 YELLOW_4A = 100 WHEAT_4 = 101 GRAY_53 = 102 LIGHT_SLATE_GRAY = 103 MEDIUM_PURPLE = 104 LIGHT_SLATE_BLUE = 105 YELLOW_4B = 106 DARK_OLIVE_GREEN_3A = 107 DARK_GREEN_SEA = 108 LIGHT_SKY_BLUE_3A = 109 LIGHT_SKY_BLUE_3B = 110 SKY_BLUE_2 = 111 CHARTREUSE_2B = 112 DARK_OLIVE_GREEN_3B = 113 PALE_GREEN_3B = 114 DARK_SEA_GREEN_3A = 115 DARK_SLATE_GRAY_3 = 116 SKY_BLUE_1 = 117 CHARTREUSE_1 = 118 LIGHT_GREEN_2 = 119 LIGHT_GREEN_3 = 120 PALE_GREEN_1A = 121 AQUAMARINE_1B = 122 DARK_SLATE_GRAY_1 = 123 RED_3A = 124 DEEP_PINK_4C = 125 MEDIUM_VIOLET_RED = 126 MAGENTA_3A = 127 DARK_VIOLET_1B = 128 PURPLE_1B = 129 DARK_ORANGE_3A = 130 INDIAN_RED_1A = 131 HOT_PINK_3A = 132 MEDIUM_ORCHID_3 = 133 MEDIUM_ORCHID = 134 MEDIUM_PURPLE_2A = 135 DARK_GOLDENROD = 136 LIGHT_SALMON_3A = 137 ROSY_BROWN = 138 GRAY_63 = 139 MEDIUM_PURPLE_2B = 140 MEDIUM_PURPLE_1 = 141 GOLD_3A = 142 DARK_KHAKI = 143 NAVAJO_WHITE_3 = 144 GRAY_69 = 145 LIGHT_STEEL_BLUE_3 = 146 LIGHT_STEEL_BLUE = 147 YELLOW_3A = 148 DARK_OLIVE_GREEN_3 = 149 DARK_SEA_GREEN_3B = 150 DARK_SEA_GREEN_2 = 151 LIGHT_CYAN_3 = 152 LIGHT_SKY_BLUE_1 = 153 GREEN_YELLOW = 154 DARK_OLIVE_GREEN_2 = 155 PALE_GREEN_1B = 156 DARK_SEA_GREEN_5B = 157 DARK_SEA_GREEN_5A = 158 PALE_TURQUOISE_1 = 159 RED_3B = 160 DEEP_PINK_3A = 161 DEEP_PINK_3B = 162 MAGENTA_3B = 163 MAGENTA_3C = 164 MAGENTA_2A = 165 DARK_ORANGE_3B = 166 INDIAN_RED_1B = 167 HOT_PINK_3B = 168 HOT_PINK_2 = 169 ORCHID = 170 MEDIUM_ORCHID_1A = 171 ORANGE_3 = 172 LIGHT_SALMON_3B = 173 LIGHT_PINK_3 = 174 PINK_3 = 175 PLUM_3 = 176 VIOLET = 177 GOLD_3B = 178 LIGHT_GOLDENROD_3 = 179 TAN = 180 MISTY_ROSE_3 = 181 THISTLE_3 = 182 PLUM_2 = 183 YELLOW_3B = 184 KHAKI_3 = 185 LIGHT_GOLDENROD_2A = 186 LIGHT_YELLOW_3 = 187 GRAY_84 = 188 LIGHT_STEEL_BLUE_1 = 189 YELLOW_2 = 190 DARK_OLIVE_GREEN_1A = 191 DARK_OLIVE_GREEN_1B = 192 DARK_SEA_GREEN_1 = 193 HONEYDEW_2 = 194 LIGHT_CYAN_1 = 195 RED_1 = 196 DEEP_PINK_2 = 197 DEEP_PINK_1A = 198 DEEP_PINK_1B = 199 MAGENTA_2B = 200 MAGENTA_1 = 201 ORANGE_RED_1 = 202 INDIAN_RED_1C = 203 INDIAN_RED_1D = 204 HOT_PINK_1A = 205 HOT_PINK_1B = 206 MEDIUM_ORCHID_1B = 207 DARK_ORANGE = 208 SALMON_1 = 209 LIGHT_CORAL = 210 PALE_VIOLET_RED_1 = 211 ORCHID_2 = 212 ORCHID_1 = 213 ORANGE_1 = 214 SANDY_BROWN = 215 LIGHT_SALMON_1 = 216 LIGHT_PINK_1 = 217 PINK_1 = 218 PLUM_1 = 219 GOLD_1 = 220 LIGHT_GOLDENROD_2B = 221 LIGHT_GOLDENROD_2C = 222 NAVAJO_WHITE_1 = 223 MISTY_ROSE1 = 224 THISTLE_1 = 225 YELLOW_1 = 226 LIGHT_GOLDENROD_1 = 227 KHAKI_1 = 228 WHEAT_1 = 229 CORNSILK_1 = 230 GRAY_100 = 231 GRAY_3 = 232 GRAY_7 = 233 GRAY_11 = 234 GRAY_15 = 235 GRAY_19 = 236 GRAY_23 = 237 GRAY_27 = 238 GRAY_30 = 239 GRAY_35 = 240 GRAY_39 = 241 GRAY_42 = 242 GRAY_46 = 243 GRAY_50 = 244 GRAY_54 = 245 GRAY_58 = 246 GRAY_62 = 247 GRAY_66 = 248 GRAY_70 = 249 GRAY_74 = 250 GRAY_78 = 251 GRAY_82 = 252 GRAY_85 = 253 GRAY_89 = 254 GRAY_93 = 255 def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using an EightBitBg in an f-string or format() call e.g. my_str = f"{EightBitBg.KHAKI_3}hello{Bg.RESET}" """ return f"{CSI}{48};5;{self.value}m" class RgbFg(FgColor): """ Create ANSI sequences for 24-bit (RGB) terminal foreground text colors. The terminal must support 24-bit/true-color mode. To reset any foreground color, including 24-bit, use Fg.RESET. """ def __init__(self, r: int, g: int, b: int) -> None: """ RgbFg initializer :param r: integer from 0-255 for the red component of the color :param g: integer from 0-255 for the green component of the color :param b: integer from 0-255 for the blue component of the color :raises: ValueError if r, g, or b is not in the range 0-255 """ if any(c < 0 or c > 255 for c in [r, g, b]): raise ValueError("RGB values must be integers in the range of 0 to 255") self._sequence = f"{CSI}{38};2;{r};{g};{b}m" def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using an RgbFg in an f-string or format() call e.g. my_str = f"{RgbFg(0, 55, 100)}hello{Fg.RESET}" """ return self._sequence class RgbBg(BgColor): """ Create ANSI sequences for 24-bit (RGB) terminal background text colors. The terminal must support 24-bit/true-color mode. To reset any background color, including 24-bit, use Bg.RESET. """ def __init__(self, r: int, g: int, b: int) -> None: """ RgbBg initializer :param r: integer from 0-255 for the red component of the color :param g: integer from 0-255 for the green component of the color :param b: integer from 0-255 for the blue component of the color :raises: ValueError if r, g, or b is not in the range 0-255 """ if any(c < 0 or c > 255 for c in [r, g, b]): raise ValueError("RGB values must be integers in the range of 0 to 255") self._sequence = f"{CSI}{48};2;{r};{g};{b}m" def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using an RgbBg in an f-string or format() call e.g. my_str = f"{RgbBg(100, 255, 27)}hello{Bg.RESET}" """ return self._sequence # TODO: Remove this PyShadowingNames usage when deprecated fg and bg classes are removed. # noinspection PyShadowingNames def style( value: Any, *, fg: Optional[FgColor] = None, bg: Optional[BgColor] = None, bold: Optional[bool] = None, dim: Optional[bool] = None, italic: Optional[bool] = None, overline: Optional[bool] = None, strikethrough: Optional[bool] = None, underline: Optional[bool] = None, ) -> str: """ Apply ANSI colors and/or styles to a string and return it. The styling is self contained which means that at the end of the string reset code(s) are issued to undo whatever styling was done at the beginning. :param value: object whose text is to be styled :param fg: foreground color provided as any subclass of FgColor (e.g. Fg, EightBitFg, RgbFg) Defaults to no color. :param bg: foreground color provided as any subclass of BgColor (e.g. Bg, EightBitBg, RgbBg) Defaults to no color. :param bold: apply the bold style if True. Defaults to False. :param dim: apply the dim style if True. Defaults to False. :param italic: apply the italic style if True. Defaults to False. :param overline: apply the overline style if True. Defaults to False. :param strikethrough: apply the strikethrough style if True. Defaults to False. :param underline: apply the underline style if True. Defaults to False. :raises: TypeError if fg isn't None or a subclass of FgColor :raises: TypeError if bg isn't None or a subclass of BgColor :return: the stylized string """ # List of strings that add style additions: List[AnsiSequence] = [] # List of strings that remove style removals: List[AnsiSequence] = [] # Process the style settings if fg is not None: if not isinstance(fg, FgColor): raise TypeError("fg must be a subclass of FgColor") additions.append(fg) removals.append(Fg.RESET) if bg is not None: if not isinstance(bg, BgColor): raise TypeError("bg must a subclass of BgColor") additions.append(bg) removals.append(Bg.RESET) if bold: additions.append(TextStyle.INTENSITY_BOLD) removals.append(TextStyle.INTENSITY_NORMAL) if dim: additions.append(TextStyle.INTENSITY_DIM) removals.append(TextStyle.INTENSITY_NORMAL) if italic: additions.append(TextStyle.ITALIC_ENABLE) removals.append(TextStyle.ITALIC_DISABLE) if overline: additions.append(TextStyle.OVERLINE_ENABLE) removals.append(TextStyle.OVERLINE_DISABLE) if strikethrough: additions.append(TextStyle.STRIKETHROUGH_ENABLE) removals.append(TextStyle.STRIKETHROUGH_DISABLE) if underline: additions.append(TextStyle.UNDERLINE_ENABLE) removals.append(TextStyle.UNDERLINE_DISABLE) # Combine the ANSI style sequences with the value's text return "".join(map(str, additions)) + str(value) + "".join(map(str, removals)) # Default styles for printing strings of various types. # These can be altered to suit an application's needs and only need to be a # function with the following structure: func(str) -> str style_success = functools.partial(style, fg=Fg.GREEN) """Partial function supplying arguments to :meth:`cmd2.ansi.style()` which colors text to signify success""" style_warning = functools.partial(style, fg=Fg.LIGHT_YELLOW) """Partial function supplying arguments to :meth:`cmd2.ansi.style()` which colors text to signify a warning""" style_error = functools.partial(style, fg=Fg.LIGHT_RED) """Partial function supplying arguments to :meth:`cmd2.ansi.style()` which colors text to signify an error""" def async_alert_str(*, terminal_columns: int, prompt: str, line: str, cursor_offset: int, alert_msg: str) -> str: """Calculate the desired string, including ANSI escape codes, for displaying an asynchronous alert message. :param terminal_columns: terminal width (number of columns) :param prompt: prompt that is displayed on the current line :param line: current contents of the Readline line buffer :param cursor_offset: the offset of the current cursor position within line :param alert_msg: the message to display to the user :return: the correct string so that the alert message appears to the user to be printed above the current line. """ # Split the prompt lines since it can contain newline characters. prompt_lines = prompt.splitlines() # Calculate how many terminal lines are taken up by all prompt lines except for the last one. # That will be included in the input lines calculations since that is where the cursor is. num_prompt_terminal_lines = 0 for line in prompt_lines[:-1]: line_width = style_aware_wcswidth(line) num_prompt_terminal_lines += int(line_width / terminal_columns) + 1 # Now calculate how many terminal lines are take up by the input last_prompt_line = prompt_lines[-1] last_prompt_line_width = style_aware_wcswidth(last_prompt_line) input_width = last_prompt_line_width + style_aware_wcswidth(line) num_input_terminal_lines = int(input_width / terminal_columns) + 1 # Get the cursor's offset from the beginning of the first input line cursor_input_offset = last_prompt_line_width + cursor_offset # Calculate what input line the cursor is on cursor_input_line = int(cursor_input_offset / terminal_columns) + 1 # Create a string that when printed will clear all input lines and display the alert terminal_str = '' # Move the cursor down to the last input line if cursor_input_line != num_input_terminal_lines: terminal_str += Cursor.DOWN(num_input_terminal_lines - cursor_input_line) # Clear each line from the bottom up so that the cursor ends up on the first prompt line total_lines = num_prompt_terminal_lines + num_input_terminal_lines terminal_str += (clear_line() + Cursor.UP(1)) * (total_lines - 1) # Clear the first prompt line terminal_str += clear_line() # Move the cursor to the beginning of the first prompt line and print the alert terminal_str += '\r' + alert_msg return terminal_str #################################################################################### # The following classes are deprecated. #################################################################################### # noinspection PyPep8Naming class fg(FgColor, Enum): """Deprecated Enum class for foreground colors. Use Fg instead.""" black = 30 red = 31 green = 32 yellow = 33 blue = 34 magenta = 35 cyan = 36 white = 37 reset = 39 bright_black = 90 bright_red = 91 bright_green = 92 bright_yellow = 93 bright_blue = 94 bright_magenta = 95 bright_cyan = 96 bright_white = 97 def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using an fg in an f-string or format() call e.g. my_str = f"{fg.blue}hello{fg.reset}" """ return f"{CSI}{self.value}m" # noinspection PyPep8Naming class bg(BgColor, Enum): """Deprecated Enum class for background colors. Use Bg instead.""" black = 40 red = 41 green = 42 yellow = 43 blue = 44 magenta = 45 cyan = 46 white = 47 reset = 49 bright_black = 100 bright_red = 101 bright_green = 102 bright_yellow = 103 bright_blue = 104 bright_magenta = 105 bright_cyan = 106 bright_white = 107 def __str__(self) -> str: """ Return ANSI color sequence instead of enum name This is helpful when using a bg in an f-string or format() call e.g. my_str = f"{bg.black}hello{bg.reset}" """ return f"{CSI}{self.value}m" cmd2-2.3.3/cmd2/argparse_completer.py000066400000000000000000001070521416142110700174230ustar00rootroot00000000000000# coding=utf-8 # flake8: noqa C901 # NOTE: Ignoring flake8 cyclomatic complexity in this file """ This module defines the ArgparseCompleter class which provides argparse-based tab completion to cmd2 apps. See the header of argparse_custom.py for instructions on how to use these features. """ import argparse import inspect import numbers from collections import ( deque, ) from typing import ( TYPE_CHECKING, Dict, List, Optional, Type, Union, cast, ) from .ansi import ( style_aware_wcswidth, widest_line, ) from .constants import ( INFINITY, ) if TYPE_CHECKING: # pragma: no cover from .cmd2 import ( Cmd, ) from .argparse_custom import ( ChoicesCallable, ChoicesProviderFuncWithTokens, CompletionItem, generate_range_error, ) from .command_definition import ( CommandSet, ) from .exceptions import ( CompletionError, ) from .table_creator import ( Column, HorizontalAlignment, SimpleTable, ) # If no descriptive header is supplied, then this will be used instead DEFAULT_DESCRIPTIVE_HEADER = 'Description' # Name of the choice/completer function argument that, if present, will be passed a dictionary of # command line tokens up through the token being completed mapped to their argparse destination name. ARG_TOKENS = 'arg_tokens' # noinspection PyProtectedMember def _build_hint(parser: argparse.ArgumentParser, arg_action: argparse.Action) -> str: """Build tab completion hint for a given argument""" # Check if hinting is disabled for this argument suppress_hint = arg_action.get_suppress_tab_hint() # type: ignore[attr-defined] if suppress_hint or arg_action.help == argparse.SUPPRESS: return '' else: # Use the parser's help formatter to display just this action's help text formatter = parser._get_formatter() formatter.start_section("Hint") formatter.add_argument(arg_action) formatter.end_section() return formatter.format_help() def _single_prefix_char(token: str, parser: argparse.ArgumentParser) -> bool: """Returns if a token is just a single flag prefix character""" return len(token) == 1 and token[0] in parser.prefix_chars # noinspection PyProtectedMember def _looks_like_flag(token: str, parser: argparse.ArgumentParser) -> bool: """ Determine if a token looks like a flag. Unless an argument has nargs set to argparse.REMAINDER, then anything that looks like a flag can't be consumed as a value for it. Based on argparse._parse_optional(). """ # Flags have to be at least characters if len(token) < 2: return False # Flags have to start with a prefix character if not token[0] in parser.prefix_chars: return False # If it looks like a negative number, it is not a flag unless there are negative-number-like flags if parser._negative_number_matcher.match(token): if not parser._has_negative_number_optionals: return False # Flags can't have a space if ' ' in token: return False # Starts like a flag return True class _ArgumentState: """Keeps state of an argument being parsed""" def __init__(self, arg_action: argparse.Action) -> None: self.action = arg_action self.min: Union[int, str] self.max: Union[float, int, str] self.count = 0 self.is_remainder = self.action.nargs == argparse.REMAINDER # Check if nargs is a range nargs_range = self.action.get_nargs_range() # type: ignore[attr-defined] if nargs_range is not None: self.min = nargs_range[0] self.max = nargs_range[1] # Otherwise check against argparse types elif self.action.nargs is None: self.min = 1 self.max = 1 elif self.action.nargs == argparse.OPTIONAL: self.min = 0 self.max = 1 elif self.action.nargs == argparse.ZERO_OR_MORE or self.action.nargs == argparse.REMAINDER: self.min = 0 self.max = INFINITY elif self.action.nargs == argparse.ONE_OR_MORE: self.min = 1 self.max = INFINITY else: self.min = self.action.nargs self.max = self.action.nargs # noinspection PyProtectedMember class _UnfinishedFlagError(CompletionError): def __init__(self, flag_arg_state: _ArgumentState) -> None: """ CompletionError which occurs when the user has not finished the current flag :param flag_arg_state: information about the unfinished flag action """ error = "Error: argument {}: {} ({} entered)".format( argparse._get_action_name(flag_arg_state.action), generate_range_error(cast(int, flag_arg_state.min), cast(Union[int, float], flag_arg_state.max)), flag_arg_state.count, ) super().__init__(error) class _NoResultsError(CompletionError): def __init__(self, parser: argparse.ArgumentParser, arg_action: argparse.Action) -> None: """ CompletionError which occurs when there are no results. If hinting is allowed, then its message will be a hint about the argument being tab completed. :param parser: ArgumentParser instance which owns the action being tab completed :param arg_action: action being tab completed """ # Set apply_style to False because we don't want hints to look like errors super().__init__(_build_hint(parser, arg_action), apply_style=False) # noinspection PyProtectedMember class ArgparseCompleter: """Automatic command line tab completion based on argparse parameters""" def __init__( self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: Optional[Dict[str, List[str]]] = None ) -> None: """ Create an ArgparseCompleter :param parser: ArgumentParser instance :param cmd2_app: reference to the Cmd2 application that owns this ArgparseCompleter :param parent_tokens: optional dictionary mapping parent parsers' arg names to their tokens This is only used by ArgparseCompleter when recursing on subcommand parsers Defaults to None """ self._parser = parser self._cmd2_app = cmd2_app if parent_tokens is None: parent_tokens = dict() self._parent_tokens = parent_tokens self._flags = [] # all flags in this command self._flag_to_action = {} # maps flags to the argparse action object self._positional_actions = [] # actions for positional arguments (by position index) self._subcommand_action = None # this will be set if self._parser has subcommands # Start digging through the argparse structures. # _actions is the top level container of parameter definitions for action in self._parser._actions: # if the parameter is flag based, it will have option_strings if action.option_strings: # record each option flag for option in action.option_strings: self._flags.append(option) self._flag_to_action[option] = action # Otherwise this is a positional parameter else: self._positional_actions.append(action) # Check if this action defines subcommands if isinstance(action, argparse._SubParsersAction): self._subcommand_action = action def complete( self, text: str, line: str, begidx: int, endidx: int, tokens: List[str], *, cmd_set: Optional[CommandSet] = None ) -> List[str]: """ Complete text using argparse metadata :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param tokens: list of argument tokens being passed to the parser :param cmd_set: if tab completing a command, the CommandSet the command's function belongs to, if applicable. Defaults to None. :raises: CompletionError for various types of tab completion errors """ if not tokens: return [] # Positionals args that are left to parse remaining_positionals = deque(self._positional_actions) # This gets set to True when flags will no longer be processed as argparse flags # That can happen when -- is used or an argument with nargs=argparse.REMAINDER is used skip_remaining_flags = False # _ArgumentState of the current positional pos_arg_state: Optional[_ArgumentState] = None # _ArgumentState of the current flag flag_arg_state: Optional[_ArgumentState] = None # Non-reusable flags that we've parsed matched_flags: List[str] = [] # Keeps track of arguments we've seen and any tokens they consumed consumed_arg_values: Dict[str, List[str]] = dict() # dict(arg_name -> List[tokens]) # Completed mutually exclusive groups completed_mutex_groups: Dict[argparse._MutuallyExclusiveGroup, argparse.Action] = dict() def consume_argument(arg_state: _ArgumentState) -> None: """Consuming token as an argument""" arg_state.count += 1 consumed_arg_values.setdefault(arg_state.action.dest, []) consumed_arg_values[arg_state.action.dest].append(token) def update_mutex_groups(arg_action: argparse.Action) -> None: """ Check if an argument belongs to a mutually exclusive group and either mark that group as complete or print an error if the group has already been completed :param arg_action: the action of the argument :raises: CompletionError if the group is already completed """ # Check if this action is in a mutually exclusive group for group in self._parser._mutually_exclusive_groups: if arg_action in group._group_actions: # Check if the group this action belongs to has already been completed if group in completed_mutex_groups: # If this is the action that completed the group, then there is no error # since it's allowed to appear on the command line more than once. completer_action = completed_mutex_groups[group] if arg_action == completer_action: return error = "Error: argument {}: not allowed with argument {}".format( argparse._get_action_name(arg_action), argparse._get_action_name(completer_action) ) raise CompletionError(error) # Mark that this action completed the group completed_mutex_groups[group] = arg_action # Don't tab complete any of the other args in the group for group_action in group._group_actions: if group_action == arg_action: continue elif group_action in self._flag_to_action.values(): matched_flags.extend(group_action.option_strings) elif group_action in remaining_positionals: remaining_positionals.remove(group_action) # Arg can only be in one group, so we are done break ############################################################################################# # Parse all but the last token ############################################################################################# for token_index, token in enumerate(tokens[:-1]): # If we're in a positional REMAINDER arg, force all future tokens to go to that if pos_arg_state is not None and pos_arg_state.is_remainder: consume_argument(pos_arg_state) continue # If we're in a flag REMAINDER arg, force all future tokens to go to that until a double dash is hit elif flag_arg_state is not None and flag_arg_state.is_remainder: if token == '--': flag_arg_state = None else: consume_argument(flag_arg_state) continue # Handle '--' which tells argparse all remaining arguments are non-flags elif token == '--' and not skip_remaining_flags: # Check if there is an unfinished flag if ( flag_arg_state is not None and isinstance(flag_arg_state.min, int) and flag_arg_state.count < flag_arg_state.min ): raise _UnfinishedFlagError(flag_arg_state) # Otherwise end the current flag else: flag_arg_state = None skip_remaining_flags = True continue # Check the format of the current token to see if it can be an argument's value if _looks_like_flag(token, self._parser) and not skip_remaining_flags: # Check if there is an unfinished flag if ( flag_arg_state is not None and isinstance(flag_arg_state.min, int) and flag_arg_state.count < flag_arg_state.min ): raise _UnfinishedFlagError(flag_arg_state) # Reset flag arg state but not positional tracking because flags can be # interspersed anywhere between positionals flag_arg_state = None action = None # Does the token match a known flag? if token in self._flag_to_action: action = self._flag_to_action[token] elif self._parser.allow_abbrev: candidates_flags = [flag for flag in self._flag_to_action if flag.startswith(token)] if len(candidates_flags) == 1: action = self._flag_to_action[candidates_flags[0]] if action is not None: update_mutex_groups(action) if isinstance(action, (argparse._AppendAction, argparse._AppendConstAction, argparse._CountAction)): # Flags with action set to append, append_const, and count can be reused # Therefore don't erase any tokens already consumed for this flag consumed_arg_values.setdefault(action.dest, []) else: # This flag is not reusable, so mark that we've seen it matched_flags.extend(action.option_strings) # It's possible we already have consumed values for this flag if it was used # earlier in the command line. Reset them now for this use of it. consumed_arg_values[action.dest] = [] new_arg_state = _ArgumentState(action) # Keep track of this flag if it can receive arguments if new_arg_state.max > 0: # type: ignore[operator] flag_arg_state = new_arg_state skip_remaining_flags = flag_arg_state.is_remainder # Check if we are consuming a flag elif flag_arg_state is not None: consume_argument(flag_arg_state) # Check if we have finished with this flag if isinstance(flag_arg_state.max, (float, int)) and flag_arg_state.count >= flag_arg_state.max: flag_arg_state = None # Otherwise treat as a positional argument else: # If we aren't current tracking a positional, then get the next positional arg to handle this token if pos_arg_state is None: # Make sure we are still have positional arguments to parse if remaining_positionals: action = remaining_positionals.popleft() # Are we at a subcommand? If so, forward to the matching completer if action == self._subcommand_action: if token in self._subcommand_action.choices: # Merge self._parent_tokens and consumed_arg_values parent_tokens = {**self._parent_tokens, **consumed_arg_values} # Include the subcommand name if its destination was set if action.dest != argparse.SUPPRESS: parent_tokens[action.dest] = [token] parser: argparse.ArgumentParser = self._subcommand_action.choices[token] completer_type = self._cmd2_app._determine_ap_completer_type(parser) completer = completer_type(parser, self._cmd2_app, parent_tokens=parent_tokens) return completer.complete( text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set ) else: # Invalid subcommand entered, so no way to complete remaining tokens return [] # Otherwise keep track of the argument else: pos_arg_state = _ArgumentState(action) # Check if we have a positional to consume this token if pos_arg_state is not None: update_mutex_groups(pos_arg_state.action) consume_argument(pos_arg_state) # No more flags are allowed if this is a REMAINDER argument if pos_arg_state.is_remainder: skip_remaining_flags = True # Check if we have finished with this positional elif isinstance(pos_arg_state.max, (float, int)) and pos_arg_state.count >= pos_arg_state.max: pos_arg_state = None # Check if the next positional has nargs set to argparse.REMAINDER. # At this point argparse allows no more flags to be processed. if remaining_positionals and remaining_positionals[0].nargs == argparse.REMAINDER: skip_remaining_flags = True ############################################################################################# # We have parsed all but the last token and have enough information to complete it ############################################################################################# # Check if we are completing a flag name. This check ignores strings with a length of one, like '-'. # This is because that could be the start of a negative number which may be a valid completion for # the current argument. We will handle the completion of flags that start with only one prefix # character (-f) at the end. if _looks_like_flag(text, self._parser) and not skip_remaining_flags: if ( flag_arg_state is not None and isinstance(flag_arg_state.min, int) and flag_arg_state.count < flag_arg_state.min ): raise _UnfinishedFlagError(flag_arg_state) return self._complete_flags(text, line, begidx, endidx, matched_flags) completion_results = [] # Check if we are completing a flag's argument if flag_arg_state is not None: completion_results = self._complete_arg( text, line, begidx, endidx, flag_arg_state, consumed_arg_values, cmd_set=cmd_set ) # If we have results, then return them if completion_results: # Don't overwrite an existing hint if not self._cmd2_app.completion_hint: self._cmd2_app.completion_hint = _build_hint(self._parser, flag_arg_state.action) return completion_results # Otherwise, print a hint if the flag isn't finished or text isn't possibly the start of a flag elif ( (isinstance(flag_arg_state.min, int) and flag_arg_state.count < flag_arg_state.min) or not _single_prefix_char(text, self._parser) or skip_remaining_flags ): raise _NoResultsError(self._parser, flag_arg_state.action) # Otherwise check if we have a positional to complete elif pos_arg_state is not None or remaining_positionals: # If we aren't current tracking a positional, then get the next positional arg to handle this token if pos_arg_state is None: action = remaining_positionals.popleft() pos_arg_state = _ArgumentState(action) completion_results = self._complete_arg( text, line, begidx, endidx, pos_arg_state, consumed_arg_values, cmd_set=cmd_set ) # If we have results, then return them if completion_results: # Don't overwrite an existing hint if not self._cmd2_app.completion_hint: self._cmd2_app.completion_hint = _build_hint(self._parser, pos_arg_state.action) return completion_results # Otherwise, print a hint if text isn't possibly the start of a flag elif not _single_prefix_char(text, self._parser) or skip_remaining_flags: raise _NoResultsError(self._parser, pos_arg_state.action) # If we aren't skipping remaining flags, then complete flag names if either is True: # 1. text is a single flag prefix character that didn't complete against any argument values # 2. there are no more positionals to complete if not skip_remaining_flags and (_single_prefix_char(text, self._parser) or not remaining_positionals): return self._complete_flags(text, line, begidx, endidx, matched_flags) return completion_results def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: List[str]) -> List[str]: """Tab completion routine for a parsers unused flags""" # Build a list of flags that can be tab completed match_against = [] for flag in self._flags: # Make sure this flag hasn't already been used if flag not in matched_flags: # Make sure this flag isn't considered hidden action = self._flag_to_action[flag] if action.help != argparse.SUPPRESS: match_against.append(flag) matches = self._cmd2_app.basic_complete(text, line, begidx, endidx, match_against) # Build a dictionary linking actions with their matched flag names matched_actions: Dict[argparse.Action, List[str]] = dict() for flag in matches: action = self._flag_to_action[flag] matched_actions.setdefault(action, []) matched_actions[action].append(flag) # For tab completion suggestions, group matched flags by action for action, option_strings in matched_actions.items(): flag_text = ', '.join(option_strings) # Mark optional flags with brackets if not action.required: flag_text = '[' + flag_text + ']' self._cmd2_app.display_matches.append(flag_text) return matches def _format_completions(self, arg_state: _ArgumentState, completions: Union[List[str], List[CompletionItem]]) -> List[str]: """Format CompletionItems into hint table""" # Nothing to do if we don't have at least 2 completions which are all CompletionItems if len(completions) < 2 or not all(isinstance(c, CompletionItem) for c in completions): return cast(List[str], completions) completion_items = cast(List[CompletionItem], completions) # Check if the data being completed have a numerical type all_nums = all(isinstance(c.orig_value, numbers.Number) for c in completion_items) # Sort CompletionItems before building the hint table if not self._cmd2_app.matches_sorted: # If all orig_value types are numbers, then sort by that value if all_nums: completion_items.sort(key=lambda c: c.orig_value) # type: ignore[no-any-return] # Otherwise sort as strings else: completion_items.sort(key=self._cmd2_app.default_sort_key) self._cmd2_app.matches_sorted = True # Check if there are too many CompletionItems to display as a table if len(completions) <= self._cmd2_app.max_completion_items: four_spaces = 4 * ' ' # If a metavar was defined, use that instead of the dest field destination = arg_state.action.metavar if arg_state.action.metavar else arg_state.action.dest # Handle case where metavar was a tuple if isinstance(destination, tuple): # Figure out what string in the tuple to use based on how many of the arguments have been completed. # Use min() to avoid going passed the end of the tuple to support nargs being ZERO_OR_MORE and # ONE_OR_MORE. In those cases, argparse limits metavar tuple to 2 elements but we may be completing # the 3rd or more argument here. tuple_index = min(len(destination) - 1, arg_state.count) destination = destination[tuple_index] desc_header = arg_state.action.get_descriptive_header() # type: ignore[attr-defined] if desc_header is None: desc_header = DEFAULT_DESCRIPTIVE_HEADER # Replace tabs with 4 spaces so we can calculate width desc_header = desc_header.replace('\t', four_spaces) # Calculate needed widths for the token and description columns of the table token_width = style_aware_wcswidth(destination) desc_width = widest_line(desc_header) for item in completion_items: token_width = max(style_aware_wcswidth(item), token_width) # Replace tabs with 4 spaces so we can calculate width item.description = item.description.replace('\t', four_spaces) desc_width = max(widest_line(item.description), desc_width) cols = list() dest_alignment = HorizontalAlignment.RIGHT if all_nums else HorizontalAlignment.LEFT cols.append( Column( destination.upper(), width=token_width, header_horiz_align=dest_alignment, data_horiz_align=dest_alignment, ) ) cols.append(Column(desc_header, width=desc_width)) hint_table = SimpleTable(cols, divider_char=self._cmd2_app.ruler) table_data = [[item, item.description] for item in completion_items] self._cmd2_app.formatted_completions = hint_table.generate_table(table_data, row_spacing=0) # Return sorted list of completions return cast(List[str], completions) def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: int, tokens: List[str]) -> List[str]: """ Supports cmd2's help command in the completion of subcommand names :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param tokens: arguments passed to command/subcommand :return: List of subcommand completions """ # If our parser has subcommands, we must examine the tokens and check if they are subcommands # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter. if self._subcommand_action is not None: for token_index, token in enumerate(tokens): if token in self._subcommand_action.choices: parser: argparse.ArgumentParser = self._subcommand_action.choices[token] completer_type = self._cmd2_app._determine_ap_completer_type(parser) completer = completer_type(parser, self._cmd2_app) return completer.complete_subcommand_help(text, line, begidx, endidx, tokens[token_index + 1 :]) elif token_index == len(tokens) - 1: # Since this is the last token, we will attempt to complete it return self._cmd2_app.basic_complete(text, line, begidx, endidx, self._subcommand_action.choices) else: break return [] def format_help(self, tokens: List[str]) -> str: """ Supports cmd2's help command in the retrieval of help text :param tokens: arguments passed to help command :return: help text of the command being queried """ # If our parser has subcommands, we must examine the tokens and check if they are subcommands # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter. if self._subcommand_action is not None: for token_index, token in enumerate(tokens): if token in self._subcommand_action.choices: parser: argparse.ArgumentParser = self._subcommand_action.choices[token] completer_type = self._cmd2_app._determine_ap_completer_type(parser) completer = completer_type(parser, self._cmd2_app) return completer.format_help(tokens[token_index + 1 :]) else: break return self._parser.format_help() def _complete_arg( self, text: str, line: str, begidx: int, endidx: int, arg_state: _ArgumentState, consumed_arg_values: Dict[str, List[str]], *, cmd_set: Optional[CommandSet] = None, ) -> List[str]: """ Tab completion routine for an argparse argument :return: list of completions :raises: CompletionError if the completer or choices function this calls raises one """ # Check if the arg provides choices to the user arg_choices: Union[List[str], ChoicesCallable] if arg_state.action.choices is not None: arg_choices = list(arg_state.action.choices) if not arg_choices: return [] # If these choices are numbers, then sort them now if all(isinstance(x, numbers.Number) for x in arg_choices): arg_choices.sort() self._cmd2_app.matches_sorted = True # Since choices can be various types, make sure they are all strings for index, choice in enumerate(arg_choices): # Prevent converting anything that is already a str (i.e. CompletionItem) if not isinstance(choice, str): arg_choices[index] = str(choice) # type: ignore[unreachable] else: choices_attr = arg_state.action.get_choices_callable() # type: ignore[attr-defined] if choices_attr is None: return [] arg_choices = choices_attr # If we are going to call a completer/choices function, then set up the common arguments args = [] kwargs = {} if isinstance(arg_choices, ChoicesCallable): # The completer may or may not be defined in the same class as the command. Since completer # functions are registered with the command argparser before anything is instantiated, we # need to find an instance at runtime that matches the types during declaration self_arg = self._cmd2_app._resolve_func_self(arg_choices.to_call, cmd_set) if self_arg is None: # No cases matched, raise an error raise CompletionError('Could not find CommandSet instance matching defining type for completer') args.append(self_arg) # Check if arg_choices.to_call expects arg_tokens to_call_params = inspect.signature(arg_choices.to_call).parameters if ARG_TOKENS in to_call_params: # Merge self._parent_tokens and consumed_arg_values arg_tokens = {**self._parent_tokens, **consumed_arg_values} # Include the token being completed arg_tokens.setdefault(arg_state.action.dest, []) arg_tokens[arg_state.action.dest].append(text) # Add the namespace to the keyword arguments for the function we are calling kwargs[ARG_TOKENS] = arg_tokens # Check if the argument uses a specific tab completion function to provide its choices if isinstance(arg_choices, ChoicesCallable) and arg_choices.is_completer: args.extend([text, line, begidx, endidx]) results = arg_choices.completer(*args, **kwargs) # type: ignore[arg-type] # Otherwise use basic_complete on the choices else: # Check if the choices come from a function completion_items: List[str] = [] if isinstance(arg_choices, ChoicesCallable): if not arg_choices.is_completer: choices_func = arg_choices.choices_provider if isinstance(choices_func, ChoicesProviderFuncWithTokens): completion_items = choices_func(*args, **kwargs) # type: ignore[arg-type] else: # pragma: no cover # This won't hit because runtime checking doesn't check function argument types and will always # resolve true above. Mypy, however, does see the difference and gives an error that can't be # ignored. Mypy issue #5485 discusses this problem completion_items = choices_func(*args) # type: ignore[arg-type] # else case is already covered above else: completion_items = arg_choices # Filter out arguments we already used used_values = consumed_arg_values.get(arg_state.action.dest, []) completion_items = [choice for choice in completion_items if choice not in used_values] # Do tab completion on the choices results = self._cmd2_app.basic_complete(text, line, begidx, endidx, completion_items) if not results: # Reset the value for matches_sorted. This is because completion of flag names # may still be attempted after we return and they haven't been sorted yet. self._cmd2_app.matches_sorted = False return [] return self._format_completions(arg_state, results) # The default ArgparseCompleter class for a cmd2 app DEFAULT_AP_COMPLETER: Type[ArgparseCompleter] = ArgparseCompleter def set_default_ap_completer_type(completer_type: Type[ArgparseCompleter]) -> None: """ Set the default ArgparseCompleter class for a cmd2 app. :param completer_type: Type that is a subclass of ArgparseCompleter. """ global DEFAULT_AP_COMPLETER DEFAULT_AP_COMPLETER = completer_type cmd2-2.3.3/cmd2/argparse_custom.py000066400000000000000000001627051416142110700167510ustar00rootroot00000000000000# coding=utf-8 """ This module adds capabilities to argparse by patching a few of its functions. It also defines a parser class called Cmd2ArgumentParser which improves error and help output over normal argparse. All cmd2 code uses this parser and it is recommended that developers of cmd2-based apps either use it or write their own parser that inherits from it. This will give a consistent look-and-feel between the help/error output of built-in cmd2 commands and the app-specific commands. If you wish to override the parser used by cmd2's built-in commands, see override_parser.py example. Since the new capabilities are added by patching at the argparse API level, they are available whether or not Cmd2ArgumentParser is used. However, the help and error output of Cmd2ArgumentParser is customized to notate nargs ranges whereas any other parser class won't be as explicit in their output. **Added capabilities** Extends argparse nargs functionality by allowing tuples which specify a range (min, max). To specify a max value with no upper bound, use a 1-item tuple (min,) Example:: # -f argument expects at least 3 values parser.add_argument('-f', nargs=(3,)) # -f argument expects 3 to 5 values parser.add_argument('-f', nargs=(3, 5)) **Tab Completion** cmd2 uses its ArgparseCompleter class to enable argparse-based tab completion on all commands that use the @with_argparse wrappers. Out of the box you get tab completion of commands, subcommands, and flag names, as well as instructive hints about the current argument that print when tab is pressed. In addition, you can add tab completion for each argument's values using parameters passed to add_argument(). Below are the 3 add_argument() parameters for enabling tab completion of an argument's value. Only one can be used at a time. ``choices`` - pass a list of values to the choices parameter. Example:: my_list = ['An Option', 'SomeOtherOption'] parser.add_argument('-o', '--options', choices=my_list) ``choices_provider`` - pass a function that returns choices. This is good in cases where the choice list is dynamically generated when the user hits tab. Example:: def my_choices_provider(self): ... return my_generated_list parser.add_argument("arg", choices_provider=my_choices_provider) ``completer`` - pass a tab completion function that does custom completion. cmd2 provides a few completer methods for convenience (e.g., path_complete, delimiter_complete) Example:: # This adds file-path completion to an argument parser.add_argument('-o', '--options', completer=cmd2.Cmd.path_complete) You can use functools.partial() to prepopulate values of the underlying choices and completer functions/methods. Example:: # This says to call path_complete with a preset value for its path_filter argument dir_completer = functools.partial(path_complete, path_filter=lambda path: os.path.isdir(path)) parser.add_argument('-o', '--options', completer=dir_completer) For ``choices_provider`` and ``completer``, do not set them to a bound method. This is because ArgparseCompleter passes the `self` argument explicitly to these functions. When ArgparseCompleter calls one, it will detect whether it is bound to a `Cmd` subclass or `CommandSet`. If bound to a `cmd2.Cmd subclass`, it will pass the app instance as the `self` argument. If bound to a `cmd2.CommandSet` subclass, it will pass the `CommandSet` instance as the `self` argument. Therefore instead of passing something like `self.path_complete`, pass `cmd2.Cmd.path_complete`. ``choices_provider`` and ``completer`` functions can also be implemented as standalone functions (i.e. not a member of a class). In this case, ArgparseCompleter will pass its ``cmd2.Cmd`` app instance as the first positional argument. Of the 3 tab completion parameters, ``choices`` is the only one where argparse validates user input against items in the choices list. This is because the other 2 parameters are meant to tab complete data sets that are viewed as dynamic. Therefore it is up to the developer to validate if the user has typed an acceptable value for these arguments. There are times when what's being tab completed is determined by a previous argument on the command line. In theses cases, ArgparseCompleter can pass a dictionary that maps the command line tokens up through the one being completed to their argparse argument name. To receive this dictionary, your choices/completer function should have an argument called arg_tokens. Example:: def my_choices_provider(self, arg_tokens) def my_completer(self, text, line, begidx, endidx, arg_tokens) All values of the arg_tokens dictionary are lists, even if a particular argument expects only 1 token. Since ArgparseCompleter is for tab completion, it does not convert the tokens to their actual argument types or validate their values. All tokens are stored in the dictionary as the raw strings provided on the command line. It is up to the developer to determine if the user entered the correct argument type (e.g. int) and validate their values. CompletionItem Class - This class was added to help in cases where uninformative data is being tab completed. For instance, tab completing ID numbers isn't very helpful to a user without context. Returning a list of CompletionItems instead of a regular string for completion results will signal the ArgparseCompleter to output the completion results in a table of completion tokens with descriptions instead of just a table of tokens:: Instead of this: 1 2 3 The user sees this: ITEM_ID Item Name ============================ 1 My item 2 Another item 3 Yet another item The left-most column is the actual value being tab completed and its header is that value's name. The right column header is defined using the descriptive_header parameter of add_argument(). The right column values come from the CompletionItem.description value. Example:: token = 1 token_description = "My Item" completion_item = CompletionItem(token, token_description) Since descriptive_header and CompletionItem.description are just strings, you can format them in such a way to have multiple columns:: ITEM_ID Item Name Checked Out Due Date ========================================================== 1 My item True 02/02/2022 2 Another item False 3 Yet another item False To use CompletionItems, just return them from your choices_provider or completer functions. They can also be used as argparse choices. When a CompletionItem is created, it stores the original value (e.g. ID number) and makes it accessible through a property called orig_value. cmd2 has patched argparse so that when evaluating choices, input is compared to CompletionItem.orig_value instead of the CompletionItem instance. To avoid printing a ton of information to the screen at once when a user presses tab, there is a maximum threshold for the number of CompletionItems that will be shown. Its value is defined in cmd2.Cmd.max_completion_items. It defaults to 50, but can be changed. If the number of completion suggestions exceeds this number, they will be displayed in the typical columnized format and will not include the description value of the CompletionItems. **Patched argparse functions** ``argparse._ActionsContainer.add_argument`` - adds arguments related to tab completion and enables nargs range parsing. See _add_argument_wrapper for more details on these arguments. ``argparse.ArgumentParser._check_value`` - adds support for using ``CompletionItems`` as argparse choices. When evaluating choices, input is compared to ``CompletionItem.orig_value`` instead of the ``CompletionItem`` instance. See _ArgumentParser_check_value for more details. ``argparse.ArgumentParser._get_nargs_pattern`` - adds support for nargs ranges. See _get_nargs_pattern_wrapper for more details. ``argparse.ArgumentParser._match_argument`` - adds support for nargs ranges. See _match_argument_wrapper for more details. ``argparse._SubParsersAction.remove_parser`` - new function which removes a sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser for more details. **Added accessor methods** cmd2 has patched ``argparse.Action`` to include the following accessor methods for cases in which you need to manually access the cmd2-specific attributes. - ``argparse.Action.get_choices_callable()`` - See :func:`_action_get_choices_callable` for more details. - ``argparse.Action.set_choices_provider()`` - See :func:`_action_set_choices_provider` for more details. - ``argparse.Action.set_completer()`` - See :func:`_action_set_completer` for more details. - ``argparse.Action.get_descriptive_header()`` - See :func:`_action_get_descriptive_header` for more details. - ``argparse.Action.set_descriptive_header()`` - See :func:`_action_set_descriptive_header` for more details. - ``argparse.Action.get_nargs_range()`` - See :func:`_action_get_nargs_range` for more details. - ``argparse.Action.set_nargs_range()`` - See :func:`_action_set_nargs_range` for more details. - ``argparse.Action.get_suppress_tab_hint()`` - See :func:`_action_get_suppress_tab_hint` for more details. - ``argparse.Action.set_suppress_tab_hint()`` - See :func:`_action_set_suppress_tab_hint` for more details. cmd2 has patched ``argparse.ArgumentParser`` to include the following accessor methods - ``argparse.ArgumentParser.get_ap_completer_type()`` - See :func:`_ArgumentParser_get_ap_completer_type` for more details. - ``argparse.Action.set_ap_completer_type()`` - See :func:`_ArgumentParser_set_ap_completer_type` for more details. **Subcommand removal** cmd2 has patched ``argparse._SubParsersAction`` to include a ``remove_parser()`` method which can be used to remove a subcommand. ``argparse._SubParsersAction.remove_parser`` - new function which removes a sub-parser from a sub-parsers group. See :func:`_SubParsersAction_remove_parser` for more details. """ import argparse import re import sys # noinspection PyUnresolvedReferences,PyProtectedMember from argparse import ( ONE_OR_MORE, ZERO_OR_MORE, ArgumentError, ) from gettext import ( gettext, ) from typing import ( IO, TYPE_CHECKING, Any, Callable, Dict, Iterable, List, NoReturn, Optional, Sequence, Set, Tuple, Type, Union, cast, ) from . import ( ansi, constants, ) try: from typing import ( Protocol, runtime_checkable, ) except ImportError: from typing_extensions import ( # type: ignore[misc] Protocol, runtime_checkable, ) if TYPE_CHECKING: # pragma: no cover from .argparse_completer import ( ArgparseCompleter, ) def generate_range_error(range_min: int, range_max: Union[int, float]) -> str: """Generate an error message when the the number of arguments provided is not within the expected range""" err_str = "expected " if range_max == constants.INFINITY: plural = '' if range_min == 1 else 's' err_str += f"at least {range_min}" else: plural = '' if range_max == 1 else 's' if range_min == range_max: err_str += f"{range_min}" else: err_str += f"{range_min} to {range_max}" err_str += f" argument{plural}" return err_str class CompletionItem(str): """ Completion item with descriptive text attached See header of this file for more information """ def __new__(cls, value: object, *args: Any, **kwargs: Any) -> 'CompletionItem': return super(CompletionItem, cls).__new__(cls, value) # noinspection PyUnusedLocal def __init__(self, value: object, description: str = '', *args: Any) -> None: """ CompletionItem Initializer :param value: the value being tab completed :param description: description text to display :param args: args for str __init__ :param kwargs: kwargs for str __init__ """ super().__init__(*args) self.description = description # Save the original value to support CompletionItems as argparse choices. # cmd2 has patched argparse so input is compared to this value instead of the CompletionItem instance. self._orig_value = value @property def orig_value(self) -> Any: """Read-only property for _orig_value""" return self._orig_value ############################################################################################################ # Class and functions related to ChoicesCallable ############################################################################################################ @runtime_checkable class ChoicesProviderFuncBase(Protocol): """ Function that returns a list of choices in support of tab completion """ def __call__(self) -> List[str]: ... # pragma: no cover @runtime_checkable class ChoicesProviderFuncWithTokens(Protocol): """ Function that returns a list of choices in support of tab completion and accepts a dictionary of prior arguments. """ def __call__(self, *, arg_tokens: Dict[str, List[str]] = {}) -> List[str]: ... # pragma: no cover ChoicesProviderFunc = Union[ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens] @runtime_checkable class CompleterFuncBase(Protocol): """ Function to support tab completion with the provided state of the user prompt """ def __call__( self, text: str, line: str, begidx: int, endidx: int, ) -> List[str]: ... # pragma: no cover @runtime_checkable class CompleterFuncWithTokens(Protocol): """ Function to support tab completion with the provided state of the user prompt and accepts a dictionary of prior arguments. """ def __call__( self, text: str, line: str, begidx: int, endidx: int, *, arg_tokens: Dict[str, List[str]] = {}, ) -> List[str]: ... # pragma: no cover CompleterFunc = Union[CompleterFuncBase, CompleterFuncWithTokens] class ChoicesCallable: """ Enables using a callable as the choices provider for an argparse argument. While argparse has the built-in choices attribute, it is limited to an iterable. """ def __init__( self, is_completer: bool, to_call: Union[CompleterFunc, ChoicesProviderFunc], ) -> None: """ Initializer :param is_completer: True if to_call is a tab completion routine which expects the args: text, line, begidx, endidx :param to_call: the callable object that will be called to provide choices for the argument """ self.is_completer = is_completer if is_completer: if not isinstance(to_call, (CompleterFuncBase, CompleterFuncWithTokens)): # pragma: no cover # runtime checking of Protocols do not currently check the parameters of a function. raise ValueError( 'With is_completer set to true, to_call must be either CompleterFunc, CompleterFuncWithTokens' ) else: if not isinstance(to_call, (ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens)): # pragma: no cover # runtime checking of Protocols do not currently check the parameters of a function. raise ValueError( 'With is_completer set to false, to_call must be either: ' 'ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens' ) self.to_call = to_call @property def completer(self) -> CompleterFunc: if not isinstance(self.to_call, (CompleterFuncBase, CompleterFuncWithTokens)): # pragma: no cover # this should've been caught in the constructor, just a backup check raise ValueError('Function is not a CompleterFunc') return self.to_call @property def choices_provider(self) -> ChoicesProviderFunc: if not isinstance(self.to_call, (ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens)): # pragma: no cover # this should've been caught in the constructor, just a backup check raise ValueError('Function is not a ChoicesProviderFunc') return self.to_call ############################################################################################################ # The following are names of custom argparse Action attributes added by cmd2 ############################################################################################################ # ChoicesCallable object that specifies the function to be called which provides choices to the argument ATTR_CHOICES_CALLABLE = 'choices_callable' # Descriptive header that prints when using CompletionItems ATTR_DESCRIPTIVE_HEADER = 'descriptive_header' # A tuple specifying nargs as a range (min, max) ATTR_NARGS_RANGE = 'nargs_range' # Pressing tab normally displays the help text for the argument if no choices are available # Setting this attribute to True will suppress these hints ATTR_SUPPRESS_TAB_HINT = 'suppress_tab_hint' ############################################################################################################ # Patch argparse.Action with accessors for choice_callable attribute ############################################################################################################ def _action_get_choices_callable(self: argparse.Action) -> Optional[ChoicesCallable]: """ Get the choices_callable attribute of an argparse Action. This function is added by cmd2 as a method called ``get_choices_callable()`` to ``argparse.Action`` class. To call: ``action.get_choices_callable()`` :param self: argparse Action being queried :return: A ChoicesCallable instance or None if attribute does not exist """ return cast(Optional[ChoicesCallable], getattr(self, ATTR_CHOICES_CALLABLE, None)) setattr(argparse.Action, 'get_choices_callable', _action_get_choices_callable) def _action_set_choices_callable(self: argparse.Action, choices_callable: ChoicesCallable) -> None: """ Set the choices_callable attribute of an argparse Action. This function is added by cmd2 as a method called ``_set_choices_callable()`` to ``argparse.Action`` class. Call this using the convenience wrappers ``set_choices_provider()`` and ``set_completer()`` instead. :param self: action being edited :param choices_callable: the ChoicesCallable instance to use :raises: TypeError if used on incompatible action type """ # Verify consistent use of parameters if self.choices is not None: err_msg = "None of the following parameters can be used alongside a choices parameter:\n" "choices_provider, completer" raise (TypeError(err_msg)) elif self.nargs == 0: err_msg = ( "None of the following parameters can be used on an action that takes no arguments:\n" "choices_provider, completer" ) raise (TypeError(err_msg)) setattr(self, ATTR_CHOICES_CALLABLE, choices_callable) setattr(argparse.Action, '_set_choices_callable', _action_set_choices_callable) def _action_set_choices_provider( self: argparse.Action, choices_provider: ChoicesProviderFunc, ) -> None: """ Set choices_provider of an argparse Action. This function is added by cmd2 as a method called ``set_choices_callable()`` to ``argparse.Action`` class. To call: ``action.set_choices_provider(choices_provider)`` :param self: action being edited :param choices_provider: the choices_provider instance to use :raises: TypeError if used on incompatible action type """ self._set_choices_callable(ChoicesCallable(is_completer=False, to_call=choices_provider)) # type: ignore[attr-defined] setattr(argparse.Action, 'set_choices_provider', _action_set_choices_provider) def _action_set_completer( self: argparse.Action, completer: CompleterFunc, ) -> None: """ Set completer of an argparse Action. This function is added by cmd2 as a method called ``set_completer()`` to ``argparse.Action`` class. To call: ``action.set_completer(completer)`` :param self: action being edited :param completer: the completer instance to use :raises: TypeError if used on incompatible action type """ self._set_choices_callable(ChoicesCallable(is_completer=True, to_call=completer)) # type: ignore[attr-defined] setattr(argparse.Action, 'set_completer', _action_set_completer) ############################################################################################################ # Patch argparse.Action with accessors for descriptive_header attribute ############################################################################################################ def _action_get_descriptive_header(self: argparse.Action) -> Optional[str]: """ Get the descriptive_header attribute of an argparse Action. This function is added by cmd2 as a method called ``get_descriptive_header()`` to ``argparse.Action`` class. To call: ``action.get_descriptive_header()`` :param self: argparse Action being queried :return: The value of descriptive_header or None if attribute does not exist """ return cast(Optional[str], getattr(self, ATTR_DESCRIPTIVE_HEADER, None)) setattr(argparse.Action, 'get_descriptive_header', _action_get_descriptive_header) def _action_set_descriptive_header(self: argparse.Action, descriptive_header: Optional[str]) -> None: """ Set the descriptive_header attribute of an argparse Action. This function is added by cmd2 as a method called ``set_descriptive_header()`` to ``argparse.Action`` class. To call: ``action.set_descriptive_header(descriptive_header)`` :param self: argparse Action being updated :param descriptive_header: value being assigned """ setattr(self, ATTR_DESCRIPTIVE_HEADER, descriptive_header) setattr(argparse.Action, 'set_descriptive_header', _action_set_descriptive_header) ############################################################################################################ # Patch argparse.Action with accessors for nargs_range attribute ############################################################################################################ def _action_get_nargs_range(self: argparse.Action) -> Optional[Tuple[int, Union[int, float]]]: """ Get the nargs_range attribute of an argparse Action. This function is added by cmd2 as a method called ``get_nargs_range()`` to ``argparse.Action`` class. To call: ``action.get_nargs_range()`` :param self: argparse Action being queried :return: The value of nargs_range or None if attribute does not exist """ return cast(Optional[Tuple[int, Union[int, float]]], getattr(self, ATTR_NARGS_RANGE, None)) setattr(argparse.Action, 'get_nargs_range', _action_get_nargs_range) def _action_set_nargs_range(self: argparse.Action, nargs_range: Optional[Tuple[int, Union[int, float]]]) -> None: """ Set the nargs_range attribute of an argparse Action. This function is added by cmd2 as a method called ``set_nargs_range()`` to ``argparse.Action`` class. To call: ``action.set_nargs_range(nargs_range)`` :param self: argparse Action being updated :param nargs_range: value being assigned """ setattr(self, ATTR_NARGS_RANGE, nargs_range) setattr(argparse.Action, 'set_nargs_range', _action_set_nargs_range) ############################################################################################################ # Patch argparse.Action with accessors for suppress_tab_hint attribute ############################################################################################################ def _action_get_suppress_tab_hint(self: argparse.Action) -> bool: """ Get the suppress_tab_hint attribute of an argparse Action. This function is added by cmd2 as a method called ``get_suppress_tab_hint()`` to ``argparse.Action`` class. To call: ``action.get_suppress_tab_hint()`` :param self: argparse Action being queried :return: The value of suppress_tab_hint or False if attribute does not exist """ return cast(bool, getattr(self, ATTR_SUPPRESS_TAB_HINT, False)) setattr(argparse.Action, 'get_suppress_tab_hint', _action_get_suppress_tab_hint) def _action_set_suppress_tab_hint(self: argparse.Action, suppress_tab_hint: bool) -> None: """ Set the suppress_tab_hint attribute of an argparse Action. This function is added by cmd2 as a method called ``set_suppress_tab_hint()`` to ``argparse.Action`` class. To call: ``action.set_suppress_tab_hint(suppress_tab_hint)`` :param self: argparse Action being updated :param suppress_tab_hint: value being assigned """ setattr(self, ATTR_SUPPRESS_TAB_HINT, suppress_tab_hint) setattr(argparse.Action, 'set_suppress_tab_hint', _action_set_suppress_tab_hint) ############################################################################################################ # Allow developers to add custom action attributes ############################################################################################################ CUSTOM_ACTION_ATTRIBS: Set[str] = set() _CUSTOM_ATTRIB_PFX = '_attr_' def register_argparse_argument_parameter(param_name: str, param_type: Optional[Type[Any]]) -> None: """ Registers a custom argparse argument parameter. The registered name will then be a recognized keyword parameter to the parser's `add_argument()` function. An accessor functions will be added to the parameter's Action object in the form of: ``get_{param_name}()`` and ``set_{param_name}(value)``. :param param_name: Name of the parameter to add. :param param_type: Type of the parameter to add. """ attr_name = f'{_CUSTOM_ATTRIB_PFX}{param_name}' if param_name in CUSTOM_ACTION_ATTRIBS or hasattr(argparse.Action, attr_name): raise KeyError(f'Custom parameter {param_name} already exists') if not re.search('^[A-Za-z_][A-Za-z0-9_]*$', param_name): raise KeyError(f'Invalid parameter name {param_name} - cannot be used as a python identifier') getter_name = f'get_{param_name}' def _action_get_custom_parameter(self: argparse.Action) -> Any: f""" Get the custom {param_name} attribute of an argparse Action. This function is added by cmd2 as a method called ``{getter_name}()`` to ``argparse.Action`` class. To call: ``action.{getter_name}()`` :param self: argparse Action being queried :return: The value of {param_name} or None if attribute does not exist """ return getattr(self, attr_name, None) setattr(argparse.Action, getter_name, _action_get_custom_parameter) setter_name = f'set_{param_name}' def _action_set_custom_parameter(self: argparse.Action, value: Any) -> None: f""" Set the custom {param_name} attribute of an argparse Action. This function is added by cmd2 as a method called ``{setter_name}()`` to ``argparse.Action`` class. To call: ``action.{setter_name}({param_name})`` :param self: argparse Action being updated :param value: value being assigned """ if param_type and not isinstance(value, param_type): raise TypeError(f'{param_name} must be of type {param_type}, got: {value} ({type(value)})') setattr(self, attr_name, value) setattr(argparse.Action, setter_name, _action_set_custom_parameter) CUSTOM_ACTION_ATTRIBS.add(param_name) ############################################################################################################ # Patch _ActionsContainer.add_argument with our wrapper to support more arguments ############################################################################################################ # Save original _ActionsContainer.add_argument so we can call it in our wrapper # noinspection PyProtectedMember orig_actions_container_add_argument = argparse._ActionsContainer.add_argument # noinspection PyProtectedMember def _add_argument_wrapper( self: argparse._ActionsContainer, *args: Any, nargs: Union[int, str, Tuple[int], Tuple[int, int], Tuple[int, float], None] = None, choices_provider: Optional[ChoicesProviderFunc] = None, completer: Optional[CompleterFunc] = None, suppress_tab_hint: bool = False, descriptive_header: Optional[str] = None, **kwargs: Any, ) -> argparse.Action: """ Wrapper around _ActionsContainer.add_argument() which supports more settings used by cmd2 # Args from original function :param self: instance of the _ActionsContainer being added to :param args: arguments expected by argparse._ActionsContainer.add_argument # Customized arguments from original function :param nargs: extends argparse nargs functionality by allowing tuples which specify a range (min, max) to specify a max value with no upper bound, use a 1-item tuple (min,) # Added args used by ArgparseCompleter :param choices_provider: function that provides choices for this argument :param completer: tab completion function that provides choices for this argument :param suppress_tab_hint: when ArgparseCompleter has no results to show during tab completion, it displays the current argument's help text as a hint. Set this to True to suppress the hint. If this argument's help text is set to argparse.SUPPRESS, then tab hints will not display regardless of the value passed for suppress_tab_hint. Defaults to False. :param descriptive_header: if the provided choices are CompletionItems, then this header will display during tab completion. Defaults to None. # Args from original function :param kwargs: keyword-arguments recognized by argparse._ActionsContainer.add_argument Note: You can only use 1 of the following in your argument: choices, choices_provider, completer See the header of this file for more information :return: the created argument action :raises: ValueError on incorrect parameter usage """ # Verify consistent use of arguments choices_callables = [choices_provider, completer] num_params_set = len(choices_callables) - choices_callables.count(None) if num_params_set > 1: err_msg = "Only one of the following parameters may be used at a time:\n" "choices_provider, completer" raise (ValueError(err_msg)) # Pre-process special ranged nargs nargs_range = None if nargs is not None: nargs_adjusted: Union[int, str, Tuple[int], Tuple[int, int], Tuple[int, float], None] # Check if nargs was given as a range if isinstance(nargs, tuple): # Handle 1-item tuple by setting max to INFINITY if len(nargs) == 1: nargs = (nargs[0], constants.INFINITY) # Validate nargs tuple if ( len(nargs) != 2 or not isinstance(nargs[0], int) # type: ignore[unreachable] or not (isinstance(nargs[1], int) or nargs[1] == constants.INFINITY) # type: ignore[misc] ): raise ValueError('Ranged values for nargs must be a tuple of 1 or 2 integers') if nargs[0] >= nargs[1]: # type: ignore[misc] raise ValueError('Invalid nargs range. The first value must be less than the second') if nargs[0] < 0: raise ValueError('Negative numbers are invalid for nargs range') # Save the nargs tuple as our range setting nargs_range = nargs range_min = nargs_range[0] range_max = nargs_range[1] # type: ignore[misc] # Convert nargs into a format argparse recognizes if range_min == 0: if range_max == 1: nargs_adjusted = argparse.OPTIONAL # No range needed since (0, 1) is just argparse.OPTIONAL nargs_range = None else: nargs_adjusted = argparse.ZERO_OR_MORE if range_max == constants.INFINITY: # No range needed since (0, INFINITY) is just argparse.ZERO_OR_MORE nargs_range = None elif range_min == 1 and range_max == constants.INFINITY: nargs_adjusted = argparse.ONE_OR_MORE # No range needed since (1, INFINITY) is just argparse.ONE_OR_MORE nargs_range = None else: nargs_adjusted = argparse.ONE_OR_MORE else: nargs_adjusted = nargs # Add the argparse-recognized version of nargs to kwargs kwargs['nargs'] = nargs_adjusted # Extract registered custom keyword arguments custom_attribs: Dict[str, Any] = {} for keyword, value in kwargs.items(): if keyword in CUSTOM_ACTION_ATTRIBS: custom_attribs[keyword] = value for keyword in custom_attribs: del kwargs[keyword] # Create the argument using the original add_argument function new_arg = orig_actions_container_add_argument(self, *args, **kwargs) # Set the custom attributes new_arg.set_nargs_range(nargs_range) # type: ignore[arg-type, attr-defined] if choices_provider: new_arg.set_choices_provider(choices_provider) # type: ignore[attr-defined] elif completer: new_arg.set_completer(completer) # type: ignore[attr-defined] new_arg.set_suppress_tab_hint(suppress_tab_hint) # type: ignore[attr-defined] new_arg.set_descriptive_header(descriptive_header) # type: ignore[attr-defined] for keyword, value in custom_attribs.items(): attr_setter = getattr(new_arg, f'set_{keyword}', None) if attr_setter is not None: attr_setter(value) return new_arg # Overwrite _ActionsContainer.add_argument with our wrapper # noinspection PyProtectedMember setattr(argparse._ActionsContainer, 'add_argument', _add_argument_wrapper) ############################################################################################################ # Patch ArgumentParser._get_nargs_pattern with our wrapper to support nargs ranges ############################################################################################################ # Save original ArgumentParser._get_nargs_pattern so we can call it in our wrapper # noinspection PyProtectedMember orig_argument_parser_get_nargs_pattern = argparse.ArgumentParser._get_nargs_pattern # noinspection PyProtectedMember def _get_nargs_pattern_wrapper(self: argparse.ArgumentParser, action: argparse.Action) -> str: # Wrapper around ArgumentParser._get_nargs_pattern behavior to support nargs ranges nargs_range = action.get_nargs_range() # type: ignore[attr-defined] if nargs_range is not None: if nargs_range[1] == constants.INFINITY: range_max = '' else: range_max = nargs_range[1] # type: ignore[assignment] nargs_pattern = f'(-*A{{{nargs_range[0]},{range_max}}}-*)' # if this is an optional action, -- is not allowed if action.option_strings: nargs_pattern = nargs_pattern.replace('-*', '') nargs_pattern = nargs_pattern.replace('-', '') return nargs_pattern return orig_argument_parser_get_nargs_pattern(self, action) # Overwrite ArgumentParser._get_nargs_pattern with our wrapper # noinspection PyProtectedMember setattr(argparse.ArgumentParser, '_get_nargs_pattern', _get_nargs_pattern_wrapper) ############################################################################################################ # Patch ArgumentParser._match_argument with our wrapper to support nargs ranges ############################################################################################################ # noinspection PyProtectedMember orig_argument_parser_match_argument = argparse.ArgumentParser._match_argument # noinspection PyProtectedMember def _match_argument_wrapper(self: argparse.ArgumentParser, action: argparse.Action, arg_strings_pattern: str) -> int: # Wrapper around ArgumentParser._match_argument behavior to support nargs ranges nargs_pattern = self._get_nargs_pattern(action) match = re.match(nargs_pattern, arg_strings_pattern) # raise an exception if we weren't able to find a match if match is None: nargs_range = action.get_nargs_range() # type: ignore[attr-defined] if nargs_range is not None: raise ArgumentError(action, generate_range_error(nargs_range[0], nargs_range[1])) return orig_argument_parser_match_argument(self, action, arg_strings_pattern) # Overwrite ArgumentParser._match_argument with our wrapper # noinspection PyProtectedMember setattr(argparse.ArgumentParser, '_match_argument', _match_argument_wrapper) ############################################################################################################ # Patch argparse.ArgumentParser with accessors for ap_completer_type attribute ############################################################################################################ # An ArgumentParser attribute which specifies a subclass of ArgparseCompleter for custom tab completion behavior on a # given parser. If this is None or not present, then cmd2 will use argparse_completer.DEFAULT_AP_COMPLETER when tab # completing a parser's arguments ATTR_AP_COMPLETER_TYPE = 'ap_completer_type' # noinspection PyPep8Naming def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> Optional[Type['ArgparseCompleter']]: """ Get the ap_completer_type attribute of an argparse ArgumentParser. This function is added by cmd2 as a method called ``get_ap_completer_type()`` to ``argparse.ArgumentParser`` class. To call: ``parser.get_ap_completer_type()`` :param self: ArgumentParser being queried :return: An ArgparseCompleter-based class or None if attribute does not exist """ return cast(Optional[Type['ArgparseCompleter']], getattr(self, ATTR_AP_COMPLETER_TYPE, None)) setattr(argparse.ArgumentParser, 'get_ap_completer_type', _ArgumentParser_get_ap_completer_type) # noinspection PyPep8Naming def _ArgumentParser_set_ap_completer_type(self: argparse.ArgumentParser, ap_completer_type: Type['ArgparseCompleter']) -> None: """ Set the ap_completer_type attribute of an argparse ArgumentParser. This function is added by cmd2 as a method called ``set_ap_completer_type()`` to ``argparse.ArgumentParser`` class. To call: ``parser.set_ap_completer_type(ap_completer_type)`` :param self: ArgumentParser being edited :param ap_completer_type: the custom ArgparseCompleter-based class to use when tab completing arguments for this parser """ setattr(self, ATTR_AP_COMPLETER_TYPE, ap_completer_type) setattr(argparse.ArgumentParser, 'set_ap_completer_type', _ArgumentParser_set_ap_completer_type) ############################################################################################################ # Patch ArgumentParser._check_value to support CompletionItems as choices ############################################################################################################ # noinspection PyPep8Naming def _ArgumentParser_check_value(self: argparse.ArgumentParser, action: argparse.Action, value: Any) -> None: """ Custom override of ArgumentParser._check_value that supports CompletionItems as choices. When evaluating choices, input is compared to CompletionItem.orig_value instead of the CompletionItem instance. :param self: ArgumentParser instance :param action: the action being populated :param value: value from command line already run through conversion function by argparse """ # Import gettext like argparse does from gettext import ( gettext as _, ) # converted value must be one of the choices (if specified) if action.choices is not None: # If any choice is a CompletionItem, then use its orig_value property. choices = [c.orig_value if isinstance(c, CompletionItem) else c for c in action.choices] if value not in choices: args = {'value': value, 'choices': ', '.join(map(repr, choices))} msg = _('invalid choice: %(value)r (choose from %(choices)s)') raise ArgumentError(action, msg % args) setattr(argparse.ArgumentParser, '_check_value', _ArgumentParser_check_value) ############################################################################################################ # Patch argparse._SubParsersAction to add remove_parser function ############################################################################################################ # noinspection PyPep8Naming,PyProtectedMember def _SubParsersAction_remove_parser(self: argparse._SubParsersAction, name: str) -> None: """ Removes a sub-parser from a sub-parsers group. Used to remove subcommands from a parser. This function is added by cmd2 as a method called ``remove_parser()`` to ``argparse._SubParsersAction`` class. To call: ``action.remove_parser(name)`` :param self: instance of the _SubParsersAction being edited :param name: name of the subcommand for the sub-parser to remove """ # Remove this subcommand from its base command's help text for choice_action in self._choices_actions: if choice_action.dest == name: self._choices_actions.remove(choice_action) break # Remove this subcommand and all its aliases from the base command subparser = self._name_parser_map.get(name) if subparser is not None: to_remove = [] for cur_name, cur_parser in self._name_parser_map.items(): if cur_parser is subparser: to_remove.append(cur_name) for cur_name in to_remove: del self._name_parser_map[cur_name] # noinspection PyProtectedMember setattr(argparse._SubParsersAction, 'remove_parser', _SubParsersAction_remove_parser) ############################################################################################################ # Unless otherwise noted, everything below this point are copied from Python's # argparse implementation with minor tweaks to adjust output. # Changes are noted if it's buried in a block of copied code. Otherwise the # function will check for a special case and fall back to the parent function ############################################################################################################ # noinspection PyCompatibility,PyShadowingBuiltins class Cmd2HelpFormatter(argparse.RawTextHelpFormatter): """Custom help formatter to configure ordering of help text""" # noinspection PyProtectedMember def _format_usage( self, usage: Optional[str], actions: Iterable[argparse.Action], groups: Iterable[argparse._ArgumentGroup], prefix: Optional[str] = None, ) -> str: if prefix is None: prefix = gettext('Usage: ') # if usage is specified, use that if usage is not None: usage %= dict(prog=self._prog) # if no optionals or positionals are available, usage is just prog elif not actions: usage = '%(prog)s' % dict(prog=self._prog) # if optionals and positionals are available, calculate usage else: prog = '%(prog)s' % dict(prog=self._prog) # split optionals from positionals optionals = [] positionals = [] # Begin cmd2 customization (separates required and optional, applies to all changes in this function) required_options = [] for action in actions: if action.option_strings: if action.required: required_options.append(action) else: optionals.append(action) else: positionals.append(action) # End cmd2 customization # build full usage string format = self._format_actions_usage action_usage = format(required_options + optionals + positionals, groups) usage = ' '.join([s for s in [prog, action_usage] if s]) # wrap the usage parts if it's too long text_width = self._width - self._current_indent if len(prefix) + len(usage) > text_width: # Begin cmd2 customization # break usage into wrappable parts part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' req_usage = format(required_options, groups) opt_usage = format(optionals, groups) pos_usage = format(positionals, groups) req_parts = re.findall(part_regexp, req_usage) opt_parts = re.findall(part_regexp, opt_usage) pos_parts = re.findall(part_regexp, pos_usage) assert ' '.join(req_parts) == req_usage assert ' '.join(opt_parts) == opt_usage assert ' '.join(pos_parts) == pos_usage # End cmd2 customization # helper for wrapping lines # noinspection PyMissingOrEmptyDocstring,PyShadowingNames def get_lines(parts: List[str], indent: str, prefix: Optional[str] = None) -> List[str]: lines: List[str] = [] line: List[str] = [] if prefix is not None: line_len = len(prefix) - 1 else: line_len = len(indent) - 1 for part in parts: if line_len + 1 + len(part) > text_width and line: lines.append(indent + ' '.join(line)) line = [] line_len = len(indent) - 1 line.append(part) line_len += len(part) + 1 if line: lines.append(indent + ' '.join(line)) if prefix is not None: lines[0] = lines[0][len(indent) :] return lines # if prog is short, follow it with optionals or positionals if len(prefix) + len(prog) <= 0.75 * text_width: indent = ' ' * (len(prefix) + len(prog) + 1) # Begin cmd2 customization if req_parts: lines = get_lines([prog] + req_parts, indent, prefix) lines.extend(get_lines(opt_parts, indent)) lines.extend(get_lines(pos_parts, indent)) elif opt_parts: lines = get_lines([prog] + opt_parts, indent, prefix) lines.extend(get_lines(pos_parts, indent)) elif pos_parts: lines = get_lines([prog] + pos_parts, indent, prefix) else: lines = [prog] # End cmd2 customization # if prog is long, put it on its own line else: indent = ' ' * len(prefix) # Begin cmd2 customization parts = req_parts + opt_parts + pos_parts lines = get_lines(parts, indent) if len(lines) > 1: lines = [] lines.extend(get_lines(req_parts, indent)) lines.extend(get_lines(opt_parts, indent)) lines.extend(get_lines(pos_parts, indent)) # End cmd2 customization lines = [prog] + lines # join lines into usage usage = '\n'.join(lines) # prefix with 'Usage:' return '%s%s\n\n' % (prefix, usage) def _format_action_invocation(self, action: argparse.Action) -> str: if not action.option_strings: default = self._get_default_metavar_for_positional(action) (metavar,) = self._metavar_formatter(action, default)(1) return metavar else: parts: List[str] = [] # if the Optional doesn't take a value, format is: # -s, --long if action.nargs == 0: parts.extend(action.option_strings) return ', '.join(parts) # Begin cmd2 customization (less verbose) # if the Optional takes a value, format is: # -s, --long ARGS else: default = self._get_default_metavar_for_optional(action) args_string = self._format_args(action, default) return ', '.join(action.option_strings) + ' ' + args_string # End cmd2 customization # noinspection PyMethodMayBeStatic def _determine_metavar( self, action: argparse.Action, default_metavar: Union[str, Tuple[str, ...]], ) -> Union[str, Tuple[str, ...]]: """Custom method to determine what to use as the metavar value of an action""" if action.metavar is not None: result = action.metavar elif action.choices is not None: choice_strs = [str(choice) for choice in action.choices] # Begin cmd2 customization (added space after comma) result = '{%s}' % ', '.join(choice_strs) # End cmd2 customization else: result = default_metavar return result def _metavar_formatter( self, action: argparse.Action, default_metavar: Union[str, Tuple[str, ...]], ) -> Callable[[int], Tuple[str, ...]]: metavar = self._determine_metavar(action, default_metavar) # noinspection PyMissingOrEmptyDocstring def format(tuple_size: int) -> Tuple[str, ...]: if isinstance(metavar, tuple): return metavar else: return (metavar,) * tuple_size return format # noinspection PyProtectedMember def _format_args(self, action: argparse.Action, default_metavar: Union[str, Tuple[str, ...]]) -> str: """Customized to handle ranged nargs and make other output less verbose""" metavar = self._determine_metavar(action, default_metavar) metavar_formatter = self._metavar_formatter(action, default_metavar) # Handle nargs specified as a range nargs_range = action.get_nargs_range() # type: ignore[attr-defined] if nargs_range is not None: if nargs_range[1] == constants.INFINITY: range_str = f'{nargs_range[0]}+' else: range_str = f'{nargs_range[0]}..{nargs_range[1]}' return '{}{{{}}}'.format('%s' % metavar_formatter(1), range_str) # Make this output less verbose. Do not customize the output when metavar is a # tuple of strings. Allow argparse's formatter to handle that instead. elif isinstance(metavar, str): if action.nargs == ZERO_OR_MORE: return '[%s [...]]' % metavar_formatter(1) elif action.nargs == ONE_OR_MORE: return '%s [...]' % metavar_formatter(1) elif isinstance(action.nargs, int) and action.nargs > 1: return '{}{{{}}}'.format('%s' % metavar_formatter(1), action.nargs) return super()._format_args(action, default_metavar) # type: ignore[arg-type] # noinspection PyCompatibility class Cmd2ArgumentParser(argparse.ArgumentParser): """Custom ArgumentParser class that improves error and help output""" def __init__( self, prog: Optional[str] = None, usage: Optional[str] = None, description: Optional[str] = None, epilog: Optional[str] = None, parents: Sequence[argparse.ArgumentParser] = (), formatter_class: Type[argparse.HelpFormatter] = Cmd2HelpFormatter, prefix_chars: str = '-', fromfile_prefix_chars: Optional[str] = None, argument_default: Optional[str] = None, conflict_handler: str = 'error', add_help: bool = True, allow_abbrev: bool = True, *, ap_completer_type: Optional[Type['ArgparseCompleter']] = None, ) -> None: """ # Custom parameter added by cmd2 :param ap_completer_type: optional parameter which specifies a subclass of ArgparseCompleter for custom tab completion behavior on this parser. If this is None or not present, then cmd2 will use argparse_completer.DEFAULT_AP_COMPLETER when tab completing this parser's arguments """ super(Cmd2ArgumentParser, self).__init__( prog=prog, usage=usage, description=description, epilog=epilog, parents=parents if parents else [], formatter_class=formatter_class, # type: ignore[arg-type] prefix_chars=prefix_chars, fromfile_prefix_chars=fromfile_prefix_chars, argument_default=argument_default, conflict_handler=conflict_handler, add_help=add_help, allow_abbrev=allow_abbrev, ) self.set_ap_completer_type(ap_completer_type) # type: ignore[attr-defined] # noinspection PyProtectedMember def add_subparsers(self, **kwargs: Any) -> argparse._SubParsersAction: """ Custom override. Sets a default title if one was not given. :param kwargs: additional keyword arguments :return: argparse Subparser Action """ if 'title' not in kwargs: kwargs['title'] = 'subcommands' return super().add_subparsers(**kwargs) def error(self, message: str) -> NoReturn: """Custom override that applies custom formatting to the error message""" lines = message.split('\n') linum = 0 formatted_message = '' for line in lines: if linum == 0: formatted_message = 'Error: ' + line else: formatted_message += '\n ' + line linum += 1 self.print_usage(sys.stderr) formatted_message = ansi.style_error(formatted_message) self.exit(2, f'{formatted_message}\n\n') # noinspection PyProtectedMember def format_help(self) -> str: """Copy of format_help() from argparse.ArgumentParser with tweaks to separately display required parameters""" formatter = self._get_formatter() # usage formatter.add_usage(self.usage, self._actions, self._mutually_exclusive_groups) # type: ignore[arg-type] # description formatter.add_text(self.description) # Begin cmd2 customization (separate required and optional arguments) # positionals, optionals and user-defined groups for action_group in self._action_groups: if sys.version_info >= (3, 10): default_options_group = action_group.title == 'options' else: default_options_group = action_group.title == 'optional arguments' if default_options_group: # check if the arguments are required, group accordingly req_args = [] opt_args = [] for action in action_group._group_actions: if action.required: req_args.append(action) else: opt_args.append(action) # separately display required arguments formatter.start_section('required arguments') formatter.add_text(action_group.description) formatter.add_arguments(req_args) formatter.end_section() # now display truly optional arguments formatter.start_section('optional arguments') formatter.add_text(action_group.description) formatter.add_arguments(opt_args) formatter.end_section() else: formatter.start_section(action_group.title) formatter.add_text(action_group.description) formatter.add_arguments(action_group._group_actions) formatter.end_section() # End cmd2 customization # epilog formatter.add_text(self.epilog) # determine help from format above return formatter.format_help() + '\n' def _print_message(self, message: str, file: Optional[IO[str]] = None) -> None: # Override _print_message to use style_aware_write() since we use ANSI escape characters to support color if message: if file is None: file = sys.stderr ansi.style_aware_write(file, message) class Cmd2AttributeWrapper: """ Wraps a cmd2-specific attribute added to an argparse Namespace. This makes it easy to know which attributes in a Namespace are arguments from a parser and which were added by cmd2. """ def __init__(self, attribute: Any) -> None: self.__attribute = attribute def get(self) -> Any: """Get the value of the attribute""" return self.__attribute def set(self, new_val: Any) -> None: """Set the value of the attribute""" self.__attribute = new_val # The default ArgumentParser class for a cmd2 app DEFAULT_ARGUMENT_PARSER: Type[argparse.ArgumentParser] = Cmd2ArgumentParser def set_default_argument_parser_type(parser_type: Type[argparse.ArgumentParser]) -> None: """ Set the default ArgumentParser class for a cmd2 app. This must be called prior to loading cmd2.py if you want to override the parser for cmd2's built-in commands. See examples/override_parser.py. """ global DEFAULT_ARGUMENT_PARSER DEFAULT_ARGUMENT_PARSER = parser_type cmd2-2.3.3/cmd2/clipboard.py000066400000000000000000000021411416142110700154750ustar00rootroot00000000000000# coding=utf-8 """ This module provides basic ability to copy from and paste to the clipboard/pastebuffer. """ from typing import ( cast, ) import pyperclip # type: ignore[import] # noinspection PyProtectedMember from pyperclip import ( PyperclipException, ) # Can we access the clipboard? Should always be true on Windows and Mac, but only sometimes on Linux try: # Try getting the contents of the clipboard _ = pyperclip.paste() except (PyperclipException, FileNotFoundError, ValueError): # NOTE: FileNotFoundError is for Windows Subsystem for Linux (WSL) when Windows paths are removed from $PATH # NOTE: ValueError is for headless Linux systems without Gtk installed can_clip = False else: can_clip = True def get_paste_buffer() -> str: """Get the contents of the clipboard / paste buffer. :return: contents of the clipboard """ pb_str = cast(str, pyperclip.paste()) return pb_str def write_to_paste_buffer(txt: str) -> None: """Copy text to the clipboard / paste buffer. :param txt: text to copy to the clipboard """ pyperclip.copy(txt) cmd2-2.3.3/cmd2/cmd2.py000066400000000000000000007420001416142110700143700ustar00rootroot00000000000000# coding=utf-8 """Variant on standard library's cmd with extra features. To use, simply import cmd2.Cmd instead of cmd.Cmd; use precisely as though you were using the standard library's cmd, while enjoying the extra features. Searchable command history (commands: "history") Run commands from file, save to file, edit commands in file Multi-line commands Special-character shortcut commands (beyond cmd's "?" and "!") Settable environment parameters Parsing commands with `argparse` argument parsers (flags) Redirection to file or paste buffer (clipboard) with > or >> Easy transcript-based testing of applications (see examples/example.py) Bash-style ``select`` available Note that redirection with > and | will only work if `self.poutput()` is used in place of `print`. - Catherine Devlin, Jan 03 2008 - catherinedevlin.blogspot.com Git repository on GitHub at https://github.com/python-cmd2/cmd2 """ # This module has many imports, quite a few of which are only # infrequently utilized. To reduce the initial overhead of # import this module, many of these imports are lazy-loaded # i.e. we only import the module when we use it # For example, we don't import the 'traceback' module # until the pexcept() function is called and the debug # setting is True import argparse import cmd import functools import glob import inspect import os import pydoc import re import sys import threading from code import ( InteractiveConsole, ) from collections import ( OrderedDict, namedtuple, ) from contextlib import ( redirect_stdout, ) from types import ( FrameType, ModuleType, ) from typing import ( Any, Callable, Dict, Iterable, List, Mapping, Optional, Set, TextIO, Tuple, Type, TypeVar, Union, cast, ) from . import ( ansi, argparse_completer, argparse_custom, constants, plugin, utils, ) from .argparse_custom import ( ChoicesProviderFunc, CompleterFunc, CompletionItem, ) from .clipboard import ( can_clip, get_paste_buffer, write_to_paste_buffer, ) from .command_definition import ( CommandFunc, CommandSet, ) from .constants import ( CLASS_ATTR_DEFAULT_HELP_CATEGORY, COMMAND_FUNC_PREFIX, COMPLETER_FUNC_PREFIX, HELP_FUNC_PREFIX, ) from .decorators import ( as_subcommand_to, with_argparser, ) from .exceptions import ( Cmd2ShlexError, CommandSetRegistrationError, CompletionError, EmbeddedConsoleExit, EmptyStatement, PassThroughException, RedirectionError, SkipPostcommandHooks, ) from .history import ( History, HistoryItem, ) from .parsing import ( Macro, MacroArg, Statement, StatementParser, shlex_split, ) from .rl_utils import ( RlType, rl_escape_prompt, rl_get_point, rl_get_prompt, rl_set_prompt, rl_type, rl_warning, vt100_support, ) from .table_creator import ( Column, SimpleTable, ) from .utils import ( Settable, get_defining_class, ) # Set up readline if rl_type == RlType.NONE: # pragma: no cover sys.stderr.write(ansi.style_warning(rl_warning)) else: from .rl_utils import ( # type: ignore[attr-defined] readline, rl_force_redisplay, ) # Used by rlcompleter in Python console loaded by py command orig_rl_delims = readline.get_completer_delims() if rl_type == RlType.PYREADLINE: # Save the original pyreadline display completion function since we need to override it and restore it # noinspection PyProtectedMember,PyUnresolvedReferences orig_pyreadline_display = readline.rl.mode._display_completions elif rl_type == RlType.GNU: # Get the readline lib so we can make changes to it import ctypes from .rl_utils import ( readline_lib, ) rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters") orig_rl_basic_quotes = cast(bytes, ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value) class _SavedReadlineSettings: """readline settings that are backed up when switching between readline environments""" def __init__(self) -> None: self.completer = None self.delims = '' self.basic_quotes: Optional[bytes] = None class _SavedCmd2Env: """cmd2 environment settings that are backed up when entering an interactive Python shell""" def __init__(self) -> None: self.readline_settings = _SavedReadlineSettings() self.readline_module: Optional[ModuleType] = None self.history: List[str] = [] self.sys_stdout: Optional[TextIO] = None self.sys_stdin: Optional[TextIO] = None # Contains data about a disabled command which is used to restore its original functions when the command is enabled DisabledCommand = namedtuple('DisabledCommand', ['command_function', 'help_function', 'completer_function']) class Cmd(cmd.Cmd): """An easy but powerful framework for writing line-oriented command interpreters. Extends the Python Standard Library’s cmd package by adding a lot of useful features to the out of the box configuration. Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes. """ DEFAULT_EDITOR = utils.find_editor() INTERNAL_COMMAND_EPILOG = ( "Notes:\n" " This command is for internal use and is not intended to be called from the\n" " command line." ) # Sorting keys for strings ALPHABETICAL_SORT_KEY = utils.norm_fold NATURAL_SORT_KEY = utils.natural_keys def __init__( self, completekey: str = 'tab', stdin: Optional[TextIO] = None, stdout: Optional[TextIO] = None, *, persistent_history_file: str = '', persistent_history_length: int = 1000, startup_script: str = '', silence_startup_script: bool = False, include_py: bool = False, include_ipy: bool = False, allow_cli_args: bool = True, transcript_files: Optional[List[str]] = None, allow_redirection: bool = True, multiline_commands: Optional[List[str]] = None, terminators: Optional[List[str]] = None, shortcuts: Optional[Dict[str, str]] = None, command_sets: Optional[Iterable[CommandSet]] = None, auto_load_commands: bool = True, ) -> None: """An easy but powerful framework for writing line-oriented command interpreters. Extends Python's cmd package. :param completekey: readline name of a completion key, default to Tab :param stdin: alternate input file object, if not specified, sys.stdin is used :param stdout: alternate output file object, if not specified, sys.stdout is used :param persistent_history_file: file path to load a persistent cmd2 command history from :param persistent_history_length: max number of history items to write to the persistent history file :param startup_script: file path to a script to execute at startup :param silence_startup_script: if ``True``, then the startup script's output will be suppressed. Anything written to stderr will still display. :param include_py: should the "py" command be included for an embedded Python shell :param include_ipy: should the "ipy" command be included for an embedded IPython shell :param allow_cli_args: if ``True``, then :meth:`cmd2.Cmd.__init__` will process command line arguments as either commands to be run or, if ``-t`` or ``--test`` are given, transcript files to run. This should be set to ``False`` if your application parses its own command line arguments. :param transcript_files: pass a list of transcript files to be run on initialization. This allows running transcript tests when ``allow_cli_args`` is ``False``. If ``allow_cli_args`` is ``True`` this parameter is ignored. :param allow_redirection: If ``False``, prevent output redirection and piping to shell commands. This parameter prevents redirection and piping, but does not alter parsing behavior. A user can still type redirection and piping tokens, and they will be parsed as such but they won't do anything. :param multiline_commands: list of commands allowed to accept multi-line input :param terminators: list of characters that terminate a command. These are mainly intended for terminating multiline commands, but will also terminate single-line commands. If not supplied, the default is a semicolon. If your app only contains single-line commands and you want terminators to be treated as literals by the parser, then set this to an empty list. :param shortcuts: dictionary containing shortcuts for commands. If not supplied, then defaults to constants.DEFAULT_SHORTCUTS. If you do not want any shortcuts, pass an empty dictionary. :param command_sets: Provide CommandSet instances to load during cmd2 initialization. This allows CommandSets with custom constructor parameters to be loaded. This also allows the a set of CommandSets to be provided when `auto_load_commands` is set to False :param auto_load_commands: If True, cmd2 will check for all subclasses of `CommandSet` that are currently loaded by Python and automatically instantiate and register all commands. If False, CommandSets must be manually installed with `register_command_set`. """ # Check if py or ipy need to be disabled in this instance if not include_py: setattr(self, 'do_py', None) if not include_ipy: setattr(self, 'do_ipy', None) # initialize plugin system # needs to be done before we call __init__(0) self._initialize_plugin_system() # Call super class constructor super().__init__(completekey=completekey, stdin=stdin, stdout=stdout) # Attributes which should NOT be dynamically settable via the set command at runtime self.default_to_shell = False # Attempt to run unrecognized commands as shell commands self.allow_redirection = allow_redirection # Security setting to prevent redirection of stdout # Attributes which ARE dynamically settable via the set command at runtime self.always_show_hint = False self.debug = False self.echo = False self.editor = Cmd.DEFAULT_EDITOR self.feedback_to_output = False # Do not include nonessentials in >, | output by default (things like timing) self.quiet = False # Do not suppress nonessential output self.timing = False # Prints elapsed time for each command # The maximum number of CompletionItems to display during tab completion. If the number of completion # suggestions exceeds this number, they will be displayed in the typical columnized format and will # not include the description value of the CompletionItems. self.max_completion_items = 50 # A dictionary mapping settable names to their Settable instance self._settables: Dict[str, Settable] = dict() self._always_prefix_settables: bool = False # CommandSet containers self._installed_command_sets: Set[CommandSet] = set() self._cmd_to_command_sets: Dict[str, CommandSet] = {} self.build_settables() # Use as prompt for multiline commands on the 2nd+ line of input self.continuation_prompt = '> ' # Allow access to your application in embedded Python shells and scripts py via self self.self_in_py = False # Commands to exclude from the help menu and tab completion self.hidden_commands = ['eof', '_relative_run_script'] # Initialize history self._persistent_history_length = persistent_history_length self._initialize_history(persistent_history_file) # Commands to exclude from the history command self.exclude_from_history = ['eof', 'history'] # Dictionary of macro names and their values self.macros: Dict[str, Macro] = dict() # Keeps track of typed command history in the Python shell self._py_history: List[str] = [] # The name by which Python environments refer to the PyBridge to call app commands self.py_bridge_name = 'app' # Defines app-specific variables/functions available in Python shells and pyscripts self.py_locals: Dict[str, Any] = dict() # True if running inside a Python shell or pyscript, False otherwise self._in_py = False self.statement_parser = StatementParser( terminators=terminators, multiline_commands=multiline_commands, shortcuts=shortcuts ) # Stores results from the last command run to enable usage of results in Python shells and pyscripts self.last_result: Any = None # Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command self._script_dir: List[str] = [] # Context manager used to protect critical sections in the main thread from stopping due to a KeyboardInterrupt self.sigint_protection = utils.ContextFlag() # If the current command created a process to pipe to, then this will be a ProcReader object. # Otherwise it will be None. It's used to know when a pipe process can be killed and/or waited upon. self._cur_pipe_proc_reader: Optional[utils.ProcReader] = None # Used to keep track of whether we are redirecting or piping output self._redirecting = False # Used to keep track of whether a continuation prompt is being displayed self._at_continuation_prompt = False # The multiline command currently being typed which is used to tab complete multiline commands. self._multiline_in_progress = '' # Set the header used for the help function's listing of documented functions self.doc_header = "Documented commands (use 'help -v' for verbose/'help ' for details):" # The error that prints when no help information can be found self.help_error = "No help on {}" # The error that prints when a non-existent command is run self.default_error = "{} is not a recognized command, alias, or macro" # If non-empty, this string will be displayed if a broken pipe error occurs self.broken_pipe_warning = '' # Commands that will run at the beginning of the command loop self._startup_commands: List[str] = [] # If a startup script is provided and exists, then execute it in the startup commands if startup_script: startup_script = os.path.abspath(os.path.expanduser(startup_script)) if os.path.exists(startup_script): script_cmd = f"run_script {utils.quote_string(startup_script)}" if silence_startup_script: script_cmd += f" {constants.REDIRECTION_OUTPUT} {os.devnull}" self._startup_commands.append(script_cmd) # Transcript files to run instead of interactive command loop self._transcript_files: Optional[List[str]] = None # Check for command line args if allow_cli_args: parser = argparse_custom.DEFAULT_ARGUMENT_PARSER() parser.add_argument('-t', '--test', action="store_true", help='Test against transcript(s) in FILE (wildcards OK)') callopts, callargs = parser.parse_known_args() # If transcript testing was called for, use other arguments as transcript files if callopts.test: self._transcript_files = callargs # If commands were supplied at invocation, then add them to the command queue elif callargs: self._startup_commands.extend(callargs) elif transcript_files: self._transcript_files = transcript_files # Set the pager(s) for use with the ppaged() method for displaying output using a pager if sys.platform.startswith('win'): self.pager = self.pager_chop = 'more' else: # Here is the meaning of the various flags we are using with the less command: # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped # -R causes ANSI "style" escape sequences to be output in raw form (i.e. colors are displayed) # -X disables sending the termcap initialization and deinitialization strings to the terminal # -F causes less to automatically exit if the entire file can be displayed on the first screen self.pager = 'less -RXF' self.pager_chop = 'less -SRXF' # This boolean flag determines whether or not the cmd2 application can interact with the clipboard self._can_clip = can_clip # This determines the value returned by cmdloop() when exiting the application self.exit_code = 0 # This lock should be acquired before doing any asynchronous changes to the terminal to # ensure the updates to the terminal don't interfere with the input being typed or output # being printed by a command. self.terminal_lock = threading.RLock() # Commands that have been disabled from use. This is to support commands that are only available # during specific states of the application. This dictionary's keys are the command names and its # values are DisabledCommand objects. self.disabled_commands: Dict[str, DisabledCommand] = dict() # If any command has been categorized, then all other commands that haven't been categorized # will display under this section in the help output. self.default_category = 'Uncategorized' # The default key for sorting string results. Its default value performs a case-insensitive alphabetical sort. # If natural sorting is preferred, then set this to NATURAL_SORT_KEY. # cmd2 uses this key for sorting: # command and category names # alias, macro, settable, and shortcut names # tab completion results when self.matches_sorted is False self.default_sort_key = Cmd.ALPHABETICAL_SORT_KEY ############################################################################################################ # The following variables are used by tab completion functions. They are reset each time complete() is run # in _reset_completion_defaults() and it is up to completer functions to set them before returning results. ############################################################################################################ # If True and a single match is returned to complete(), then a space will be appended # if the match appears at the end of the line self.allow_appended_space = True # If True and a single match is returned to complete(), then a closing quote # will be added if there is an unmatched opening quote self.allow_closing_quote = True # An optional hint which prints above tab completion suggestions self.completion_hint = '' # Normally cmd2 uses readline's formatter to columnize the list of completion suggestions. # If a custom format is preferred, write the formatted completions to this string. cmd2 will # then print it instead of the readline format. ANSI style sequences and newlines are supported # when using this value. Even when using formatted_completions, the full matches must still be returned # from your completer function. ArgparseCompleter writes its tab completion tables to this string. self.formatted_completions = '' # Used by complete() for readline tab completion self.completion_matches: List[str] = [] # Use this list if you need to display tab completion suggestions that are different than the actual text # of the matches. For instance, if you are completing strings that contain a common delimiter and you only # want to display the final portion of the matches as the tab completion suggestions. The full matches # still must be returned from your completer function. For an example, look at path_complete() which # uses this to show only the basename of paths as the suggestions. delimiter_complete() also populates # this list. These are ignored if self.formatted_completions is populated. self.display_matches: List[str] = [] # Used by functions like path_complete() and delimiter_complete() to properly # quote matches that are completed in a delimited fashion self.matches_delimited = False # Set to True before returning matches to complete() in cases where matches have already been sorted. # If False, then complete() will sort the matches using self.default_sort_key before they are displayed. # This does not affect self.formatted_completions. self.matches_sorted = False ############################################################################################################ # The following code block loads CommandSets, verifies command names, and registers subcommands. # This block should appear after all attributes have been created since the registration code # depends on them and it's possible a module's on_register() method may need to access some. ############################################################################################################ # Load modular commands if command_sets: for command_set in command_sets: self.register_command_set(command_set) if auto_load_commands: self._autoload_commands() # Verify commands don't have invalid names (like starting with a shortcut) for cur_cmd in self.get_all_commands(): valid, errmsg = self.statement_parser.is_valid_command(cur_cmd) if not valid: raise ValueError(f"Invalid command name '{cur_cmd}': {errmsg}") # Add functions decorated to be subcommands self._register_subcommands(self) def find_commandsets(self, commandset_type: Type[CommandSet], *, subclass_match: bool = False) -> List[CommandSet]: """ Find all CommandSets that match the provided CommandSet type. By default, locates a CommandSet that is an exact type match but may optionally return all CommandSets that are sub-classes of the provided type :param commandset_type: CommandSet sub-class type to search for :param subclass_match: If True, return all sub-classes of provided type, otherwise only search for exact match :return: Matching CommandSets """ return [ cmdset for cmdset in self._installed_command_sets if type(cmdset) == commandset_type or (subclass_match and isinstance(cmdset, commandset_type)) ] def find_commandset_for_command(self, command_name: str) -> Optional[CommandSet]: """ Finds the CommandSet that registered the command name :param command_name: command name to search :return: CommandSet that provided the command """ return self._cmd_to_command_sets.get(command_name) def _autoload_commands(self) -> None: """Load modular command definitions.""" # Search for all subclasses of CommandSet, instantiate them if they weren't already provided in the constructor all_commandset_defs = CommandSet.__subclasses__() existing_commandset_types = [type(command_set) for command_set in self._installed_command_sets] def load_commandset_by_type(commandset_types: List[Type[CommandSet]]) -> None: for cmdset_type in commandset_types: # check if the type has sub-classes. We will only auto-load leaf class types. subclasses = cmdset_type.__subclasses__() if subclasses: load_commandset_by_type(subclasses) else: init_sig = inspect.signature(cmdset_type.__init__) if not ( cmdset_type in existing_commandset_types or len(init_sig.parameters) != 1 or 'self' not in init_sig.parameters ): cmdset = cmdset_type() self.register_command_set(cmdset) load_commandset_by_type(all_commandset_defs) def register_command_set(self, cmdset: CommandSet) -> None: """ Installs a CommandSet, loading all commands defined in the CommandSet :param cmdset: CommandSet to load """ existing_commandset_types = [type(command_set) for command_set in self._installed_command_sets] if type(cmdset) in existing_commandset_types: raise CommandSetRegistrationError('CommandSet ' + type(cmdset).__name__ + ' is already installed') all_settables = self.settables if self.always_prefix_settables: if not cmdset.settable_prefix.strip(): raise CommandSetRegistrationError('CommandSet settable prefix must not be empty') for key in cmdset.settables.keys(): prefixed_name = f'{cmdset.settable_prefix}.{key}' if prefixed_name in all_settables: raise CommandSetRegistrationError(f'Duplicate settable: {key}') else: for key in cmdset.settables.keys(): if key in all_settables: raise CommandSetRegistrationError(f'Duplicate settable {key} is already registered') cmdset.on_register(self) methods = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) default_category = getattr(cmdset, CLASS_ATTR_DEFAULT_HELP_CATEGORY, None) installed_attributes = [] try: for method_name, method in methods: command = method_name[len(COMMAND_FUNC_PREFIX) :] self._install_command_function(command, method, type(cmdset).__name__) installed_attributes.append(method_name) completer_func_name = COMPLETER_FUNC_PREFIX + command cmd_completer = getattr(cmdset, completer_func_name, None) if cmd_completer is not None: self._install_completer_function(command, cmd_completer) installed_attributes.append(completer_func_name) help_func_name = HELP_FUNC_PREFIX + command cmd_help = getattr(cmdset, help_func_name, None) if cmd_help is not None: self._install_help_function(command, cmd_help) installed_attributes.append(help_func_name) self._cmd_to_command_sets[command] = cmdset if default_category and not hasattr(method, constants.CMD_ATTR_HELP_CATEGORY): utils.categorize(method, default_category) self._installed_command_sets.add(cmdset) self._register_subcommands(cmdset) cmdset.on_registered() except Exception: cmdset.on_unregister() for attrib in installed_attributes: delattr(self, attrib) if cmdset in self._installed_command_sets: self._installed_command_sets.remove(cmdset) if cmdset in self._cmd_to_command_sets.values(): self._cmd_to_command_sets = {key: val for key, val in self._cmd_to_command_sets.items() if val is not cmdset} cmdset.on_unregistered() raise def _install_command_function(self, command: str, command_wrapper: Callable[..., Any], context: str = '') -> None: cmd_func_name = COMMAND_FUNC_PREFIX + command # Make sure command function doesn't share name with existing attribute if hasattr(self, cmd_func_name): raise CommandSetRegistrationError(f'Attribute already exists: {cmd_func_name} ({context})') # Check if command has an invalid name valid, errmsg = self.statement_parser.is_valid_command(command) if not valid: raise CommandSetRegistrationError(f"Invalid command name '{command}': {errmsg}") # Check if command shares a name with an alias if command in self.aliases: self.pwarning(f"Deleting alias '{command}' because it shares its name with a new command") del self.aliases[command] # Check if command shares a name with a macro if command in self.macros: self.pwarning(f"Deleting macro '{command}' because it shares its name with a new command") del self.macros[command] setattr(self, cmd_func_name, command_wrapper) def _install_completer_function(self, cmd_name: str, cmd_completer: CompleterFunc) -> None: completer_func_name = COMPLETER_FUNC_PREFIX + cmd_name if hasattr(self, completer_func_name): raise CommandSetRegistrationError(f'Attribute already exists: {completer_func_name}') setattr(self, completer_func_name, cmd_completer) def _install_help_function(self, cmd_name: str, cmd_help: Callable[..., None]) -> None: help_func_name = HELP_FUNC_PREFIX + cmd_name if hasattr(self, help_func_name): raise CommandSetRegistrationError(f'Attribute already exists: {help_func_name}') setattr(self, help_func_name, cmd_help) def unregister_command_set(self, cmdset: CommandSet) -> None: """ Uninstalls a CommandSet and unloads all associated commands :param cmdset: CommandSet to uninstall """ if cmdset in self._installed_command_sets: self._check_uninstallable(cmdset) cmdset.on_unregister() self._unregister_subcommands(cmdset) methods = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) for method in methods: cmd_name = method[0][len(COMMAND_FUNC_PREFIX) :] # Enable the command before uninstalling it to make sure we remove both # the real functions and the ones used by the DisabledCommand object. if cmd_name in self.disabled_commands: self.enable_command(cmd_name) if cmd_name in self._cmd_to_command_sets: del self._cmd_to_command_sets[cmd_name] delattr(self, COMMAND_FUNC_PREFIX + cmd_name) if hasattr(self, COMPLETER_FUNC_PREFIX + cmd_name): delattr(self, COMPLETER_FUNC_PREFIX + cmd_name) if hasattr(self, HELP_FUNC_PREFIX + cmd_name): delattr(self, HELP_FUNC_PREFIX + cmd_name) cmdset.on_unregistered() self._installed_command_sets.remove(cmdset) def _check_uninstallable(self, cmdset: CommandSet) -> None: methods = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) for method in methods: command_name = method[0][len(COMMAND_FUNC_PREFIX) :] # Search for the base command function and verify it has an argparser defined if command_name in self.disabled_commands: command_func = self.disabled_commands[command_name].command_function else: command_func = self.cmd_func(command_name) command_parser = cast(argparse.ArgumentParser, getattr(command_func, constants.CMD_ATTR_ARGPARSER, None)) def check_parser_uninstallable(parser: argparse.ArgumentParser) -> None: for action in parser._actions: if isinstance(action, argparse._SubParsersAction): for subparser in action.choices.values(): attached_cmdset = getattr(subparser, constants.PARSER_ATTR_COMMANDSET, None) if attached_cmdset is not None and attached_cmdset is not cmdset: raise CommandSetRegistrationError( 'Cannot uninstall CommandSet when another CommandSet depends on it' ) check_parser_uninstallable(subparser) break if command_parser is not None: check_parser_uninstallable(command_parser) def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None: """ Register subcommands with their base command :param cmdset: CommandSet or cmd2.Cmd subclass containing subcommands """ if not (cmdset is self or cmdset in self._installed_command_sets): raise CommandSetRegistrationError('Cannot register subcommands with an unregistered CommandSet') # find methods that have the required attributes necessary to be recognized as a sub-command methods = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, constants.SUBCMD_ATTR_NAME) and hasattr(meth, constants.SUBCMD_ATTR_COMMAND) and hasattr(meth, constants.CMD_ATTR_ARGPARSER), ) # iterate through all matching methods for method_name, method in methods: subcommand_name: str = getattr(method, constants.SUBCMD_ATTR_NAME) full_command_name: str = getattr(method, constants.SUBCMD_ATTR_COMMAND) subcmd_parser = getattr(method, constants.CMD_ATTR_ARGPARSER) subcommand_valid, errmsg = self.statement_parser.is_valid_command(subcommand_name, is_subcommand=True) if not subcommand_valid: raise CommandSetRegistrationError(f'Subcommand {str(subcommand_name)} is not valid: {errmsg}') command_tokens = full_command_name.split() command_name = command_tokens[0] subcommand_names = command_tokens[1:] # Search for the base command function and verify it has an argparser defined if command_name in self.disabled_commands: command_func = self.disabled_commands[command_name].command_function else: command_func = self.cmd_func(command_name) if command_func is None: raise CommandSetRegistrationError( f"Could not find command '{command_name}' needed by subcommand: {str(method)}" ) command_parser = getattr(command_func, constants.CMD_ATTR_ARGPARSER, None) if command_parser is None: raise CommandSetRegistrationError( f"Could not find argparser for command '{command_name}' needed by subcommand: {str(method)}" ) def find_subcommand(action: argparse.ArgumentParser, subcmd_names: List[str]) -> argparse.ArgumentParser: if not subcmd_names: return action cur_subcmd = subcmd_names.pop(0) for sub_action in action._actions: if isinstance(sub_action, argparse._SubParsersAction): for choice_name, choice in sub_action.choices.items(): if choice_name == cur_subcmd: return find_subcommand(choice, subcmd_names) break raise CommandSetRegistrationError(f"Could not find subcommand '{full_command_name}'") target_parser = find_subcommand(command_parser, subcommand_names) for action in target_parser._actions: if isinstance(action, argparse._SubParsersAction): # Temporary workaround for avoiding subcommand help text repeatedly getting added to # action._choices_actions. Until we have instance-specific parser objects, we will remove # any existing subcommand which has the same name before replacing it. This problem is # exercised when more than one cmd2.Cmd-based object is created and the same subcommands # get added each time. Argparse overwrites the previous subcommand but keeps growing the help # text which is shown by running something like 'alias -h'. action.remove_parser(subcommand_name) # type: ignore[arg-type,attr-defined] # Get the kwargs for add_parser() add_parser_kwargs = getattr(method, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, {}) # Set subcmd_parser as the parent to the parser we're creating to get its arguments add_parser_kwargs['parents'] = [subcmd_parser] # argparse only copies actions from a parent and not the following settings. # To retain these settings, we will copy them from subcmd_parser and pass them # as ArgumentParser constructor arguments to add_parser(). add_parser_kwargs['prog'] = subcmd_parser.prog add_parser_kwargs['usage'] = subcmd_parser.usage add_parser_kwargs['description'] = subcmd_parser.description add_parser_kwargs['epilog'] = subcmd_parser.epilog add_parser_kwargs['formatter_class'] = subcmd_parser.formatter_class add_parser_kwargs['prefix_chars'] = subcmd_parser.prefix_chars add_parser_kwargs['fromfile_prefix_chars'] = subcmd_parser.fromfile_prefix_chars add_parser_kwargs['argument_default'] = subcmd_parser.argument_default add_parser_kwargs['conflict_handler'] = subcmd_parser.conflict_handler add_parser_kwargs['allow_abbrev'] = subcmd_parser.allow_abbrev # Set add_help to False and use whatever help option subcmd_parser already has add_parser_kwargs['add_help'] = False attached_parser = action.add_parser(subcommand_name, **add_parser_kwargs) # Set the subcommand handler defaults = {constants.NS_ATTR_SUBCMD_HANDLER: method} attached_parser.set_defaults(**defaults) # Copy value for custom ArgparseCompleter type, which will be None if not present on subcmd_parser attached_parser.set_ap_completer_type(subcmd_parser.get_ap_completer_type()) # type: ignore[attr-defined] # Set what instance the handler is bound to setattr(attached_parser, constants.PARSER_ATTR_COMMANDSET, cmdset) break def _unregister_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None: """ Unregister subcommands from their base command :param cmdset: CommandSet containing subcommands """ if not (cmdset is self or cmdset in self._installed_command_sets): raise CommandSetRegistrationError('Cannot unregister subcommands with an unregistered CommandSet') # find methods that have the required attributes necessary to be recognized as a sub-command methods = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, constants.SUBCMD_ATTR_NAME) and hasattr(meth, constants.SUBCMD_ATTR_COMMAND) and hasattr(meth, constants.CMD_ATTR_ARGPARSER), ) # iterate through all matching methods for method_name, method in methods: subcommand_name = getattr(method, constants.SUBCMD_ATTR_NAME) command_name = getattr(method, constants.SUBCMD_ATTR_COMMAND) # Search for the base command function and verify it has an argparser defined if command_name in self.disabled_commands: command_func = self.disabled_commands[command_name].command_function else: command_func = self.cmd_func(command_name) if command_func is None: # pragma: no cover # This really shouldn't be possible since _register_subcommands would prevent this from happening # but keeping in case it does for some strange reason raise CommandSetRegistrationError( f"Could not find command '{command_name}' needed by subcommand: {str(method)}" ) command_parser = getattr(command_func, constants.CMD_ATTR_ARGPARSER, None) if command_parser is None: # pragma: no cover # This really shouldn't be possible since _register_subcommands would prevent this from happening # but keeping in case it does for some strange reason raise CommandSetRegistrationError( f"Could not find argparser for command '{command_name}' needed by subcommand: {str(method)}" ) for action in command_parser._actions: if isinstance(action, argparse._SubParsersAction): action.remove_parser(subcommand_name) # type: ignore[arg-type,attr-defined] break @property def always_prefix_settables(self) -> bool: """ Flags whether CommandSet settable values should always be prefixed :return: True if CommandSet settable values will always be prefixed. False if not. """ return self._always_prefix_settables @always_prefix_settables.setter def always_prefix_settables(self, new_value: bool) -> None: """ Set whether CommandSet settable values should always be prefixed. :param new_value: True if CommandSet settable values should always be prefixed. False if not. :raises ValueError: If a registered CommandSet does not have a defined prefix """ if not self._always_prefix_settables and new_value: for cmd_set in self._installed_command_sets: if not cmd_set.settable_prefix: raise ValueError( f'Cannot force settable prefixes. CommandSet {cmd_set.__class__.__name__} does ' f'not have a settable prefix defined.' ) self._always_prefix_settables = new_value @property def settables(self) -> Mapping[str, Settable]: """ Get all available user-settable attributes. This includes settables defined in installed CommandSets :return: Mapping from attribute-name to Settable of all user-settable attributes from """ all_settables = dict(self._settables) for cmd_set in self._installed_command_sets: cmdset_settables = cmd_set.settables for settable_name, settable in cmdset_settables.items(): if self.always_prefix_settables: all_settables[f'{cmd_set.settable_prefix}.{settable_name}'] = settable else: all_settables[settable_name] = settable return all_settables def add_settable(self, settable: Settable) -> None: """ Add a settable parameter to ``self.settables`` :param settable: Settable object being added """ if not self.always_prefix_settables: if settable.name in self.settables.keys() and settable.name not in self._settables.keys(): raise KeyError(f'Duplicate settable: {settable.name}') self._settables[settable.name] = settable def remove_settable(self, name: str) -> None: """ Convenience method for removing a settable parameter from ``self.settables`` :param name: name of the settable being removed :raises: KeyError if the Settable matches this name """ try: del self._settables[name] except KeyError: raise KeyError(name + " is not a settable parameter") def build_settables(self) -> None: """Create the dictionary of user-settable parameters""" def get_allow_style_choices(cli_self: Cmd) -> List[str]: """Used to tab complete allow_style values""" return [val.name.lower() for val in ansi.AllowStyle] def allow_style_type(value: str) -> ansi.AllowStyle: """Converts a string value into an ansi.AllowStyle""" try: return ansi.AllowStyle[value.upper()] except KeyError: raise ValueError( f"must be {ansi.AllowStyle.ALWAYS}, {ansi.AllowStyle.NEVER}, or " f"{ansi.AllowStyle.TERMINAL} (case-insensitive)" ) self.add_settable( Settable( 'allow_style', allow_style_type, 'Allow ANSI text style sequences in output (valid values: ' f'{ansi.AllowStyle.ALWAYS}, {ansi.AllowStyle.NEVER}, {ansi.AllowStyle.TERMINAL})', self, choices_provider=cast(ChoicesProviderFunc, get_allow_style_choices), ) ) self.add_settable( Settable( 'always_show_hint', bool, 'Display tab completion hint even when completion suggestions print', self, ) ) self.add_settable(Settable('debug', bool, "Show full traceback on exception", self)) self.add_settable(Settable('echo', bool, "Echo command issued into output", self)) self.add_settable(Settable('editor', str, "Program used by 'edit'", self)) self.add_settable(Settable('feedback_to_output', bool, "Include nonessentials in '|', '>' results", self)) self.add_settable( Settable('max_completion_items', int, "Maximum number of CompletionItems to display during tab completion", self) ) self.add_settable(Settable('quiet', bool, "Don't print nonessential feedback", self)) self.add_settable(Settable('timing', bool, "Report execution times", self)) # ----- Methods related to presenting output to the user ----- @property def allow_style(self) -> ansi.AllowStyle: """Read-only property needed to support do_set when it reads allow_style""" return ansi.allow_style @allow_style.setter def allow_style(self, new_val: ansi.AllowStyle) -> None: """Setter property needed to support do_set when it updates allow_style""" ansi.allow_style = new_val def _completion_supported(self) -> bool: """Return whether tab completion is supported""" return self.use_rawinput and bool(self.completekey) and rl_type != RlType.NONE @property def visible_prompt(self) -> str: """Read-only property to get the visible prompt with any ANSI style escape codes stripped. Used by transcript testing to make it easier and more reliable when users are doing things like coloring the prompt using ANSI color codes. :return: prompt stripped of any ANSI escape codes """ return ansi.strip_style(self.prompt) def poutput(self, msg: Any = '', *, end: str = '\n') -> None: """Print message to self.stdout and appends a newline by default Also handles BrokenPipeError exceptions for when a command's output has been piped to another process and that process terminates before the cmd2 command is finished executing. :param msg: object to print :param end: string appended after the end of the message, default a newline """ try: ansi.style_aware_write(self.stdout, f"{msg}{end}") except BrokenPipeError: # This occurs if a command's output is being piped to another # process and that process closes before the command is # finished. If you would like your application to print a # warning message, then set the broken_pipe_warning attribute # to the message you want printed. if self.broken_pipe_warning: sys.stderr.write(self.broken_pipe_warning) # noinspection PyMethodMayBeStatic def perror(self, msg: Any = '', *, end: str = '\n', apply_style: bool = True) -> None: """Print message to sys.stderr :param msg: object to print :param end: string appended after the end of the message, default a newline :param apply_style: If True, then ansi.style_error will be applied to the message text. Set to False in cases where the message text already has the desired style. Defaults to True. """ if apply_style: final_msg = ansi.style_error(msg) else: final_msg = str(msg) ansi.style_aware_write(sys.stderr, final_msg + end) def pwarning(self, msg: Any = '', *, end: str = '\n', apply_style: bool = True) -> None: """Wraps perror, but applies ansi.style_warning by default :param msg: object to print :param end: string appended after the end of the message, default a newline :param apply_style: If True, then ansi.style_warning will be applied to the message text. Set to False in cases where the message text already has the desired style. Defaults to True. """ if apply_style: msg = ansi.style_warning(msg) self.perror(msg, end=end, apply_style=False) def pexcept(self, msg: Any, *, end: str = '\n', apply_style: bool = True) -> None: """Print Exception message to sys.stderr. If debug is true, print exception traceback if one exists. :param msg: message or Exception to print :param end: string appended after the end of the message, default a newline :param apply_style: If True, then ansi.style_error will be applied to the message text. Set to False in cases where the message text already has the desired style. Defaults to True. """ if self.debug and sys.exc_info() != (None, None, None): import traceback traceback.print_exc() if isinstance(msg, Exception): final_msg = f"EXCEPTION of type '{type(msg).__name__}' occurred with message: {msg}" else: final_msg = str(msg) if apply_style: final_msg = ansi.style_error(final_msg) if not self.debug and 'debug' in self.settables: warning = "\nTo enable full traceback, run the following command: 'set debug true'" final_msg += ansi.style_warning(warning) self.perror(final_msg, end=end, apply_style=False) def pfeedback(self, msg: Any, *, end: str = '\n') -> None: """For printing nonessential feedback. Can be silenced with `quiet`. Inclusion in redirected output is controlled by `feedback_to_output`. :param msg: object to print :param end: string appended after the end of the message, default a newline """ if not self.quiet: if self.feedback_to_output: self.poutput(msg, end=end) else: self.perror(msg, end=end, apply_style=False) def ppaged(self, msg: Any, *, end: str = '\n', chop: bool = False) -> None: """Print output using a pager if it would go off screen and stdout isn't currently being redirected. Never uses a pager inside of a script (Python or text) or when output is being redirected or piped or when stdout or stdin are not a fully functional terminal. :param msg: object to print :param end: string appended after the end of the message, default a newline :param chop: True -> causes lines longer than the screen width to be chopped (truncated) rather than wrapped - truncated text is still accessible by scrolling with the right & left arrow keys - chopping is ideal for displaying wide tabular data as is done in utilities like pgcli False -> causes lines longer than the screen width to wrap to the next line - wrapping is ideal when you want to keep users from having to use horizontal scrolling WARNING: On Windows, the text always wraps regardless of what the chop argument is set to """ # msg can be any type, so convert to string before checking if it's blank msg_str = str(msg) # Consider None to be no data to print if msg is None or msg_str == '': return try: import subprocess # Attempt to detect if we are not running within a fully functional terminal. # Don't try to use the pager when being run by a continuous integration system like Jenkins + pexpect. functional_terminal = False if self.stdin.isatty() and self.stdout.isatty(): if sys.platform.startswith('win') or os.environ.get('TERM') is not None: functional_terminal = True # Don't attempt to use a pager that can block if redirecting or running a script (either text or Python) # Also only attempt to use a pager if actually running in a real fully functional terminal if functional_terminal and not self._redirecting and not self.in_pyscript() and not self.in_script(): if ansi.allow_style == ansi.AllowStyle.NEVER: msg_str = ansi.strip_style(msg_str) msg_str += end pager = self.pager if chop: pager = self.pager_chop # Prevent KeyboardInterrupts while in the pager. The pager application will # still receive the SIGINT since it is in the same process group as us. with self.sigint_protection: pipe_proc = subprocess.Popen(pager, shell=True, stdin=subprocess.PIPE) pipe_proc.communicate(msg_str.encode('utf-8', 'replace')) else: self.poutput(msg_str, end=end) except BrokenPipeError: # This occurs if a command's output is being piped to another process and that process closes before the # command is finished. If you would like your application to print a warning message, then set the # broken_pipe_warning attribute to the message you want printed.` if self.broken_pipe_warning: sys.stderr.write(self.broken_pipe_warning) # ----- Methods related to tab completion ----- def _reset_completion_defaults(self) -> None: """ Resets tab completion settings Needs to be called each time readline runs tab completion """ self.allow_appended_space = True self.allow_closing_quote = True self.completion_hint = '' self.formatted_completions = '' self.completion_matches = [] self.display_matches = [] self.matches_delimited = False self.matches_sorted = False if rl_type == RlType.GNU: readline.set_completion_display_matches_hook(self._display_matches_gnu_readline) elif rl_type == RlType.PYREADLINE: # noinspection PyUnresolvedReferences readline.rl.mode._display_completions = self._display_matches_pyreadline def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> Tuple[List[str], List[str]]: """Used by tab completion functions to get all tokens through the one being completed. :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :return: A 2 item tuple where the items are **On Success** - tokens: list of unquoted tokens - this is generally the list needed for tab completion functions - raw_tokens: list of tokens with any quotes preserved = this can be used to know if a token was quoted or is missing a closing quote Both lists are guaranteed to have at least 1 item. The last item in both lists is the token being tab completed **On Failure** - Two empty lists """ import copy unclosed_quote = '' quotes_to_try = copy.copy(constants.QUOTES) tmp_line = line[:endidx] tmp_endidx = endidx # Parse the line into tokens while True: try: initial_tokens = shlex_split(tmp_line[:tmp_endidx]) # If the cursor is at an empty token outside of a quoted string, # then that is the token being completed. Add it to the list. if not unclosed_quote and begidx == tmp_endidx: initial_tokens.append('') break except ValueError as ex: # Make sure the exception was due to an unclosed quote and # we haven't exhausted the closing quotes to try if str(ex) == "No closing quotation" and quotes_to_try: # Add a closing quote and try to parse again unclosed_quote = quotes_to_try[0] quotes_to_try = quotes_to_try[1:] tmp_line = line[:endidx] tmp_line += unclosed_quote tmp_endidx = endidx + 1 else: # pragma: no cover # The parsing error is not caused by unclosed quotes. # Return empty lists since this means the line is malformed. return [], [] # Further split tokens on punctuation characters raw_tokens = self.statement_parser.split_on_punctuation(initial_tokens) # Save the unquoted tokens tokens = [utils.strip_quotes(cur_token) for cur_token in raw_tokens] # If the token being completed had an unclosed quote, we need # to remove the closing quote that was added in order for it # to match what was on the command line. if unclosed_quote: raw_tokens[-1] = raw_tokens[-1][:-1] return tokens, raw_tokens # noinspection PyMethodMayBeStatic, PyUnusedLocal def basic_complete( self, text: str, line: str, begidx: int, endidx: int, match_against: Iterable[str], ) -> List[str]: """ Basic tab completion function that matches against a list of strings without considering line contents or cursor position. The args required by this function are defined in the header of Python's cmd.py. :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param match_against: the strings being matched against :return: a list of possible tab completions """ return [cur_match for cur_match in match_against if cur_match.startswith(text)] def delimiter_complete( self, text: str, line: str, begidx: int, endidx: int, match_against: Iterable[str], delimiter: str, ) -> List[str]: """ Performs tab completion against a list but each match is split on a delimiter and only the portion of the match being tab completed is shown as the completion suggestions. This is useful if you match against strings that are hierarchical in nature and have a common delimiter. An easy way to illustrate this concept is path completion since paths are just directories/files delimited by a slash. If you are tab completing items in /home/user you don't get the following as suggestions: /home/user/file.txt /home/user/program.c /home/user/maps/ /home/user/cmd2.py Instead you are shown: file.txt program.c maps/ cmd2.py For a large set of data, this can be visually more pleasing and easier to search. Another example would be strings formatted with the following syntax: company::department::name In this case the delimiter would be :: and the user could easily narrow down what they are looking for if they were only shown suggestions in the category they are at in the string. :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param match_against: the list being matched against :param delimiter: what delimits each portion of the matches (ex: paths are delimited by a slash) :return: a list of possible tab completions """ matches = self.basic_complete(text, line, begidx, endidx, match_against) # Display only the portion of the match that's being completed based on delimiter if matches: # Set this to True for proper quoting of matches with spaces self.matches_delimited = True # Get the common beginning for the matches common_prefix = os.path.commonprefix(matches) prefix_tokens = common_prefix.split(delimiter) # Calculate what portion of the match we are completing display_token_index = 0 if prefix_tokens: display_token_index = len(prefix_tokens) - 1 # Get this portion for each match and store them in self.display_matches for cur_match in matches: match_tokens = cur_match.split(delimiter) display_token = match_tokens[display_token_index] if not display_token: display_token = delimiter self.display_matches.append(display_token) return matches def flag_based_complete( self, text: str, line: str, begidx: int, endidx: int, flag_dict: Dict[str, Union[Iterable[str], CompleterFunc]], *, all_else: Union[None, Iterable[str], CompleterFunc] = None, ) -> List[str]: """Tab completes based on a particular flag preceding the token being completed. :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param flag_dict: dictionary whose structure is the following: `keys` - flags (ex: -c, --create) that result in tab completion for the next argument in the command line `values` - there are two types of values: 1. iterable list of strings to match against (dictionaries, lists, etc.) 2. function that performs tab completion (ex: path_complete) :param all_else: an optional parameter for tab completing any token that isn't preceded by a flag in flag_dict :return: a list of possible tab completions """ # Get all tokens through the one being completed tokens, _ = self.tokens_for_completion(line, begidx, endidx) if not tokens: # pragma: no cover return [] completions_matches = [] match_against = all_else # Must have at least 2 args for a flag to precede the token being completed if len(tokens) > 1: flag = tokens[-2] if flag in flag_dict: match_against = flag_dict[flag] # Perform tab completion using an Iterable if isinstance(match_against, Iterable): completions_matches = self.basic_complete(text, line, begidx, endidx, match_against) # Perform tab completion using a function elif callable(match_against): completions_matches = match_against(text, line, begidx, endidx) return completions_matches def index_based_complete( self, text: str, line: str, begidx: int, endidx: int, index_dict: Mapping[int, Union[Iterable[str], CompleterFunc]], *, all_else: Optional[Union[Iterable[str], CompleterFunc]] = None, ) -> List[str]: """Tab completes based on a fixed position in the input string. :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param index_dict: dictionary whose structure is the following: `keys` - 0-based token indexes into command line that determine which tokens perform tab completion `values` - there are two types of values: 1. iterable list of strings to match against (dictionaries, lists, etc.) 2. function that performs tab completion (ex: path_complete) :param all_else: an optional parameter for tab completing any token that isn't at an index in index_dict :return: a list of possible tab completions """ # Get all tokens through the one being completed tokens, _ = self.tokens_for_completion(line, begidx, endidx) if not tokens: # pragma: no cover return [] matches = [] # Get the index of the token being completed index = len(tokens) - 1 # Check if token is at an index in the dictionary match_against: Optional[Union[Iterable[str], CompleterFunc]] if index in index_dict: match_against = index_dict[index] else: match_against = all_else # Perform tab completion using a Iterable if isinstance(match_against, Iterable): matches = self.basic_complete(text, line, begidx, endidx, match_against) # Perform tab completion using a function elif callable(match_against): matches = match_against(text, line, begidx, endidx) return matches # noinspection PyUnusedLocal def path_complete( self, text: str, line: str, begidx: int, endidx: int, *, path_filter: Optional[Callable[[str], bool]] = None ) -> List[str]: """Performs completion of local file system paths :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param path_filter: optional filter function that determines if a path belongs in the results this function takes a path as its argument and returns True if the path should be kept in the results :return: a list of possible tab completions """ # Used to complete ~ and ~user strings def complete_users() -> List[str]: # We are returning ~user strings that resolve to directories, # so don't append a space or quote in the case of a single result. self.allow_appended_space = False self.allow_closing_quote = False users = [] # Windows lacks the pwd module so we can't get a list of users. # Instead we will return a result once the user enters text that # resolves to an existing home directory. if sys.platform.startswith('win'): expanded_path = os.path.expanduser(text) if os.path.isdir(expanded_path): user = text if add_trailing_sep_if_dir: user += os.path.sep users.append(user) else: import pwd # Iterate through a list of users from the password database for cur_pw in pwd.getpwall(): # Check if the user has an existing home dir if os.path.isdir(cur_pw.pw_dir): # Add a ~ to the user to match against text cur_user = '~' + cur_pw.pw_name if cur_user.startswith(text): if add_trailing_sep_if_dir: cur_user += os.path.sep users.append(cur_user) return users # Determine if a trailing separator should be appended to directory completions add_trailing_sep_if_dir = False if endidx == len(line) or (endidx < len(line) and line[endidx] != os.path.sep): add_trailing_sep_if_dir = True # Used to replace cwd in the final results cwd = os.getcwd() cwd_added = False # Used to replace expanded user path in final result orig_tilde_path = '' expanded_tilde_path = '' # If the search text is blank, then search in the CWD for * if not text: search_str = os.path.join(os.getcwd(), '*') cwd_added = True else: # Purposely don't match any path containing wildcards wildcards = ['*', '?'] for wildcard in wildcards: if wildcard in text: return [] # Start the search string search_str = text + '*' # Handle tilde expansion and completion if text.startswith('~'): sep_index = text.find(os.path.sep, 1) # If there is no slash, then the user is still completing the user after the tilde if sep_index == -1: return complete_users() # Otherwise expand the user dir else: search_str = os.path.expanduser(search_str) # Get what we need to restore the original tilde path later orig_tilde_path = text[:sep_index] expanded_tilde_path = os.path.expanduser(orig_tilde_path) # If the search text does not have a directory, then use the cwd elif not os.path.dirname(text): search_str = os.path.join(os.getcwd(), search_str) cwd_added = True # Set this to True for proper quoting of paths with spaces self.matches_delimited = True # Find all matching path completions matches = glob.glob(search_str) # Filter out results that don't belong if path_filter is not None: matches = [c for c in matches if path_filter(c)] # Don't append a space or closing quote to directory if len(matches) == 1 and os.path.isdir(matches[0]): self.allow_appended_space = False self.allow_closing_quote = False # Sort the matches before any trailing slashes are added matches.sort(key=self.default_sort_key) self.matches_sorted = True # Build display_matches and add a slash to directories for index, cur_match in enumerate(matches): # Display only the basename of this path in the tab completion suggestions self.display_matches.append(os.path.basename(cur_match)) # Add a separator after directories if the next character isn't already a separator if os.path.isdir(cur_match) and add_trailing_sep_if_dir: matches[index] += os.path.sep self.display_matches[index] += os.path.sep # Remove cwd if it was added to match the text readline expects if cwd_added: if cwd == os.path.sep: to_replace = cwd else: to_replace = cwd + os.path.sep matches = [cur_path.replace(to_replace, '', 1) for cur_path in matches] # Restore the tilde string if we expanded one to match the text readline expects if expanded_tilde_path: matches = [cur_path.replace(expanded_tilde_path, orig_tilde_path, 1) for cur_path in matches] return matches def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *, complete_blank: bool = False) -> List[str]: """Performs completion of executables either in a user's path or a given path :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param complete_blank: If True, then a blank will complete all shell commands in a user's path. If False, then no completion is performed. Defaults to False to match Bash shell behavior. :return: a list of possible tab completions """ # Don't tab complete anything if no shell command has been started if not complete_blank and not text: return [] # If there are no path characters in the search text, then do shell command completion in the user's path if not text.startswith('~') and os.path.sep not in text: return utils.get_exes_in_path(text) # Otherwise look for executables in the given path else: return self.path_complete( text, line, begidx, endidx, path_filter=lambda path: os.path.isdir(path) or os.access(path, os.X_OK) ) def _redirect_complete(self, text: str, line: str, begidx: int, endidx: int, compfunc: CompleterFunc) -> List[str]: """Called by complete() as the first tab completion function for all commands It determines if it should tab complete for redirection (|, >, >>) or use the completer function for the current command :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param compfunc: the completer function for the current command this will be called if we aren't completing for redirection :return: a list of possible tab completions """ # Get all tokens through the one being completed. We want the raw tokens # so we can tell if redirection strings are quoted and ignore them. _, raw_tokens = self.tokens_for_completion(line, begidx, endidx) if not raw_tokens: # pragma: no cover return [] # Must at least have the command if len(raw_tokens) > 1: # True when command line contains any redirection tokens has_redirection = False # Keep track of state while examining tokens in_pipe = False in_file_redir = False do_shell_completion = False do_path_completion = False prior_token = None for cur_token in raw_tokens: # Process redirection tokens if cur_token in constants.REDIRECTION_TOKENS: has_redirection = True # Check if we are at a pipe if cur_token == constants.REDIRECTION_PIPE: # Do not complete bad syntax (e.g cmd | |) if prior_token == constants.REDIRECTION_PIPE: return [] in_pipe = True in_file_redir = False # Otherwise this is a file redirection token else: if prior_token in constants.REDIRECTION_TOKENS or in_file_redir: # Do not complete bad syntax (e.g cmd | >) (e.g cmd > blah >) return [] in_pipe = False in_file_redir = True # Only tab complete after redirection tokens if redirection is allowed elif self.allow_redirection: do_shell_completion = False do_path_completion = False if prior_token == constants.REDIRECTION_PIPE: do_shell_completion = True elif in_pipe or prior_token in (constants.REDIRECTION_OUTPUT, constants.REDIRECTION_APPEND): do_path_completion = True prior_token = cur_token if do_shell_completion: return self.shell_cmd_complete(text, line, begidx, endidx) elif do_path_completion: return self.path_complete(text, line, begidx, endidx) # If there were redirection strings anywhere on the command line, then we # are no longer tab completing for the current command elif has_redirection: return [] # Call the command's completer function return compfunc(text, line, begidx, endidx) @staticmethod def _pad_matches_to_display(matches_to_display: List[str]) -> Tuple[List[str], int]: # pragma: no cover """Adds padding to the matches being displayed as tab completion suggestions. The default padding of readline/pyreadine is small and not visually appealing especially if matches have spaces. It appears very squished together. :param matches_to_display: the matches being padded :return: the padded matches and length of padding that was added """ if rl_type == RlType.GNU: # Add 2 to the padding of 2 that readline uses for a total of 4. padding = 2 * ' ' elif rl_type == RlType.PYREADLINE: # Add 3 to the padding of 1 that pyreadline uses for a total of 4. padding = 3 * ' ' else: return matches_to_display, 0 return [cur_match + padding for cur_match in matches_to_display], len(padding) def _display_matches_gnu_readline( self, substitution: str, matches: List[str], longest_match_length: int ) -> None: # pragma: no cover """Prints a match list using GNU readline's rl_display_match_list() :param substitution: the substitution written to the command line :param matches: the tab completion matches to display :param longest_match_length: longest printed length of the matches """ if rl_type == RlType.GNU: # Print hint if one exists and we are supposed to display it hint_printed = False if self.always_show_hint and self.completion_hint: hint_printed = True sys.stdout.write('\n' + self.completion_hint) # Check if we already have formatted results to print if self.formatted_completions: if not hint_printed: sys.stdout.write('\n') sys.stdout.write('\n' + self.formatted_completions + '\n\n') # Otherwise use readline's formatter else: # Check if we should show display_matches if self.display_matches: matches_to_display = self.display_matches # Recalculate longest_match_length for display_matches longest_match_length = 0 for cur_match in matches_to_display: cur_length = ansi.style_aware_wcswidth(cur_match) if cur_length > longest_match_length: longest_match_length = cur_length else: matches_to_display = matches # Add padding for visual appeal matches_to_display, padding_length = self._pad_matches_to_display(matches_to_display) longest_match_length += padding_length # We will use readline's display function (rl_display_match_list()), so we # need to encode our string as bytes to place in a C array. encoded_substitution = bytes(substitution, encoding='utf-8') encoded_matches = [bytes(cur_match, encoding='utf-8') for cur_match in matches_to_display] # rl_display_match_list() expects matches to be in argv format where # substitution is the first element, followed by the matches, and then a NULL. # noinspection PyCallingNonCallable,PyTypeChecker strings_array = cast(List[Optional[bytes]], (ctypes.c_char_p * (1 + len(encoded_matches) + 1))()) # Copy in the encoded strings and add a NULL to the end strings_array[0] = encoded_substitution strings_array[1:-1] = encoded_matches strings_array[-1] = None # rl_display_match_list(strings_array, number of completion matches, longest match length) readline_lib.rl_display_match_list(strings_array, len(encoded_matches), longest_match_length) # Redraw prompt and input line rl_force_redisplay() def _display_matches_pyreadline(self, matches: List[str]) -> None: # pragma: no cover """Prints a match list using pyreadline's _display_completions() :param matches: the tab completion matches to display """ if rl_type == RlType.PYREADLINE: # Print hint if one exists and we are supposed to display it hint_printed = False if self.always_show_hint and self.completion_hint: hint_printed = True readline.rl.mode.console.write('\n' + self.completion_hint) # Check if we already have formatted results to print if self.formatted_completions: if not hint_printed: readline.rl.mode.console.write('\n') readline.rl.mode.console.write('\n' + self.formatted_completions + '\n\n') # Redraw the prompt and input lines rl_force_redisplay() # Otherwise use pyreadline's formatter else: # Check if we should show display_matches if self.display_matches: matches_to_display = self.display_matches else: matches_to_display = matches # Add padding for visual appeal matches_to_display, _ = self._pad_matches_to_display(matches_to_display) # Display matches using actual display function. This also redraws the prompt and input lines. orig_pyreadline_display(matches_to_display) @staticmethod def _determine_ap_completer_type(parser: argparse.ArgumentParser) -> Type[argparse_completer.ArgparseCompleter]: """ Determine what type of ArgparseCompleter to use on a given parser. If the parser does not have one set, then use argparse_completer.DEFAULT_AP_COMPLETER. :param parser: the parser to examine :return: type of ArgparseCompleter """ completer_type: Optional[ Type[argparse_completer.ArgparseCompleter] ] = parser.get_ap_completer_type() # type: ignore[attr-defined] if completer_type is None: completer_type = argparse_completer.DEFAULT_AP_COMPLETER return completer_type def _perform_completion( self, text: str, line: str, begidx: int, endidx: int, custom_settings: Optional[utils.CustomCompletionSettings] = None ) -> None: """ Helper function for complete() that performs the actual completion :param text: the string prefix we are attempting to match (all matches must begin with it) :param line: the current input line with leading whitespace removed :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param custom_settings: optional prepopulated completion settings """ # If custom_settings is None, then we are completing a command's argument. # Parse the command line to get the command token. command = '' if custom_settings is None: statement = self.statement_parser.parse_command_only(line) command = statement.command # Malformed command line (e.g. quoted command token) if not command: return expanded_line = statement.command_and_args # We overwrote line with a properly formatted but fully stripped version # Restore the end spaces since line is only supposed to be lstripped when # passed to completer functions according to Python docs rstripped_len = len(line) - len(line.rstrip()) expanded_line += ' ' * rstripped_len # Fix the index values if expanded_line has a different size than line if len(expanded_line) != len(line): diff = len(expanded_line) - len(line) begidx += diff endidx += diff # Overwrite line to pass into completers line = expanded_line # Get all tokens through the one being completed tokens, raw_tokens = self.tokens_for_completion(line, begidx, endidx) if not tokens: # pragma: no cover return # Determine the completer function to use for the command's argument if custom_settings is None: # Check if a macro was entered if command in self.macros: completer_func = self.path_complete # Check if a command was entered elif command in self.get_all_commands(): # Get the completer function for this command func_attr = getattr(self, constants.COMPLETER_FUNC_PREFIX + command, None) if func_attr is not None: completer_func = func_attr else: # There's no completer function, next see if the command uses argparse func = self.cmd_func(command) argparser: Optional[argparse.ArgumentParser] = getattr(func, constants.CMD_ATTR_ARGPARSER, None) if func is not None and argparser is not None: # Get arguments for complete() preserve_quotes = getattr(func, constants.CMD_ATTR_PRESERVE_QUOTES) cmd_set = self._cmd_to_command_sets[command] if command in self._cmd_to_command_sets else None # Create the argparse completer completer_type = self._determine_ap_completer_type(argparser) completer = completer_type(argparser, self) completer_func = functools.partial( completer.complete, tokens=raw_tokens[1:] if preserve_quotes else tokens[1:], cmd_set=cmd_set ) else: completer_func = self.completedefault # type: ignore[assignment] # Not a recognized macro or command else: # Check if this command should be run as a shell command if self.default_to_shell and command in utils.get_exes_in_path(command): completer_func = self.path_complete else: completer_func = self.completedefault # type: ignore[assignment] # Otherwise we are completing the command token or performing custom completion else: # Create the argparse completer completer_type = self._determine_ap_completer_type(custom_settings.parser) completer = completer_type(custom_settings.parser, self) completer_func = functools.partial( completer.complete, tokens=raw_tokens if custom_settings.preserve_quotes else tokens, cmd_set=None ) # Text we need to remove from completions later text_to_remove = '' # Get the token being completed with any opening quote preserved raw_completion_token = raw_tokens[-1] # Used for adding quotes to the completion token completion_token_quote = '' # Check if the token being completed has an opening quote if raw_completion_token and raw_completion_token[0] in constants.QUOTES: # Since the token is still being completed, we know the opening quote is unclosed. # Save the quote so we can add a matching closing quote later. completion_token_quote = raw_completion_token[0] # readline still performs word breaks after a quote. Therefore something like quoted search # text with a space would have resulted in begidx pointing to the middle of the token we # we want to complete. Figure out where that token actually begins and save the beginning # portion of it that was not part of the text readline gave us. We will remove it from the # completions later since readline expects them to start with the original text. actual_begidx = line[:endidx].rfind(tokens[-1]) if actual_begidx != begidx: text_to_remove = line[actual_begidx:begidx] # Adjust text and where it begins so the completer routines # get unbroken search text to complete on. text = text_to_remove + text begidx = actual_begidx # Attempt tab completion for redirection first, and if that isn't occurring, # call the completer function for the current command self.completion_matches = self._redirect_complete(text, line, begidx, endidx, completer_func) if self.completion_matches: # Eliminate duplicates self.completion_matches = utils.remove_duplicates(self.completion_matches) self.display_matches = utils.remove_duplicates(self.display_matches) if not self.display_matches: # Since self.display_matches is empty, set it to self.completion_matches # before we alter them. That way the suggestions will reflect how we parsed # the token being completed and not how readline did. import copy self.display_matches = copy.copy(self.completion_matches) # Check if we need to add an opening quote if not completion_token_quote: add_quote = False # This is the tab completion text that will appear on the command line. common_prefix = os.path.commonprefix(self.completion_matches) if self.matches_delimited: # Check if any portion of the display matches appears in the tab completion display_prefix = os.path.commonprefix(self.display_matches) # For delimited matches, we check for a space in what appears before the display # matches (common_prefix) as well as in the display matches themselves. if ' ' in common_prefix or (display_prefix and any(' ' in match for match in self.display_matches)): add_quote = True # If there is a tab completion and any match has a space, then add an opening quote elif common_prefix and any(' ' in match for match in self.completion_matches): add_quote = True if add_quote: # Figure out what kind of quote to add and save it as the unclosed_quote if any('"' in match for match in self.completion_matches): completion_token_quote = "'" else: completion_token_quote = '"' self.completion_matches = [completion_token_quote + match for match in self.completion_matches] # Check if we need to remove text from the beginning of tab completions elif text_to_remove: self.completion_matches = [match.replace(text_to_remove, '', 1) for match in self.completion_matches] # If we have one result, then add a closing quote if needed and allowed if len(self.completion_matches) == 1 and self.allow_closing_quote and completion_token_quote: self.completion_matches[0] += completion_token_quote def complete( # type: ignore[override] self, text: str, state: int, custom_settings: Optional[utils.CustomCompletionSettings] = None ) -> Optional[str]: """Override of cmd's complete method which returns the next possible completion for 'text' This completer function is called by readline as complete(text, state), for state in 0, 1, 2, …, until it returns a non-string value. It should return the next possible completion starting with text. Since readline suppresses any exception raised in completer functions, they can be difficult to debug. Therefore this function wraps the actual tab completion logic and prints to stderr any exception that occurs before returning control to readline. :param text: the current word that user is typing :param state: non-negative integer :param custom_settings: used when not tab completing the main command line :return: the next possible completion for text or None """ # noinspection PyBroadException try: if state == 0: self._reset_completion_defaults() # Check if we are completing a multiline command if self._at_continuation_prompt: # lstrip and prepend the previously typed portion of this multiline command lstripped_previous = self._multiline_in_progress.lstrip().replace(constants.LINE_FEED, ' ') line = lstripped_previous + readline.get_line_buffer() # Increment the indexes to account for the prepended text begidx = len(lstripped_previous) + readline.get_begidx() endidx = len(lstripped_previous) + readline.get_endidx() else: # lstrip the original line orig_line = readline.get_line_buffer() line = orig_line.lstrip() num_stripped = len(orig_line) - len(line) # Calculate new indexes for the stripped line. If the cursor is at a position before the end of a # line of spaces, then the following math could result in negative indexes. Enforce a max of 0. begidx = max(readline.get_begidx() - num_stripped, 0) endidx = max(readline.get_endidx() - num_stripped, 0) # Shortcuts are not word break characters when tab completing. Therefore shortcuts become part # of the text variable if there isn't a word break, like a space, after it. We need to remove it # from text and update the indexes. This only applies if we are at the beginning of the command line. shortcut_to_restore = '' if begidx == 0 and custom_settings is None: for (shortcut, _) in self.statement_parser.shortcuts: if text.startswith(shortcut): # Save the shortcut to restore later shortcut_to_restore = shortcut # Adjust text and where it begins text = text[len(shortcut_to_restore) :] begidx += len(shortcut_to_restore) break else: # No shortcut was found. Complete the command token. parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False) parser.add_argument( 'command', metavar="COMMAND", help="command, alias, or macro name", choices=self._get_commands_aliases_and_macros_for_completion(), ) custom_settings = utils.CustomCompletionSettings(parser) self._perform_completion(text, line, begidx, endidx, custom_settings) # Check if we need to restore a shortcut in the tab completions # so it doesn't get erased from the command line if shortcut_to_restore: self.completion_matches = [shortcut_to_restore + match for match in self.completion_matches] # If we have one result and we are at the end of the line, then add a space if allowed if len(self.completion_matches) == 1 and endidx == len(line) and self.allow_appended_space: self.completion_matches[0] += ' ' # Sort matches if they haven't already been sorted if not self.matches_sorted: self.completion_matches.sort(key=self.default_sort_key) self.display_matches.sort(key=self.default_sort_key) self.matches_sorted = True try: return self.completion_matches[state] except IndexError: return None except CompletionError as ex: # Don't print error and redraw the prompt unless the error has length err_str = str(ex) if err_str: if ex.apply_style: err_str = ansi.style_error(err_str) ansi.style_aware_write(sys.stdout, '\n' + err_str + '\n') rl_force_redisplay() return None except Exception as ex: # Insert a newline so the exception doesn't print in the middle of the command line being tab completed self.perror() self.pexcept(ex) rl_force_redisplay() return None def in_script(self) -> bool: """Return whether a text script is running""" return self._current_script_dir is not None def in_pyscript(self) -> bool: """Return whether running inside a Python shell or pyscript""" return self._in_py @property def aliases(self) -> Dict[str, str]: """Read-only property to access the aliases stored in the StatementParser""" return self.statement_parser.aliases def get_names(self) -> List[str]: """Return an alphabetized list of names comprising the attributes of the cmd2 class instance.""" return dir(self) def get_all_commands(self) -> List[str]: """Return a list of all commands""" return [ name[len(constants.COMMAND_FUNC_PREFIX) :] for name in self.get_names() if name.startswith(constants.COMMAND_FUNC_PREFIX) and callable(getattr(self, name)) ] def get_visible_commands(self) -> List[str]: """Return a list of commands that have not been hidden or disabled""" return [ command for command in self.get_all_commands() if command not in self.hidden_commands and command not in self.disabled_commands ] # Table displayed when tab completing aliases _alias_completion_table = SimpleTable([Column('Value', width=80)], divider_char=None) def _get_alias_completion_items(self) -> List[CompletionItem]: """Return list of alias names and values as CompletionItems""" results: List[CompletionItem] = [] for cur_key in self.aliases: row_data = [self.aliases[cur_key]] results.append(CompletionItem(cur_key, self._alias_completion_table.generate_data_row(row_data))) return results # Table displayed when tab completing macros _macro_completion_table = SimpleTable([Column('Value', width=80)], divider_char=None) def _get_macro_completion_items(self) -> List[CompletionItem]: """Return list of macro names and values as CompletionItems""" results: List[CompletionItem] = [] for cur_key in self.macros: row_data = [self.macros[cur_key].value] results.append(CompletionItem(cur_key, self._macro_completion_table.generate_data_row(row_data))) return results # Table displayed when tab completing Settables _settable_completion_table = SimpleTable([Column('Value', width=30), Column('Description', width=60)], divider_char=None) def _get_settable_completion_items(self) -> List[CompletionItem]: """Return list of Settable names, values, and descriptions as CompletionItems""" results: List[CompletionItem] = [] for cur_key in self.settables: row_data = [self.settables[cur_key].get_value(), self.settables[cur_key].description] results.append(CompletionItem(cur_key, self._settable_completion_table.generate_data_row(row_data))) return results def _get_commands_aliases_and_macros_for_completion(self) -> List[str]: """Return a list of visible commands, aliases, and macros for tab completion""" visible_commands = set(self.get_visible_commands()) alias_names = set(self.aliases) macro_names = set(self.macros) return list(visible_commands | alias_names | macro_names) def get_help_topics(self) -> List[str]: """Return a list of help topics""" all_topics = [ name[len(constants.HELP_FUNC_PREFIX) :] for name in self.get_names() if name.startswith(constants.HELP_FUNC_PREFIX) and callable(getattr(self, name)) ] # Filter out hidden and disabled commands return [topic for topic in all_topics if topic not in self.hidden_commands and topic not in self.disabled_commands] # noinspection PyUnusedLocal def sigint_handler(self, signum: int, _: FrameType) -> None: """Signal handler for SIGINTs which typically come from Ctrl-C events. If you need custom SIGINT behavior, then override this function. :param signum: signal number :param _: required param for signal handlers """ if self._cur_pipe_proc_reader is not None: # Pass the SIGINT to the current pipe process self._cur_pipe_proc_reader.send_sigint() # Check if we are allowed to re-raise the KeyboardInterrupt if not self.sigint_protection: self._raise_keyboard_interrupt() def _raise_keyboard_interrupt(self) -> None: """Helper function to raise a KeyboardInterrupt""" raise KeyboardInterrupt("Got a keyboard interrupt") def precmd(self, statement: Union[Statement, str]) -> Statement: """Hook method executed just before the command is executed by :meth:`~cmd2.Cmd.onecmd` and after adding it to history. :param statement: subclass of str which also contains the parsed input :return: a potentially modified version of the input Statement object See :meth:`~cmd2.Cmd.register_postparsing_hook` and :meth:`~cmd2.Cmd.register_precmd_hook` for more robust ways to run hooks before the command is executed. See :ref:`features/hooks:Postparsing Hooks` and :ref:`features/hooks:Precommand Hooks` for more information. """ return Statement(statement) if not isinstance(statement, Statement) else statement def postcmd(self, stop: bool, statement: Union[Statement, str]) -> bool: """Hook method executed just after a command is executed by :meth:`~cmd2.Cmd.onecmd`. :param stop: return `True` to request the command loop terminate :param statement: subclass of str which also contains the parsed input See :meth:`~cmd2.Cmd.register_postcmd_hook` and :meth:`~cmd2.Cmd.register_cmdfinalization_hook` for more robust ways to run hooks after the command is executed. See :ref:`features/hooks:Postcommand Hooks` and :ref:`features/hooks:Command Finalization Hooks` for more information. """ return stop def preloop(self) -> None: """Hook method executed once when the :meth:`~.cmd2.Cmd.cmdloop()` method is called. See :meth:`~cmd2.Cmd.register_preloop_hook` for a more robust way to run hooks before the command loop begins. See :ref:`features/hooks:Application Lifecycle Hooks` for more information. """ pass def postloop(self) -> None: """Hook method executed once when the :meth:`~.cmd2.Cmd.cmdloop()` method is about to return. See :meth:`~cmd2.Cmd.register_postloop_hook` for a more robust way to run hooks after the command loop completes. See :ref:`features/hooks:Application Lifecycle Hooks` for more information. """ pass def parseline(self, line: str) -> Tuple[str, str, str]: """Parse the line into a command name and a string containing the arguments. NOTE: This is an override of a parent class method. It is only used by other parent class methods. Different from the parent class method, this ignores self.identchars. :param line: line read by readline :return: tuple containing (command, args, line) """ statement = self.statement_parser.parse_command_only(line) return statement.command, statement.args, statement.command_and_args def onecmd_plus_hooks( self, line: str, *, add_to_history: bool = True, raise_keyboard_interrupt: bool = False, py_bridge_call: bool = False ) -> bool: """Top-level function called by cmdloop() to handle parsing a line and running the command and all of its hooks. :param line: command line to run :param add_to_history: If True, then add this command to history. Defaults to True. :param raise_keyboard_interrupt: if True, then KeyboardInterrupt exceptions will be raised if stop isn't already True. This is used when running commands in a loop to be able to stop the whole loop and not just the current command. Defaults to False. :param py_bridge_call: This should only ever be set to True by PyBridge to signify the beginning of an app() call from Python. It is used to enable/disable the storage of the command's stdout. :return: True if running of commands should stop """ import datetime stop = False statement = None try: # Convert the line into a Statement statement = self._input_line_to_statement(line) # call the postparsing hooks postparsing_data = plugin.PostparsingData(False, statement) for postparsing_func in self._postparsing_hooks: postparsing_data = postparsing_func(postparsing_data) if postparsing_data.stop: break # unpack the postparsing_data object statement = postparsing_data.statement stop = postparsing_data.stop if stop: # we should not run the command, but # we need to run the finalization hooks raise EmptyStatement redir_saved_state: Optional[utils.RedirectionSavedState] = None try: # Get sigint protection while we set up redirection with self.sigint_protection: if py_bridge_call: # Start saving command's stdout at this point self.stdout.pause_storage = False # type: ignore[attr-defined] redir_saved_state = self._redirect_output(statement) timestart = datetime.datetime.now() # precommand hooks precmd_data = plugin.PrecommandData(statement) for precmd_func in self._precmd_hooks: precmd_data = precmd_func(precmd_data) statement = precmd_data.statement # call precmd() for compatibility with cmd.Cmd statement = self.precmd(statement) # go run the command function stop = self.onecmd(statement, add_to_history=add_to_history) # postcommand hooks postcmd_data = plugin.PostcommandData(stop, statement) for postcmd_func in self._postcmd_hooks: postcmd_data = postcmd_func(postcmd_data) # retrieve the final value of stop, ignoring any statement modification from the hooks stop = postcmd_data.stop # call postcmd() for compatibility with cmd.Cmd stop = self.postcmd(stop, statement) if self.timing: self.pfeedback(f'Elapsed: {datetime.datetime.now() - timestart}') finally: # Get sigint protection while we restore stuff with self.sigint_protection: if redir_saved_state is not None: self._restore_output(statement, redir_saved_state) if py_bridge_call: # Stop saving command's stdout before command finalization hooks run self.stdout.pause_storage = True # type: ignore[attr-defined] except (SkipPostcommandHooks, EmptyStatement): # Don't do anything, but do allow command finalization hooks to run pass except Cmd2ShlexError as ex: self.perror(f"Invalid syntax: {ex}") except RedirectionError as ex: self.perror(ex) except KeyboardInterrupt as ex: if raise_keyboard_interrupt and not stop: raise ex except SystemExit as ex: self.exit_code = ex.code stop = True except PassThroughException as ex: raise ex.wrapped_ex except Exception as ex: self.pexcept(ex) finally: try: stop = self._run_cmdfinalization_hooks(stop, statement) except KeyboardInterrupt as ex: if raise_keyboard_interrupt and not stop: raise ex except SystemExit as ex: self.exit_code = ex.code stop = True except PassThroughException as ex: raise ex.wrapped_ex except Exception as ex: self.pexcept(ex) return stop def _run_cmdfinalization_hooks(self, stop: bool, statement: Optional[Statement]) -> bool: """Run the command finalization hooks""" with self.sigint_protection: if not sys.platform.startswith('win') and self.stdin.isatty(): # Before the next command runs, fix any terminal problems like those # caused by certain binary characters having been printed to it. import subprocess proc = subprocess.Popen(['stty', 'sane']) proc.communicate() data = plugin.CommandFinalizationData(stop, statement) for func in self._cmdfinalization_hooks: data = func(data) # retrieve the final value of stop, ignoring any # modifications to the statement return data.stop def runcmds_plus_hooks( self, cmds: Union[List[HistoryItem], List[str]], *, add_to_history: bool = True, stop_on_keyboard_interrupt: bool = False, ) -> bool: """ Used when commands are being run in an automated fashion like text scripts or history replays. The prompt and command line for each command will be printed if echo is True. :param cmds: commands to run :param add_to_history: If True, then add these commands to history. Defaults to True. :param stop_on_keyboard_interrupt: if True, then stop running contents of cmds if Ctrl-C is pressed instead of moving to the next command in the list. This is used when the commands are part of a group, like a text script, which should stop upon Ctrl-C. Defaults to False. :return: True if running of commands should stop """ for line in cmds: if isinstance(line, HistoryItem): line = line.raw if self.echo: self.poutput(f'{self.prompt}{line}') try: if self.onecmd_plus_hooks( line, add_to_history=add_to_history, raise_keyboard_interrupt=stop_on_keyboard_interrupt ): return True except KeyboardInterrupt as ex: if stop_on_keyboard_interrupt: self.perror(ex) break return False def _complete_statement(self, line: str) -> Statement: """Keep accepting lines of input until the command is complete. There is some pretty hacky code here to handle some quirks of self._read_command_line(). It returns a literal 'eof' if the input pipe runs out. We can't refactor it because we need to retain backwards compatibility with the standard library version of cmd. :param line: the line being parsed :return: the completed Statement :raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation) :raises: EmptyStatement when the resulting Statement is blank """ while True: try: statement = self.statement_parser.parse(line) if statement.multiline_command and statement.terminator: # we have a completed multiline command, we are done break if not statement.multiline_command: # it's not a multiline command, but we parsed it ok # so we are done break except Cmd2ShlexError: # we have unclosed quotation marks, lets parse only the command # and see if it's a multiline statement = self.statement_parser.parse_command_only(line) if not statement.multiline_command: # not a multiline command, so raise the exception raise # if we get here we must have: # - a multiline command with no terminator # - a multiline command with unclosed quotation marks try: self._at_continuation_prompt = True # Save the command line up to this point for tab completion self._multiline_in_progress = line + '\n' nextline = self._read_command_line(self.continuation_prompt) if nextline == 'eof': # they entered either a blank line, or we hit an EOF # for some other reason. Turn the literal 'eof' # into a blank line, which serves as a command # terminator nextline = '\n' self.poutput(nextline) line = f'{self._multiline_in_progress}{nextline}' except KeyboardInterrupt: self.poutput('^C') statement = self.statement_parser.parse('') break finally: self._at_continuation_prompt = False if not statement.command: raise EmptyStatement return statement def _input_line_to_statement(self, line: str) -> Statement: """ Parse the user's input line and convert it to a Statement, ensuring that all macros are also resolved :param line: the line being parsed :return: parsed command line as a Statement :raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation) :raises: EmptyStatement when the resulting Statement is blank """ used_macros = [] orig_line = None # Continue until all macros are resolved while True: # Make sure all input has been read and convert it to a Statement statement = self._complete_statement(line) # Save the fully entered line if this is the first loop iteration if orig_line is None: orig_line = statement.raw # Check if this command matches a macro and wasn't already processed to avoid an infinite loop if statement.command in self.macros.keys() and statement.command not in used_macros: used_macros.append(statement.command) resolve_result = self._resolve_macro(statement) if resolve_result is None: raise EmptyStatement line = resolve_result else: break # This will be true when a macro was used if orig_line != statement.raw: # Build a Statement that contains the resolved macro line # but the originally typed line for its raw member. statement = Statement( statement.args, raw=orig_line, command=statement.command, arg_list=statement.arg_list, multiline_command=statement.multiline_command, terminator=statement.terminator, suffix=statement.suffix, pipe_to=statement.pipe_to, output=statement.output, output_to=statement.output_to, ) return statement def _resolve_macro(self, statement: Statement) -> Optional[str]: """ Resolve a macro and return the resulting string :param statement: the parsed statement from the command line :return: the resolved macro or None on error """ if statement.command not in self.macros.keys(): raise KeyError(f"{statement.command} is not a macro") macro = self.macros[statement.command] # Make sure enough arguments were passed in if len(statement.arg_list) < macro.minimum_arg_count: plural = '' if macro.minimum_arg_count == 1 else 's' self.perror(f"The macro '{statement.command}' expects at least {macro.minimum_arg_count} argument{plural}") return None # Resolve the arguments in reverse and read their values from statement.argv since those # are unquoted. Macro args should have been quoted when the macro was created. resolved = macro.value reverse_arg_list = sorted(macro.arg_list, key=lambda ma: ma.start_index, reverse=True) for macro_arg in reverse_arg_list: if macro_arg.is_escaped: to_replace = '{{' + macro_arg.number_str + '}}' replacement = '{' + macro_arg.number_str + '}' else: to_replace = '{' + macro_arg.number_str + '}' replacement = statement.argv[int(macro_arg.number_str)] parts = resolved.rsplit(to_replace, maxsplit=1) resolved = parts[0] + replacement + parts[1] # Append extra arguments and use statement.arg_list since these arguments need their quotes preserved for stmt_arg in statement.arg_list[macro.minimum_arg_count :]: resolved += ' ' + stmt_arg # Restore any terminator, suffix, redirection, etc. return resolved + statement.post_command def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState: """Set up a command's output redirection for >, >>, and |. :param statement: a parsed statement from the user :return: A bool telling if an error occurred and a utils.RedirectionSavedState object :raises: RedirectionError if an error occurs trying to pipe or redirect """ import io import subprocess # Initialize the redirection saved state redir_saved_state = utils.RedirectionSavedState( cast(TextIO, self.stdout), sys.stdout, self._cur_pipe_proc_reader, self._redirecting ) # The ProcReader for this command cmd_pipe_proc_reader: Optional[utils.ProcReader] = None if not self.allow_redirection: # Don't return since we set some state variables at the end of the function pass elif statement.pipe_to: # Create a pipe with read and write sides read_fd, write_fd = os.pipe() # Open each side of the pipe subproc_stdin = io.open(read_fd, 'r') new_stdout: TextIO = cast(TextIO, io.open(write_fd, 'w')) # Create pipe process in a separate group to isolate our signals from it. If a Ctrl-C event occurs, # our sigint handler will forward it only to the most recent pipe process. This makes sure pipe # processes close in the right order (most recent first). kwargs: Dict[str, Any] = dict() if sys.platform == 'win32': kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP else: kwargs['start_new_session'] = True # Attempt to run the pipe process in the user's preferred shell instead of the default behavior of using sh. shell = os.environ.get("SHELL") if shell: kwargs['executable'] = shell # For any stream that is a StdSim, we will use a pipe so we can capture its output proc = subprocess.Popen( # type: ignore[call-overload] statement.pipe_to, stdin=subproc_stdin, stdout=subprocess.PIPE if isinstance(self.stdout, utils.StdSim) else self.stdout, # type: ignore[unreachable] stderr=subprocess.PIPE if isinstance(sys.stderr, utils.StdSim) else sys.stderr, # type: ignore[unreachable] shell=True, **kwargs, ) # Popen was called with shell=True so the user can chain pipe commands and redirect their output # like: !ls -l | grep user | wc -l > out.txt. But this makes it difficult to know if the pipe process # started OK, since the shell itself always starts. Therefore, we will wait a short time and check # if the pipe process is still running. try: proc.wait(0.2) except subprocess.TimeoutExpired: pass # Check if the pipe process already exited if proc.returncode is not None: subproc_stdin.close() new_stdout.close() raise RedirectionError(f'Pipe process exited with code {proc.returncode} before command could run') else: redir_saved_state.redirecting = True # type: ignore[unreachable] cmd_pipe_proc_reader = utils.ProcReader(proc, cast(TextIO, self.stdout), sys.stderr) sys.stdout = self.stdout = new_stdout elif statement.output: import tempfile if (not statement.output_to) and (not self._can_clip): raise RedirectionError("Cannot redirect to paste buffer; missing 'pyperclip' and/or pyperclip dependencies") # Redirecting to a file elif statement.output_to: # statement.output can only contain REDIRECTION_APPEND or REDIRECTION_OUTPUT mode = 'a' if statement.output == constants.REDIRECTION_APPEND else 'w' try: # Use line buffering new_stdout = cast(TextIO, open(utils.strip_quotes(statement.output_to), mode=mode, buffering=1)) except OSError as ex: raise RedirectionError(f'Failed to redirect because: {ex}') redir_saved_state.redirecting = True sys.stdout = self.stdout = new_stdout # Redirecting to a paste buffer else: new_stdout = cast(TextIO, tempfile.TemporaryFile(mode="w+")) redir_saved_state.redirecting = True sys.stdout = self.stdout = new_stdout if statement.output == constants.REDIRECTION_APPEND: self.stdout.write(get_paste_buffer()) self.stdout.flush() # These are updated regardless of whether the command redirected self._cur_pipe_proc_reader = cmd_pipe_proc_reader self._redirecting = redir_saved_state.redirecting return redir_saved_state def _restore_output(self, statement: Statement, saved_redir_state: utils.RedirectionSavedState) -> None: """Handles restoring state after output redirection :param statement: Statement object which contains the parsed input from the user :param saved_redir_state: contains information needed to restore state data """ if saved_redir_state.redirecting: # If we redirected output to the clipboard if statement.output and not statement.output_to: self.stdout.seek(0) write_to_paste_buffer(self.stdout.read()) try: # Close the file or pipe that stdout was redirected to self.stdout.close() except BrokenPipeError: pass # Restore the stdout values self.stdout = cast(TextIO, saved_redir_state.saved_self_stdout) sys.stdout = cast(TextIO, saved_redir_state.saved_sys_stdout) # Check if we need to wait for the process being piped to if self._cur_pipe_proc_reader is not None: self._cur_pipe_proc_reader.wait() # These are restored regardless of whether the command redirected self._cur_pipe_proc_reader = saved_redir_state.saved_pipe_proc_reader self._redirecting = saved_redir_state.saved_redirecting def cmd_func(self, command: str) -> Optional[CommandFunc]: """ Get the function for a command :param command: the name of the command :Example: >>> helpfunc = self.cmd_func('help') helpfunc now contains a reference to the ``do_help`` method """ func_name = self._cmd_func_name(command) if func_name: return cast(Optional[CommandFunc], getattr(self, func_name)) return None def _cmd_func_name(self, command: str) -> str: """Get the method name associated with a given command. :param command: command to look up method name which implements it :return: method name which implements the given command """ target = constants.COMMAND_FUNC_PREFIX + command return target if callable(getattr(self, target, None)) else '' # noinspection PyMethodOverriding def onecmd(self, statement: Union[Statement, str], *, add_to_history: bool = True) -> bool: """This executes the actual do_* method for a command. If the command provided doesn't exist, then it executes default() instead. :param statement: intended to be a Statement instance parsed command from the input stream, alternative acceptance of a str is present only for backward compatibility with cmd :param add_to_history: If True, then add this command to history. Defaults to True. :return: a flag indicating whether the interpretation of commands should stop """ # For backwards compatibility with cmd, allow a str to be passed in if not isinstance(statement, Statement): statement = self._input_line_to_statement(statement) func = self.cmd_func(statement.command) if func: # Check to see if this command should be stored in history if ( statement.command not in self.exclude_from_history and statement.command not in self.disabled_commands and add_to_history ): self.history.append(statement) stop = func(statement) else: stop = self.default(statement) return stop if stop is not None else False def default(self, statement: Statement) -> Optional[bool]: # type: ignore[override] """Executed when the command given isn't a recognized command implemented by a do_* method. :param statement: Statement object with parsed input """ if self.default_to_shell: if 'shell' not in self.exclude_from_history: self.history.append(statement) # noinspection PyTypeChecker return self.do_shell(statement.command_and_args) else: err_msg = self.default_error.format(statement.command) # Set apply_style to False so default_error's style is not overridden self.perror(err_msg, apply_style=False) return None def read_input( self, prompt: str, *, history: Optional[List[str]] = None, completion_mode: utils.CompletionMode = utils.CompletionMode.NONE, preserve_quotes: bool = False, choices: Optional[Iterable[Any]] = None, choices_provider: Optional[ChoicesProviderFunc] = None, completer: Optional[CompleterFunc] = None, parser: Optional[argparse.ArgumentParser] = None, ) -> str: """ Read input from appropriate stdin value. Also supports tab completion and up-arrow history while input is being entered. :param prompt: prompt to display to user :param history: optional list of strings to use for up-arrow history. If completion_mode is CompletionMode.COMMANDS and this is None, then cmd2's command list history will be used. The passed in history will not be edited. It is the caller's responsibility to add the returned input to history if desired. Defaults to None. :param completion_mode: tells what type of tab completion to support. Tab completion only works when self.use_rawinput is True and sys.stdin is a terminal. Defaults to CompletionMode.NONE. The following optional settings apply when completion_mode is CompletionMode.CUSTOM: :param preserve_quotes: if True, then quoted tokens will keep their quotes when processed by ArgparseCompleter. This is helpful in cases when you're tab completing flag-like tokens (e.g. -o, --option) and you don't want them to be treated as argparse flags when quoted. Set this to True if you plan on passing the string to argparse with the tokens still quoted. A maximum of one of these should be provided: :param choices: iterable of accepted values for single argument :param choices_provider: function that provides choices for single argument :param completer: tab completion function that provides choices for single argument :param parser: an argument parser which supports the tab completion of multiple arguments :return: the line read from stdin with all trailing new lines removed :raises: any exceptions raised by input() and stdin.readline() """ readline_configured = False saved_completer: Optional[CompleterFunc] = None saved_history: Optional[List[str]] = None def configure_readline() -> None: """Configure readline tab completion and history""" nonlocal readline_configured nonlocal saved_completer nonlocal saved_history nonlocal parser if readline_configured: # pragma: no cover return # Configure tab completion if self._completion_supported(): saved_completer = readline.get_completer() # Disable completion if completion_mode == utils.CompletionMode.NONE: # noinspection PyUnusedLocal def complete_none(text: str, state: int) -> Optional[str]: # pragma: no cover return None complete_func = complete_none # Complete commands elif completion_mode == utils.CompletionMode.COMMANDS: complete_func = self.complete # Set custom completion settings else: if parser is None: parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False) parser.add_argument( 'arg', suppress_tab_hint=True, choices=choices, # type: ignore[arg-type] choices_provider=choices_provider, completer=completer, ) custom_settings = utils.CustomCompletionSettings(parser, preserve_quotes=preserve_quotes) complete_func = functools.partial(self.complete, custom_settings=custom_settings) readline.set_completer(complete_func) # Overwrite history if not completing commands or new history was provided if completion_mode != utils.CompletionMode.COMMANDS or history is not None: saved_history = [] for i in range(1, readline.get_current_history_length() + 1): # noinspection PyArgumentList saved_history.append(readline.get_history_item(i)) readline.clear_history() if history is not None: for item in history: readline.add_history(item) readline_configured = True def restore_readline() -> None: """Restore readline tab completion and history""" nonlocal readline_configured if not readline_configured: # pragma: no cover return if self._completion_supported(): readline.set_completer(saved_completer) if saved_history is not None: readline.clear_history() for item in saved_history: readline.add_history(item) readline_configured = False # Check we are reading from sys.stdin if self.use_rawinput: if sys.stdin.isatty(): try: # Deal with the vagaries of readline and ANSI escape codes escaped_prompt = rl_escape_prompt(prompt) with self.sigint_protection: configure_readline() line = input(escaped_prompt) finally: with self.sigint_protection: restore_readline() else: line = input() if self.echo: sys.stdout.write(f'{prompt}{line}\n') # Otherwise read from self.stdin else: if self.stdin.isatty(): # on a tty, print the prompt first, then read the line self.poutput(prompt, end='') self.stdout.flush() line = self.stdin.readline() if len(line) == 0: line = 'eof' else: # we are reading from a pipe, read the line to see if there is # anything there, if so, then decide whether to print the # prompt or not line = self.stdin.readline() if len(line): # we read something, output the prompt and the something if self.echo: self.poutput(f'{prompt}{line}') else: line = 'eof' return line.rstrip('\r\n') def _read_command_line(self, prompt: str) -> str: """ Read command line from appropriate stdin :param prompt: prompt to display to user :return: command line text of 'eof' if an EOFError was caught :raises: whatever exceptions are raised by input() except for EOFError """ try: # Wrap in try since terminal_lock may not be locked try: # Command line is about to be drawn. Allow asynchronous changes to the terminal. self.terminal_lock.release() except RuntimeError: pass return self.read_input(prompt, completion_mode=utils.CompletionMode.COMMANDS) except EOFError: return 'eof' finally: # Command line is gone. Do not allow asynchronous changes to the terminal. self.terminal_lock.acquire() def _set_up_cmd2_readline(self) -> _SavedReadlineSettings: """ Called at beginning of command loop to set up readline with cmd2-specific settings :return: Class containing saved readline settings """ readline_settings = _SavedReadlineSettings() if self._completion_supported(): # Set up readline for our tab completion needs if rl_type == RlType.GNU: # Set GNU readline's rl_basic_quote_characters to NULL so it won't automatically add a closing quote # We don't need to worry about setting rl_completion_suppress_quote since we never declared # rl_completer_quote_characters. readline_settings.basic_quotes = cast(bytes, ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value) rl_basic_quote_characters.value = None readline_settings.completer = readline.get_completer() readline.set_completer(self.complete) # Set the readline word delimiters for completion completer_delims = " \t\n" completer_delims += ''.join(constants.QUOTES) completer_delims += ''.join(constants.REDIRECTION_CHARS) completer_delims += ''.join(self.statement_parser.terminators) readline_settings.delims = readline.get_completer_delims() readline.set_completer_delims(completer_delims) # Enable tab completion readline.parse_and_bind(self.completekey + ": complete") return readline_settings def _restore_readline(self, readline_settings: _SavedReadlineSettings) -> None: """ Called at end of command loop to restore saved readline settings :param readline_settings: the readline settings to restore """ if self._completion_supported(): # Restore what we changed in readline readline.set_completer(readline_settings.completer) readline.set_completer_delims(readline_settings.delims) if rl_type == RlType.GNU: readline.set_completion_display_matches_hook(None) rl_basic_quote_characters.value = readline_settings.basic_quotes elif rl_type == RlType.PYREADLINE: # noinspection PyUnresolvedReferences readline.rl.mode._display_completions = orig_pyreadline_display def _cmdloop(self) -> None: """Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. This serves the same role as cmd.cmdloop(). """ saved_readline_settings = None try: # Get sigint protection while we set up readline for cmd2 with self.sigint_protection: saved_readline_settings = self._set_up_cmd2_readline() # Run startup commands stop = self.runcmds_plus_hooks(self._startup_commands) self._startup_commands.clear() while not stop: # Get commands from user try: line = self._read_command_line(self.prompt) except KeyboardInterrupt: self.poutput('^C') line = '' # Run the command along with all associated pre and post hooks stop = self.onecmd_plus_hooks(line) finally: # Get sigint protection while we restore readline settings with self.sigint_protection: if saved_readline_settings is not None: self._restore_readline(saved_readline_settings) ############################################################# # Parsers and functions for alias command and subcommands ############################################################# # Top-level parser for alias alias_description = "Manage aliases\n" "\n" "An alias is a command that enables replacement of a word by another string." alias_epilog = "See also:\n" " macro" alias_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_description, epilog=alias_epilog) alias_subparsers = alias_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND') alias_subparsers.required = True # Preserve quotes since we are passing strings to other commands @with_argparser(alias_parser, preserve_quotes=True) def do_alias(self, args: argparse.Namespace) -> None: """Manage aliases""" # Call handler for whatever subcommand was selected handler = args.cmd2_handler.get() handler(args) # alias -> create alias_create_description = "Create or overwrite an alias" alias_create_epilog = ( "Notes:\n" " If you want to use redirection, pipes, or terminators in the value of the\n" " alias, then quote them.\n" "\n" " Since aliases are resolved during parsing, tab completion will function as\n" " it would for the actual command the alias resolves to.\n" "\n" "Examples:\n" " alias create ls !ls -lF\n" " alias create show_log !cat \"log file.txt\"\n" " alias create save_results print_results \">\" out.txt\n" ) alias_create_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description=alias_create_description, epilog=alias_create_epilog ) alias_create_parser.add_argument('name', help='name of this alias') alias_create_parser.add_argument( 'command', help='what the alias resolves to', choices_provider=_get_commands_aliases_and_macros_for_completion ) alias_create_parser.add_argument( 'command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer=path_complete ) @as_subcommand_to('alias', 'create', alias_create_parser, help=alias_create_description.lower()) def _alias_create(self, args: argparse.Namespace) -> None: """Create or overwrite an alias""" self.last_result = False # Validate the alias name valid, errmsg = self.statement_parser.is_valid_command(args.name) if not valid: self.perror(f"Invalid alias name: {errmsg}") return if args.name in self.get_all_commands(): self.perror("Alias cannot have the same name as a command") return if args.name in self.macros: self.perror("Alias cannot have the same name as a macro") return # Unquote redirection and terminator tokens tokens_to_unquote = constants.REDIRECTION_TOKENS tokens_to_unquote.extend(self.statement_parser.terminators) utils.unquote_specific_tokens(args.command_args, tokens_to_unquote) # Build the alias value string value = args.command if args.command_args: value += ' ' + ' '.join(args.command_args) # Set the alias result = "overwritten" if args.name in self.aliases else "created" self.poutput(f"Alias '{args.name}' {result}") self.aliases[args.name] = value self.last_result = True # alias -> delete alias_delete_help = "delete aliases" alias_delete_description = "Delete specified aliases or all aliases if --all is used" alias_delete_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_delete_description) alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases") alias_delete_parser.add_argument( 'names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to delete', choices_provider=_get_alias_completion_items, descriptive_header=_alias_completion_table.generate_header(), ) @as_subcommand_to('alias', 'delete', alias_delete_parser, help=alias_delete_help) def _alias_delete(self, args: argparse.Namespace) -> None: """Delete aliases""" self.last_result = True if args.all: self.aliases.clear() self.poutput("All aliases deleted") elif not args.names: self.perror("Either --all or alias name(s) must be specified") self.last_result = False else: for cur_name in utils.remove_duplicates(args.names): if cur_name in self.aliases: del self.aliases[cur_name] self.poutput(f"Alias '{cur_name}' deleted") else: self.perror(f"Alias '{cur_name}' does not exist") # alias -> list alias_list_help = "list aliases" alias_list_description = ( "List specified aliases in a reusable form that can be saved to a startup\n" "script to preserve aliases across sessions\n" "\n" "Without arguments, all aliases will be listed." ) alias_list_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_list_description) alias_list_parser.add_argument( 'names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to list', choices_provider=_get_alias_completion_items, descriptive_header=_alias_completion_table.generate_header(), ) @as_subcommand_to('alias', 'list', alias_list_parser, help=alias_list_help) def _alias_list(self, args: argparse.Namespace) -> None: """List some or all aliases as 'alias create' commands""" self.last_result = {} # Dict[alias_name, alias_value] tokens_to_quote = constants.REDIRECTION_TOKENS tokens_to_quote.extend(self.statement_parser.terminators) if args.names: to_list = utils.remove_duplicates(args.names) else: to_list = sorted(self.aliases, key=self.default_sort_key) not_found: List[str] = [] for name in to_list: if name not in self.aliases: not_found.append(name) continue # Quote redirection and terminator tokens for the 'alias create' command tokens = shlex_split(self.aliases[name]) command = tokens[0] command_args = tokens[1:] utils.quote_specific_tokens(command_args, tokens_to_quote) val = command if command_args: val += ' ' + ' '.join(command_args) self.poutput(f"alias create {name} {val}") self.last_result[name] = val for name in not_found: self.perror(f"Alias '{name}' not found") ############################################################# # Parsers and functions for macro command and subcommands ############################################################# # Top-level parser for macro macro_description = "Manage macros\n" "\n" "A macro is similar to an alias, but it can contain argument placeholders." macro_epilog = "See also:\n" " alias" macro_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_description, epilog=macro_epilog) macro_subparsers = macro_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND') macro_subparsers.required = True # Preserve quotes since we are passing strings to other commands @with_argparser(macro_parser, preserve_quotes=True) def do_macro(self, args: argparse.Namespace) -> None: """Manage macros""" # Call handler for whatever subcommand was selected handler = args.cmd2_handler.get() handler(args) # macro -> create macro_create_help = "create or overwrite a macro" macro_create_description = "Create or overwrite a macro" macro_create_epilog = ( "A macro is similar to an alias, but it can contain argument placeholders.\n" "Arguments are expressed when creating a macro using {#} notation where {1}\n" "means the first argument.\n" "\n" "The following creates a macro called my_macro that expects two arguments:\n" "\n" " macro create my_macro make_dinner --meat {1} --veggie {2}\n" "\n" "When the macro is called, the provided arguments are resolved and the\n" "assembled command is run. For example:\n" "\n" " my_macro beef broccoli ---> make_dinner --meat beef --veggie broccoli\n" "\n" "Notes:\n" " To use the literal string {1} in your command, escape it this way: {{1}}.\n" "\n" " Extra arguments passed to a macro are appended to resolved command.\n" "\n" " An argument number can be repeated in a macro. In the following example the\n" " first argument will populate both {1} instances.\n" "\n" " macro create ft file_taxes -p {1} -q {2} -r {1}\n" "\n" " To quote an argument in the resolved command, quote it during creation.\n" "\n" " macro create backup !cp \"{1}\" \"{1}.orig\"\n" "\n" " If you want to use redirection, pipes, or terminators in the value of the\n" " macro, then quote them.\n" "\n" " macro create show_results print_results -type {1} \"|\" less\n" "\n" " Because macros do not resolve until after hitting Enter, tab completion\n" " will only complete paths while typing a macro." ) macro_create_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description=macro_create_description, epilog=macro_create_epilog ) macro_create_parser.add_argument('name', help='name of this macro') macro_create_parser.add_argument( 'command', help='what the macro resolves to', choices_provider=_get_commands_aliases_and_macros_for_completion ) macro_create_parser.add_argument( 'command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer=path_complete ) @as_subcommand_to('macro', 'create', macro_create_parser, help=macro_create_help) def _macro_create(self, args: argparse.Namespace) -> None: """Create or overwrite a macro""" self.last_result = False # Validate the macro name valid, errmsg = self.statement_parser.is_valid_command(args.name) if not valid: self.perror(f"Invalid macro name: {errmsg}") return if args.name in self.get_all_commands(): self.perror("Macro cannot have the same name as a command") return if args.name in self.aliases: self.perror("Macro cannot have the same name as an alias") return # Unquote redirection and terminator tokens tokens_to_unquote = constants.REDIRECTION_TOKENS tokens_to_unquote.extend(self.statement_parser.terminators) utils.unquote_specific_tokens(args.command_args, tokens_to_unquote) # Build the macro value string value = args.command if args.command_args: value += ' ' + ' '.join(args.command_args) # Find all normal arguments arg_list = [] normal_matches = re.finditer(MacroArg.macro_normal_arg_pattern, value) max_arg_num = 0 arg_nums = set() while True: try: cur_match = normal_matches.__next__() # Get the number string between the braces cur_num_str = re.findall(MacroArg.digit_pattern, cur_match.group())[0] cur_num = int(cur_num_str) if cur_num < 1: self.perror("Argument numbers must be greater than 0") return arg_nums.add(cur_num) if cur_num > max_arg_num: max_arg_num = cur_num arg_list.append(MacroArg(start_index=cur_match.start(), number_str=cur_num_str, is_escaped=False)) except StopIteration: break # Make sure the argument numbers are continuous if len(arg_nums) != max_arg_num: self.perror(f"Not all numbers between 1 and {max_arg_num} are present in the argument placeholders") return # Find all escaped arguments escaped_matches = re.finditer(MacroArg.macro_escaped_arg_pattern, value) while True: try: cur_match = escaped_matches.__next__() # Get the number string between the braces cur_num_str = re.findall(MacroArg.digit_pattern, cur_match.group())[0] arg_list.append(MacroArg(start_index=cur_match.start(), number_str=cur_num_str, is_escaped=True)) except StopIteration: break # Set the macro result = "overwritten" if args.name in self.macros else "created" self.poutput(f"Macro '{args.name}' {result}") self.macros[args.name] = Macro(name=args.name, value=value, minimum_arg_count=max_arg_num, arg_list=arg_list) self.last_result = True # macro -> delete macro_delete_help = "delete macros" macro_delete_description = "Delete specified macros or all macros if --all is used" macro_delete_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_delete_description) macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros") macro_delete_parser.add_argument( 'names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to delete', choices_provider=_get_macro_completion_items, descriptive_header=_macro_completion_table.generate_header(), ) @as_subcommand_to('macro', 'delete', macro_delete_parser, help=macro_delete_help) def _macro_delete(self, args: argparse.Namespace) -> None: """Delete macros""" self.last_result = True if args.all: self.macros.clear() self.poutput("All macros deleted") elif not args.names: self.perror("Either --all or macro name(s) must be specified") self.last_result = False else: for cur_name in utils.remove_duplicates(args.names): if cur_name in self.macros: del self.macros[cur_name] self.poutput(f"Macro '{cur_name}' deleted") else: self.perror(f"Macro '{cur_name}' does not exist") # macro -> list macro_list_help = "list macros" macro_list_description = ( "List specified macros in a reusable form that can be saved to a startup script\n" "to preserve macros across sessions\n" "\n" "Without arguments, all macros will be listed." ) macro_list_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_list_description) macro_list_parser.add_argument( 'names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to list', choices_provider=_get_macro_completion_items, descriptive_header=_macro_completion_table.generate_header(), ) @as_subcommand_to('macro', 'list', macro_list_parser, help=macro_list_help) def _macro_list(self, args: argparse.Namespace) -> None: """List some or all macros as 'macro create' commands""" self.last_result = {} # Dict[macro_name, macro_value] tokens_to_quote = constants.REDIRECTION_TOKENS tokens_to_quote.extend(self.statement_parser.terminators) if args.names: to_list = utils.remove_duplicates(args.names) else: to_list = sorted(self.macros, key=self.default_sort_key) not_found: List[str] = [] for name in to_list: if name not in self.macros: not_found.append(name) continue # Quote redirection and terminator tokens for the 'macro create' command tokens = shlex_split(self.macros[name].value) command = tokens[0] command_args = tokens[1:] utils.quote_specific_tokens(command_args, tokens_to_quote) val = command if command_args: val += ' ' + ' '.join(command_args) self.poutput(f"macro create {name} {val}") self.last_result[name] = val for name in not_found: self.perror(f"Macro '{name}' not found") def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: """Completes the command argument of help""" # Complete token against topics and visible commands topics = set(self.get_help_topics()) visible_commands = set(self.get_visible_commands()) strs_to_match = list(topics | visible_commands) return self.basic_complete(text, line, begidx, endidx, strs_to_match) def complete_help_subcommands( self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Dict[str, List[str]] ) -> List[str]: """Completes the subcommands argument of help""" # Make sure we have a command whose subcommands we will complete command = arg_tokens['command'][0] if not command: return [] # Check if this command uses argparse func = self.cmd_func(command) argparser = getattr(func, constants.CMD_ATTR_ARGPARSER, None) if func is None or argparser is None: return [] completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, self) return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens['subcommands']) help_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description="List available commands or provide " "detailed help for a specific command" ) help_parser.add_argument( '-v', '--verbose', action='store_true', help="print a list of all commands with descriptions of each" ) help_parser.add_argument( 'command', nargs=argparse.OPTIONAL, help="command to retrieve help for", completer=complete_help_command ) help_parser.add_argument( 'subcommands', nargs=argparse.REMAINDER, help="subcommand(s) to retrieve help for", completer=complete_help_subcommands ) # Get rid of cmd's complete_help() functions so ArgparseCompleter will complete the help command if getattr(cmd.Cmd, 'complete_help', None) is not None: delattr(cmd.Cmd, 'complete_help') @with_argparser(help_parser) def do_help(self, args: argparse.Namespace) -> None: """List available commands or provide detailed help for a specific command""" self.last_result = True if not args.command or args.verbose: self._help_menu(args.verbose) else: # Getting help for a specific command func = self.cmd_func(args.command) help_func = getattr(self, constants.HELP_FUNC_PREFIX + args.command, None) argparser = getattr(func, constants.CMD_ATTR_ARGPARSER, None) # If the command function uses argparse, then use argparse's help if func is not None and argparser is not None: completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, self) # Set end to blank so the help output matches how it looks when "command -h" is used self.poutput(completer.format_help(args.subcommands), end='') # If there is a help func delegate to do_help elif help_func is not None: super().do_help(args.command) # If there's no help_func __doc__ then format and output it elif func is not None and func.__doc__ is not None: self.poutput(pydoc.getdoc(func)) # If there is no help information then print an error else: err_msg = self.help_error.format(args.command) # Set apply_style to False so help_error's style is not overridden self.perror(err_msg, apply_style=False) self.last_result = False def print_topics(self, header: str, cmds: Optional[List[str]], cmdlen: int, maxcol: int) -> None: """ Print groups of commands and topics in columns and an optional header Override of cmd's print_topics() to handle headers with newlines, ANSI style sequences, and wide characters :param header: string to print above commands being printed :param cmds: list of topics to print :param cmdlen: unused, even by cmd's version :param maxcol: max number of display columns to fit into """ if cmds: self.poutput(header) if self.ruler: divider = utils.align_left('', fill_char=self.ruler, width=ansi.widest_line(header)) self.poutput(divider) self.columnize(cmds, maxcol - 1) self.poutput() def columnize(self, str_list: Optional[List[str]], display_width: int = 80) -> None: """Display a list of single-line strings as a compact set of columns. Override of cmd's print_topics() to handle strings with ANSI style sequences and wide characters Each column is only as wide as necessary. Columns are separated by two spaces (one was not legible enough). """ if not str_list: self.poutput("") return nonstrings = [i for i in range(len(str_list)) if not isinstance(str_list[i], str)] if nonstrings: raise TypeError(f"str_list[i] not a string for i in {nonstrings}") size = len(str_list) if size == 1: self.poutput(str_list[0]) return # Try every row count from 1 upwards for nrows in range(1, len(str_list)): ncols = (size + nrows - 1) // nrows colwidths = [] totwidth = -2 for col in range(ncols): colwidth = 0 for row in range(nrows): i = row + nrows * col if i >= size: break x = str_list[i] colwidth = max(colwidth, ansi.style_aware_wcswidth(x)) colwidths.append(colwidth) totwidth += colwidth + 2 if totwidth > display_width: break if totwidth <= display_width: break else: nrows = len(str_list) ncols = 1 colwidths = [0] for row in range(nrows): texts = [] for col in range(ncols): i = row + nrows * col if i >= size: x = "" else: x = str_list[i] texts.append(x) while texts and not texts[-1]: del texts[-1] for col in range(len(texts)): texts[col] = utils.align_left(texts[col], width=colwidths[col]) self.poutput(" ".join(texts)) def _help_menu(self, verbose: bool = False) -> None: """Show a list of commands which help can be displayed for""" cmds_cats, cmds_doc, cmds_undoc, help_topics = self._build_command_info() if not cmds_cats: # No categories found, fall back to standard behavior self.poutput(self.doc_leader) self._print_topics(self.doc_header, cmds_doc, verbose) else: # Categories found, Organize all commands by category self.poutput(self.doc_leader) self.poutput(self.doc_header, end="\n\n") for category in sorted(cmds_cats.keys(), key=self.default_sort_key): self._print_topics(category, cmds_cats[category], verbose) self._print_topics(self.default_category, cmds_doc, verbose) self.print_topics(self.misc_header, help_topics, 15, 80) self.print_topics(self.undoc_header, cmds_undoc, 15, 80) def _build_command_info(self) -> Tuple[Dict[str, List[str]], List[str], List[str], List[str]]: # Get a sorted list of help topics help_topics = sorted(self.get_help_topics(), key=self.default_sort_key) # Get a sorted list of visible command names visible_commands = sorted(self.get_visible_commands(), key=self.default_sort_key) cmds_doc: List[str] = [] cmds_undoc: List[str] = [] cmds_cats: Dict[str, List[str]] = {} for command in visible_commands: func = self.cmd_func(command) has_help_func = False if command in help_topics: # Prevent the command from showing as both a command and help topic in the output help_topics.remove(command) # Non-argparse commands can have help_functions for their documentation if not hasattr(func, constants.CMD_ATTR_ARGPARSER): has_help_func = True if hasattr(func, constants.CMD_ATTR_HELP_CATEGORY): category: str = getattr(func, constants.CMD_ATTR_HELP_CATEGORY) cmds_cats.setdefault(category, []) cmds_cats[category].append(command) elif func.__doc__ or has_help_func: cmds_doc.append(command) else: cmds_undoc.append(command) return cmds_cats, cmds_doc, cmds_undoc, help_topics def _print_topics(self, header: str, cmds: List[str], verbose: bool) -> None: """Customized version of print_topics that can switch between verbose or traditional output""" import io if cmds: if not verbose: self.print_topics(header, cmds, 15, 80) else: # Find the widest command widest = max([ansi.style_aware_wcswidth(command) for command in cmds]) # Define the table structure name_column = Column('', width=max(widest, 20)) desc_column = Column('', width=80) topic_table = SimpleTable([name_column, desc_column], divider_char=self.ruler) # Build the topic table table_str_buf = io.StringIO() if header: table_str_buf.write(header + "\n") divider = topic_table.generate_divider() if divider: table_str_buf.write(divider + "\n") # Try to get the documentation string for each command topics = self.get_help_topics() for command in cmds: cmd_func = self.cmd_func(command) doc: Optional[str] # Non-argparse commands can have help_functions for their documentation if not hasattr(cmd_func, constants.CMD_ATTR_ARGPARSER) and command in topics: help_func = getattr(self, constants.HELP_FUNC_PREFIX + command) result = io.StringIO() # try to redirect system stdout with redirect_stdout(result): # save our internal stdout stdout_orig = self.stdout try: # redirect our internal stdout self.stdout = cast(TextIO, result) help_func() finally: # restore internal stdout self.stdout = stdout_orig doc = result.getvalue() else: doc = cmd_func.__doc__ # Attempt to locate the first documentation block cmd_desc = '' if doc: found_first = False for doc_line in doc.splitlines(): stripped_line = doc_line.strip() # Don't include :param type lines if stripped_line.startswith(':'): if found_first: break elif stripped_line: if found_first: cmd_desc += "\n" cmd_desc += stripped_line found_first = True elif found_first: break # Add this command to the table table_row = topic_table.generate_data_row([command, cmd_desc]) table_str_buf.write(table_row + '\n') self.poutput(table_str_buf.getvalue()) shortcuts_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="List available shortcuts") @with_argparser(shortcuts_parser) def do_shortcuts(self, _: argparse.Namespace) -> None: """List available shortcuts""" # Sort the shortcut tuples by name sorted_shortcuts = sorted(self.statement_parser.shortcuts, key=lambda x: self.default_sort_key(x[0])) result = "\n".join('{}: {}'.format(sc[0], sc[1]) for sc in sorted_shortcuts) self.poutput(f"Shortcuts for other commands:\n{result}") self.last_result = True eof_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description="Called when Ctrl-D is pressed", epilog=INTERNAL_COMMAND_EPILOG ) @with_argparser(eof_parser) def do_eof(self, _: argparse.Namespace) -> Optional[bool]: """ Called when Ctrl-D is pressed and calls quit with no arguments. This can be overridden if quit should be called differently. """ self.poutput() # self.last_result will be set by do_quit() # noinspection PyTypeChecker return self.do_quit('') quit_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Exit this application") @with_argparser(quit_parser) def do_quit(self, _: argparse.Namespace) -> Optional[bool]: """Exit this application""" # Return True to stop the command loop self.last_result = True return True def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]], prompt: str = 'Your choice? ') -> Any: """Presents a numbered menu to the user. Modeled after the bash shell's SELECT. Returns the item chosen. Argument ``opts`` can be: | a single string -> will be split into one-word options | a list of strings -> will be offered as options | a list of tuples -> interpreted as (value, text), so that the return value can differ from the text advertised to the user""" local_opts: Union[List[str], List[Tuple[Any, Optional[str]]]] if isinstance(opts, str): local_opts = cast(List[Tuple[Any, Optional[str]]], list(zip(opts.split(), opts.split()))) else: local_opts = opts fulloptions: List[Tuple[Any, Optional[str]]] = [] for opt in local_opts: if isinstance(opt, str): fulloptions.append((opt, opt)) else: try: fulloptions.append((opt[0], opt[1])) except IndexError: fulloptions.append((opt[0], opt[0])) for (idx, (_, text)) in enumerate(fulloptions): self.poutput(' %2d. %s' % (idx + 1, text)) while True: try: response = self.read_input(prompt) except EOFError: response = '' self.poutput() except KeyboardInterrupt as ex: self.poutput('^C') raise ex if not response: continue try: choice = int(response) if choice < 1: raise IndexError return fulloptions[choice - 1][0] except (ValueError, IndexError): self.poutput(f"'{response}' isn't a valid choice. Pick a number between 1 and {len(fulloptions)}:") def complete_set_value( self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Dict[str, List[str]] ) -> List[str]: """Completes the value argument of set""" param = arg_tokens['param'][0] try: settable = self.settables[param] except KeyError: raise CompletionError(param + " is not a settable parameter") # Create a parser with a value field based on this settable settable_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(parents=[Cmd.set_parser_parent]) # Settables with choices list the values of those choices instead of the arg name # in help text and this shows in tab completion hints. Set metavar to avoid this. arg_name = 'value' settable_parser.add_argument( arg_name, metavar=arg_name, help=settable.description, choices=settable.choices, # type: ignore[arg-type] choices_provider=settable.choices_provider, completer=settable.completer, ) completer = argparse_completer.DEFAULT_AP_COMPLETER(settable_parser, self) # Use raw_tokens since quotes have been preserved _, raw_tokens = self.tokens_for_completion(line, begidx, endidx) return completer.complete(text, line, begidx, endidx, raw_tokens[1:]) # When tab completing value, we recreate the set command parser with a value argument specific to # the settable being edited. To make this easier, define a parent parser with all the common elements. set_description = ( "Set a settable parameter or show current settings of parameters\n" "Call without arguments for a list of all settable parameters with their values.\n" "Call with just param to view that parameter's value." ) set_parser_parent = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=set_description, add_help=False) set_parser_parent.add_argument( 'param', nargs=argparse.OPTIONAL, help='parameter to set or view', choices_provider=_get_settable_completion_items, descriptive_header=_settable_completion_table.generate_header(), ) # Create the parser for the set command set_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(parents=[set_parser_parent]) set_parser.add_argument( 'value', nargs=argparse.OPTIONAL, help='new value for settable', completer=complete_set_value, suppress_tab_hint=True ) # Preserve quotes so users can pass in quoted empty strings and flags (e.g. -h) as the value @with_argparser(set_parser, preserve_quotes=True) def do_set(self, args: argparse.Namespace) -> None: """Set a settable parameter or show current settings of parameters""" self.last_result = False if not self.settables: self.pwarning("There are no settable parameters") return if args.param: try: settable = self.settables[args.param] except KeyError: self.perror(f"Parameter '{args.param}' not supported (type 'set' for list of parameters).") return if args.value: # Try to update the settable's value try: orig_value = settable.get_value() new_value = settable.set_value(utils.strip_quotes(args.value)) # noinspection PyBroadException except Exception as ex: self.perror(f"Error setting {args.param}: {ex}") else: self.poutput(f"{args.param} - was: {orig_value!r}\nnow: {new_value!r}") self.last_result = True return # Show one settable to_show = [args.param] else: # Show all settables to_show = list(self.settables.keys()) # Define the table structure name_label = 'Name' max_name_width = max([ansi.style_aware_wcswidth(param) for param in to_show]) max_name_width = max(max_name_width, ansi.style_aware_wcswidth(name_label)) cols: List[Column] = [ Column(name_label, width=max_name_width), Column('Value', width=30), Column('Description', width=60), ] table = SimpleTable(cols, divider_char=self.ruler) self.poutput(table.generate_header()) # Build the table and populate self.last_result self.last_result = {} # Dict[settable_name, settable_value] for param in sorted(to_show, key=self.default_sort_key): settable = self.settables[param] row_data = [param, settable.get_value(), settable.description] self.poutput(table.generate_data_row(row_data)) self.last_result[param] = settable.get_value() shell_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Execute a command as if at the OS prompt") shell_parser.add_argument('command', help='the command to run', completer=shell_cmd_complete) shell_parser.add_argument( 'command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer=path_complete ) # Preserve quotes since we are passing these strings to the shell @with_argparser(shell_parser, preserve_quotes=True) def do_shell(self, args: argparse.Namespace) -> None: """Execute a command as if at the OS prompt""" import signal import subprocess kwargs: Dict[str, Any] = dict() # Set OS-specific parameters if sys.platform.startswith('win'): # Windows returns STATUS_CONTROL_C_EXIT when application stopped by Ctrl-C ctrl_c_ret_code = 0xC000013A else: # On POSIX, Popen() returns -SIGINT when application stopped by Ctrl-C ctrl_c_ret_code = signal.SIGINT.value * -1 # On POSIX with shell=True, Popen() defaults to /bin/sh as the shell. # sh reports an incorrect return code for some applications when Ctrl-C is pressed within that # application (e.g. less). Since sh received the SIGINT, it sets the return code to reflect being # closed by SIGINT even though less did not exit upon a Ctrl-C press. In the same situation, other # shells like bash and zsh report the actual return code of less. Therefore we will try to run the # user's preferred shell which most likely will be something other than sh. This also allows the user # to run builtin commands of their preferred shell. shell = os.environ.get("SHELL") if shell: kwargs['executable'] = shell # Create a list of arguments to shell tokens = [args.command] + args.command_args # Expand ~ where needed utils.expand_user_in_tokens(tokens) expanded_command = ' '.join(tokens) # Prevent KeyboardInterrupts while in the shell process. The shell process will # still receive the SIGINT since it is in the same process group as us. with self.sigint_protection: # For any stream that is a StdSim, we will use a pipe so we can capture its output proc = subprocess.Popen( # type: ignore[call-overload] expanded_command, stdout=subprocess.PIPE if isinstance(self.stdout, utils.StdSim) else self.stdout, # type: ignore[unreachable] stderr=subprocess.PIPE if isinstance(sys.stderr, utils.StdSim) else sys.stderr, # type: ignore[unreachable] shell=True, **kwargs, ) proc_reader = utils.ProcReader(proc, cast(TextIO, self.stdout), sys.stderr) # type: ignore[arg-type] proc_reader.wait() # Save the return code of the application for use in a pyscript self.last_result = proc.returncode # If the process was stopped by Ctrl-C, then inform the caller by raising a KeyboardInterrupt. # This is to support things like stop_on_keyboard_interrupt in run_cmds_plus_hooks(). if proc.returncode == ctrl_c_ret_code: self._raise_keyboard_interrupt() @staticmethod def _reset_py_display() -> None: """ Resets the dynamic objects in the sys module that the py and ipy consoles fight over. When a Python console starts it adopts certain display settings if they've already been set. If an ipy console has previously been run, then py uses its settings and ends up looking like an ipy console in terms of prompt and exception text. This method forces the Python console to create its own display settings since they won't exist. IPython does not have this problem since it always overwrites the display settings when it is run. Therefore this method only needs to be called before creating a Python console. """ # Delete any prompts that have been set attributes = ['ps1', 'ps2', 'ps3'] for cur_attr in attributes: try: del sys.__dict__[cur_attr] except KeyError: pass # Reset functions sys.displayhook = sys.__displayhook__ sys.excepthook = sys.__excepthook__ def _set_up_py_shell_env(self, interp: InteractiveConsole) -> _SavedCmd2Env: """ Set up interactive Python shell environment :return: Class containing saved up cmd2 environment """ cmd2_env = _SavedCmd2Env() # Set up readline for Python shell if rl_type != RlType.NONE: # Save cmd2 history for i in range(1, readline.get_current_history_length() + 1): # noinspection PyArgumentList cmd2_env.history.append(readline.get_history_item(i)) readline.clear_history() # Restore py's history for item in self._py_history: readline.add_history(item) if self._completion_supported(): # Set up tab completion for the Python console # rlcompleter relies on the default settings of the Python readline module if rl_type == RlType.GNU: cmd2_env.readline_settings.basic_quotes = cast( bytes, ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value ) rl_basic_quote_characters.value = orig_rl_basic_quotes if 'gnureadline' in sys.modules: # rlcompleter imports readline by name, so it won't use gnureadline # Force rlcompleter to use gnureadline instead so it has our settings and history if 'readline' in sys.modules: cmd2_env.readline_module = sys.modules['readline'] sys.modules['readline'] = sys.modules['gnureadline'] cmd2_env.readline_settings.delims = readline.get_completer_delims() readline.set_completer_delims(orig_rl_delims) # rlcompleter will not need cmd2's custom display function # This will be restored by cmd2 the next time complete() is called if rl_type == RlType.GNU: readline.set_completion_display_matches_hook(None) elif rl_type == RlType.PYREADLINE: # noinspection PyUnresolvedReferences readline.rl.mode._display_completions = orig_pyreadline_display # Save off the current completer and set a new one in the Python console # Make sure it tab completes from its locals() dictionary cmd2_env.readline_settings.completer = readline.get_completer() interp.runcode("from rlcompleter import Completer") # type: ignore[arg-type] interp.runcode("import readline") # type: ignore[arg-type] interp.runcode("readline.set_completer(Completer(locals()).complete)") # type: ignore[arg-type] # Set up sys module for the Python console self._reset_py_display() cmd2_env.sys_stdout = sys.stdout sys.stdout = self.stdout # type: ignore[assignment] cmd2_env.sys_stdin = sys.stdin sys.stdin = self.stdin # type: ignore[assignment] return cmd2_env def _restore_cmd2_env(self, cmd2_env: _SavedCmd2Env) -> None: """ Restore cmd2 environment after exiting an interactive Python shell :param cmd2_env: the environment settings to restore """ sys.stdout = cmd2_env.sys_stdout # type: ignore[assignment] sys.stdin = cmd2_env.sys_stdin # type: ignore[assignment] # Set up readline for cmd2 if rl_type != RlType.NONE: # Save py's history self._py_history.clear() for i in range(1, readline.get_current_history_length() + 1): # noinspection PyArgumentList self._py_history.append(readline.get_history_item(i)) readline.clear_history() # Restore cmd2's history for item in cmd2_env.history: readline.add_history(item) if self._completion_supported(): # Restore cmd2's tab completion settings readline.set_completer(cmd2_env.readline_settings.completer) readline.set_completer_delims(cmd2_env.readline_settings.delims) if rl_type == RlType.GNU: rl_basic_quote_characters.value = cmd2_env.readline_settings.basic_quotes if 'gnureadline' in sys.modules: # Restore what the readline module pointed to if cmd2_env.readline_module is None: del sys.modules['readline'] else: sys.modules['readline'] = cmd2_env.readline_module def _run_python(self, *, pyscript: Optional[str] = None) -> Optional[bool]: """ Called by do_py() and do_run_pyscript(). If pyscript is None, then this function runs an interactive Python shell. Otherwise, it runs the pyscript file. :param pyscript: optional path to a pyscript file to run. This is intended only to be used by do_run_pyscript() after it sets up sys.argv for the script. (Defaults to None) :return: True if running of commands should stop """ self.last_result = False def py_quit() -> None: """Function callable from the interactive Python console to exit that environment""" raise EmbeddedConsoleExit from .py_bridge import ( PyBridge, ) py_bridge = PyBridge(self) saved_sys_path = None if self.in_pyscript(): self.perror("Recursively entering interactive Python shells is not allowed") return None try: self._in_py = True py_code_to_run = '' # Make a copy of self.py_locals for the locals dictionary in the Python environment we are creating. # This is to prevent pyscripts from editing it. (e.g. locals().clear()). It also ensures a pyscript's # environment won't be filled with data from a previously run pyscript. Only make a shallow copy since # it's OK for py_locals to contain objects which are editable in a pyscript. local_vars = self.py_locals.copy() local_vars[self.py_bridge_name] = py_bridge local_vars['quit'] = py_quit local_vars['exit'] = py_quit if self.self_in_py: local_vars['self'] = self # Handle case where we were called by do_run_pyscript() if pyscript is not None: # Read the script file expanded_filename = os.path.expanduser(pyscript) try: with open(expanded_filename) as f: py_code_to_run = f.read() except OSError as ex: self.perror(f"Error reading script file '{expanded_filename}': {ex}") return None local_vars['__name__'] = '__main__' local_vars['__file__'] = expanded_filename # Place the script's directory at sys.path[0] just as Python does when executing a script saved_sys_path = list(sys.path) sys.path.insert(0, os.path.dirname(os.path.abspath(expanded_filename))) else: # This is the default name chosen by InteractiveConsole when no locals are passed in local_vars['__name__'] = '__console__' # Create the Python interpreter self.last_result = True interp = InteractiveConsole(locals=local_vars) # Check if we are running Python code if py_code_to_run: # noinspection PyBroadException try: interp.runcode(py_code_to_run) # type: ignore[arg-type] except BaseException: # We don't care about any exception that happened in the Python code pass # Otherwise we will open an interactive Python shell else: cprt = 'Type "help", "copyright", "credits" or "license" for more information.' instructions = ( 'Use `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()` to exit.\n' f'Run CLI commands with: {self.py_bridge_name}("command ...")' ) banner = f"Python {sys.version} on {sys.platform}\n{cprt}\n\n{instructions}\n" saved_cmd2_env = None # noinspection PyBroadException try: # Get sigint protection while we set up the Python shell environment with self.sigint_protection: saved_cmd2_env = self._set_up_py_shell_env(interp) # Since quit() or exit() raise an EmbeddedConsoleExit, interact() exits before printing # the exitmsg. Therefore we will not provide it one and print it manually later. interp.interact(banner=banner, exitmsg='') except BaseException: # We don't care about any exception that happened in the interactive console pass finally: # Get sigint protection while we restore cmd2 environment settings with self.sigint_protection: if saved_cmd2_env is not None: self._restore_cmd2_env(saved_cmd2_env) self.poutput("Now exiting Python shell...") finally: with self.sigint_protection: if saved_sys_path is not None: sys.path = saved_sys_path self._in_py = False return py_bridge.stop py_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Run an interactive Python shell") @with_argparser(py_parser) def do_py(self, _: argparse.Namespace) -> Optional[bool]: """ Run an interactive Python shell :return: True if running of commands should stop """ # self.last_resort will be set by _run_python() return self._run_python() run_pyscript_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Run a Python script file inside the console") run_pyscript_parser.add_argument('script_path', help='path to the script file', completer=path_complete) run_pyscript_parser.add_argument( 'script_arguments', nargs=argparse.REMAINDER, help='arguments to pass to script', completer=path_complete ) @with_argparser(run_pyscript_parser) def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]: """ Run a Python script file inside the console :return: True if running of commands should stop """ self.last_result = False # Expand ~ before placing this path in sys.argv just as a shell would args.script_path = os.path.expanduser(args.script_path) # Add some protection against accidentally running a non-Python file. The happens when users # mix up run_script and run_pyscript. if not args.script_path.endswith('.py'): self.pwarning(f"'{args.script_path}' does not have a .py extension") selection = self.select('Yes No', 'Continue to try to run it as a Python script? ') if selection != 'Yes': return None # Save current command line arguments orig_args = sys.argv try: # Overwrite sys.argv to allow the script to take command line arguments sys.argv = [args.script_path] + args.script_arguments # self.last_resort will be set by _run_python() py_return = self._run_python(pyscript=args.script_path) finally: # Restore command line arguments to original state sys.argv = orig_args return py_return ipython_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Run an interactive IPython shell") # noinspection PyPackageRequirements @with_argparser(ipython_parser) def do_ipy(self, _: argparse.Namespace) -> Optional[bool]: # pragma: no cover """ Enter an interactive IPython shell :return: True if running of commands should stop """ self.last_result = False # Detect whether IPython is installed try: import traitlets.config.loader as TraitletsLoader # type: ignore[import] from IPython import ( # type: ignore[import] start_ipython, ) from IPython.terminal.interactiveshell import ( # type: ignore[import] TerminalInteractiveShell, ) from IPython.terminal.ipapp import ( # type: ignore[import] TerminalIPythonApp, ) except ImportError: self.perror("IPython package is not installed") return None from .py_bridge import ( PyBridge, ) if self.in_pyscript(): self.perror("Recursively entering interactive Python shells is not allowed") return None self.last_result = True try: self._in_py = True py_bridge = PyBridge(self) # Make a copy of self.py_locals for the locals dictionary in the IPython environment we are creating. # This is to prevent ipy from editing it. (e.g. locals().clear()). Only make a shallow copy since # it's OK for py_locals to contain objects which are editable in ipy. local_vars = self.py_locals.copy() local_vars[self.py_bridge_name] = py_bridge if self.self_in_py: local_vars['self'] = self # Configure IPython config = TraitletsLoader.Config() config.InteractiveShell.banner2 = ( 'Entering an IPython shell. Type exit, quit, or Ctrl-D to exit.\n' f'Run CLI commands with: {self.py_bridge_name}("command ...")\n' ) # Start IPython start_ipython(config=config, argv=[], user_ns=local_vars) self.poutput("Now exiting IPython shell...") # The IPython application is a singleton and won't be recreated next time # this function runs. That's a problem since the contents of local_vars # may need to be changed. Therefore we must destroy all instances of the # relevant classes. TerminalIPythonApp.clear_instance() TerminalInteractiveShell.clear_instance() return py_bridge.stop finally: self._in_py = False history_description = "View, run, edit, save, or clear previously entered commands" history_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=history_description) history_action_group = history_parser.add_mutually_exclusive_group() history_action_group.add_argument('-r', '--run', action='store_true', help='run selected history items') history_action_group.add_argument('-e', '--edit', action='store_true', help='edit and then run selected history items') history_action_group.add_argument( '-o', '--output_file', metavar='FILE', help='output commands to a script file, implies -s', completer=path_complete ) history_action_group.add_argument( '-t', '--transcript', metavar='TRANSCRIPT_FILE', help='output commands and results to a transcript file,\nimplies -s', completer=path_complete, ) history_action_group.add_argument('-c', '--clear', action='store_true', help='clear all history') history_format_group = history_parser.add_argument_group(title='formatting') history_format_group.add_argument( '-s', '--script', action='store_true', help='output commands in script format, i.e. without command\n' 'numbers' ) history_format_group.add_argument( '-x', '--expanded', action='store_true', help='output fully parsed commands with any aliases and\n' 'macros expanded, instead of typed commands', ) history_format_group.add_argument( '-v', '--verbose', action='store_true', help='display history and include expanded commands if they\n' 'differ from the typed command', ) history_format_group.add_argument( '-a', '--all', action='store_true', help='display all commands, including ones persisted from\n' 'previous sessions' ) history_arg_help = ( "empty all history items\n" "a one history item by number\n" "a..b, a:b, a:, ..b items by indices (inclusive)\n" "string items containing string\n" "/regex/ items matching regular expression" ) history_parser.add_argument('arg', nargs=argparse.OPTIONAL, help=history_arg_help) @with_argparser(history_parser) def do_history(self, args: argparse.Namespace) -> Optional[bool]: """ View, run, edit, save, or clear previously entered commands :return: True if running of commands should stop """ self.last_result = False # -v must be used alone with no other options if args.verbose: if args.clear or args.edit or args.output_file or args.run or args.transcript or args.expanded or args.script: self.poutput("-v cannot be used with any other options") self.poutput(self.history_parser.format_usage()) return None # -s and -x can only be used if none of these options are present: [-c -r -e -o -t] if (args.script or args.expanded) and (args.clear or args.edit or args.output_file or args.run or args.transcript): self.poutput("-s and -x cannot be used with -c, -r, -e, -o, or -t") self.poutput(self.history_parser.format_usage()) return None if args.clear: self.last_result = True # Clear command and readline history self.history.clear() if self.persistent_history_file: try: os.remove(self.persistent_history_file) except FileNotFoundError: pass except OSError as ex: self.perror(f"Error removing history file '{self.persistent_history_file}': {ex}") self.last_result = False return None if rl_type != RlType.NONE: readline.clear_history() return None # If an argument was supplied, then retrieve partial contents of the history, otherwise retrieve it all history = self._get_history(args) if args.run: if not args.arg: self.perror("Cowardly refusing to run all previously entered commands.") self.perror("If this is what you want to do, specify '1:' as the range of history.") else: stop = self.runcmds_plus_hooks(list(history.values())) self.last_result = True return stop elif args.edit: import tempfile fd, fname = tempfile.mkstemp(suffix='.txt', text=True) fobj: TextIO with os.fdopen(fd, 'w') as fobj: for command in history.values(): if command.statement.multiline_command: fobj.write(f'{command.expanded}\n') else: fobj.write(f'{command.raw}\n') try: self.run_editor(fname) # self.last_resort will be set by do_run_script() # noinspection PyTypeChecker return self.do_run_script(utils.quote_string(fname)) finally: os.remove(fname) elif args.output_file: full_path = os.path.abspath(os.path.expanduser(args.output_file)) try: with open(full_path, 'w') as fobj: for item in history.values(): if item.statement.multiline_command: fobj.write(f"{item.expanded}\n") else: fobj.write(f"{item.raw}\n") plural = '' if len(history) == 1 else 's' except OSError as ex: self.perror(f"Error saving history file '{full_path}': {ex}") else: self.pfeedback(f"{len(history)} command{plural} saved to {full_path}") self.last_result = True elif args.transcript: # self.last_resort will be set by _generate_transcript() self._generate_transcript(list(history.values()), args.transcript) else: # Display the history items retrieved for idx, hi in history.items(): self.poutput(hi.pr(idx, script=args.script, expanded=args.expanded, verbose=args.verbose)) self.last_result = history return None def _get_history(self, args: argparse.Namespace) -> 'OrderedDict[int, HistoryItem]': """If an argument was supplied, then retrieve partial contents of the history; otherwise retrieve entire history. This function returns a dictionary with history items keyed by their 1-based index in ascending order. """ if args.arg: try: int_arg = int(args.arg) return OrderedDict({int_arg: self.history.get(int_arg)}) except ValueError: pass if '..' in args.arg or ':' in args.arg: # Get a slice of history history = self.history.span(args.arg, args.all) elif args.arg.startswith(r'/') and args.arg.endswith(r'/'): history = self.history.regex_search(args.arg, args.all) else: history = self.history.str_search(args.arg, args.all) else: # Get a copy of the history so it doesn't get mutated while we are using it history = self.history.span(':', args.all) return history def _initialize_history(self, hist_file: str) -> None: """Initialize history using history related attributes :param hist_file: optional path to persistent history file. If specified, then history from previous sessions will be included. Additionally, all history will be written to this file when the application exits. """ import json import lzma self.history = History() # with no persistent history, nothing else in this method is relevant if not hist_file: self.persistent_history_file = hist_file return hist_file = os.path.abspath(os.path.expanduser(hist_file)) # On Windows, trying to open a directory throws a permission # error, not a `IsADirectoryError`. So we'll check it ourselves. if os.path.isdir(hist_file): self.perror(f"Persistent history file '{hist_file}' is a directory") return # Create the directory for the history file if it doesn't already exist hist_file_dir = os.path.dirname(hist_file) try: os.makedirs(hist_file_dir, exist_ok=True) except OSError as ex: self.perror(f"Error creating persistent history file directory '{hist_file_dir}': {ex}") return # Read and process history file try: with open(hist_file, 'rb') as fobj: compressed_bytes = fobj.read() history_json = lzma.decompress(compressed_bytes).decode(encoding='utf-8') self.history = History.from_json(history_json) except FileNotFoundError: # Just use an empty history pass except OSError as ex: self.perror(f"Cannot read persistent history file '{hist_file}': {ex}") return except (json.JSONDecodeError, lzma.LZMAError, KeyError, UnicodeDecodeError, ValueError) as ex: self.perror( f"Error processing persistent history file '{hist_file}': {ex}\n" f"The history file will be recreated when this application exits." ) self.history.start_session() self.persistent_history_file = hist_file # populate readline history if rl_type != RlType.NONE: last = None for item in self.history: # Break the command into its individual lines for line in item.raw.splitlines(): # readline only adds a single entry for multiple sequential identical lines # so we emulate that behavior here if line != last: readline.add_history(line) last = line # register a function to write history at save # if the history file is in plain text format from 0.9.12 or lower # this will fail, and the history in the plain text file will be lost import atexit atexit.register(self._persist_history) def _persist_history(self) -> None: """Write history out to the persistent history file as compressed JSON""" import lzma if not self.persistent_history_file: return self.history.truncate(self._persistent_history_length) try: history_json = self.history.to_json() compressed_bytes = lzma.compress(history_json.encode(encoding='utf-8')) with open(self.persistent_history_file, 'wb') as fobj: fobj.write(compressed_bytes) except OSError as ex: self.perror(f"Cannot write persistent history file '{self.persistent_history_file}': {ex}") def _generate_transcript(self, history: Union[List[HistoryItem], List[str]], transcript_file: str) -> None: """Generate a transcript file from a given history of commands""" self.last_result = False # Validate the transcript file path to make sure directory exists and write access is available transcript_path = os.path.abspath(os.path.expanduser(transcript_file)) transcript_dir = os.path.dirname(transcript_path) if not os.path.isdir(transcript_dir) or not os.access(transcript_dir, os.W_OK): self.perror(f"'{transcript_dir}' is not a directory or you don't have write access") return commands_run = 0 try: with self.sigint_protection: # Disable echo while we manually redirect stdout to a StringIO buffer saved_echo = self.echo saved_stdout = self.stdout self.echo = False # The problem with supporting regular expressions in transcripts # is that they shouldn't be processed in the command, just the output. # In addition, when we generate a transcript, any slashes in the output # are not really intended to indicate regular expressions, so they should # be escaped. # # We have to jump through some hoops here in order to catch the commands # separately from the output and escape the slashes in the output. transcript = '' for history_item in history: # build the command, complete with prompts. When we replay # the transcript, we look for the prompts to separate # the command from the output first = True command = '' if isinstance(history_item, HistoryItem): history_item = history_item.raw for line in history_item.splitlines(): if first: command += f"{self.prompt}{line}\n" first = False else: command += f"{self.continuation_prompt}{line}\n" transcript += command # Use a StdSim object to capture output stdsim = utils.StdSim(cast(TextIO, self.stdout)) self.stdout = cast(TextIO, stdsim) # then run the command and let the output go into our buffer try: stop = self.onecmd_plus_hooks(history_item, raise_keyboard_interrupt=True) except KeyboardInterrupt as ex: self.perror(ex) stop = True commands_run += 1 # add the regex-escaped output to the transcript transcript += stdsim.getvalue().replace('/', r'\/') # check if we are supposed to stop if stop: break finally: with self.sigint_protection: # Restore altered attributes to their original state self.echo = saved_echo self.stdout = cast(TextIO, saved_stdout) # Check if all commands ran if commands_run < len(history): self.pwarning(f"Command {commands_run} triggered a stop and ended transcript generation early") # finally, we can write the transcript out to the file try: with open(transcript_path, 'w') as fout: fout.write(transcript) except OSError as ex: self.perror(f"Error saving transcript file '{transcript_path}': {ex}") else: # and let the user know what we did if commands_run == 1: plural = 'command and its output' else: plural = 'commands and their outputs' self.pfeedback(f"{commands_run} {plural} saved to transcript file '{transcript_path}'") self.last_result = True edit_description = ( "Run a text editor and optionally open a file with it\n" "\n" "The editor used is determined by a settable parameter. To set it:\n" "\n" " set editor (program-name)" ) edit_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=edit_description) edit_parser.add_argument( 'file_path', nargs=argparse.OPTIONAL, help="optional path to a file to open in editor", completer=path_complete ) @with_argparser(edit_parser) def do_edit(self, args: argparse.Namespace) -> None: """Run a text editor and optionally open a file with it""" # self.last_result will be set by do_shell() which is called by run_editor() self.run_editor(args.file_path) def run_editor(self, file_path: Optional[str] = None) -> None: """ Run a text editor and optionally open a file with it :param file_path: optional path of the file to edit. Defaults to None. :raises: EnvironmentError if self.editor is not set """ if not self.editor: raise EnvironmentError("Please use 'set editor' to specify your text editing program of choice.") command = utils.quote_string(os.path.expanduser(self.editor)) if file_path: command += " " + utils.quote_string(os.path.expanduser(file_path)) # noinspection PyTypeChecker self.do_shell(command) @property def _current_script_dir(self) -> Optional[str]: """Accessor to get the current script directory from the _script_dir LIFO queue.""" if self._script_dir: return self._script_dir[-1] else: return None run_script_description = ( "Run commands in script file that is encoded as either ASCII or UTF-8 text\n" "\n" "Script should contain one command per line, just like the command would be\n" "typed in the console.\n" "\n" "If the -t/--transcript flag is used, this command instead records\n" "the output of the script commands to a transcript for testing purposes.\n" ) run_script_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=run_script_description) run_script_parser.add_argument( '-t', '--transcript', metavar='TRANSCRIPT_FILE', help='record the output of the script as a transcript file', completer=path_complete, ) run_script_parser.add_argument('script_path', help="path to the script file", completer=path_complete) @with_argparser(run_script_parser) def do_run_script(self, args: argparse.Namespace) -> Optional[bool]: """Run commands in script file that is encoded as either ASCII or UTF-8 text. :return: True if running of commands should stop """ self.last_result = False expanded_path = os.path.abspath(os.path.expanduser(args.script_path)) # Add some protection against accidentally running a Python file. The happens when users # mix up run_script and run_pyscript. if expanded_path.endswith('.py'): self.pwarning(f"'{expanded_path}' appears to be a Python file") selection = self.select('Yes No', 'Continue to try to run it as a text script? ') if selection != 'Yes': return None try: # An empty file is not an error, so just return if os.path.getsize(expanded_path) == 0: self.last_result = True return None # Make sure the file is ASCII or UTF-8 encoded text if not utils.is_text_file(expanded_path): self.perror(f"'{expanded_path}' is not an ASCII or UTF-8 encoded text file") return None # Read all lines of the script with open(expanded_path, encoding='utf-8') as target: script_commands = target.read().splitlines() except OSError as ex: self.perror(f"Problem accessing script from '{expanded_path}': {ex}") return None orig_script_dir_count = len(self._script_dir) try: self._script_dir.append(os.path.dirname(expanded_path)) if args.transcript: # self.last_resort will be set by _generate_transcript() self._generate_transcript(script_commands, os.path.expanduser(args.transcript)) else: stop = self.runcmds_plus_hooks(script_commands, stop_on_keyboard_interrupt=True) self.last_result = True return stop finally: with self.sigint_protection: # Check if a script dir was added before an exception occurred if orig_script_dir_count != len(self._script_dir): self._script_dir.pop() return None relative_run_script_description = run_script_description relative_run_script_description += ( "\n\n" "If this is called from within an already-running script, the filename will be\n" "interpreted relative to the already-running script's directory." ) relative_run_script_epilog = "Notes:\n" " This command is intended to only be used within text file scripts." relative_run_script_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description=relative_run_script_description, epilog=relative_run_script_epilog ) relative_run_script_parser.add_argument('file_path', help='a file path pointing to a script') @with_argparser(relative_run_script_parser) def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]: """ Run commands in script file that is encoded as either ASCII or UTF-8 text :return: True if running of commands should stop """ file_path = args.file_path # NOTE: Relative path is an absolute path, it is just relative to the current script directory relative_path = os.path.join(self._current_script_dir or '', file_path) # self.last_result will be set by do_run_script() # noinspection PyTypeChecker return self.do_run_script(utils.quote_string(relative_path)) def _run_transcript_tests(self, transcript_paths: List[str]) -> None: """Runs transcript tests for provided file(s). This is called when either -t is provided on the command line or the transcript_files argument is provided during construction of the cmd2.Cmd instance. :param transcript_paths: list of transcript test file paths """ import time import unittest import cmd2 from .transcript import ( Cmd2TestCase, ) class TestMyAppCase(Cmd2TestCase): cmdapp = self # Validate that there is at least one transcript file transcripts_expanded = utils.files_from_glob_patterns(transcript_paths, access=os.R_OK) if not transcripts_expanded: self.perror('No test files found - nothing to test') self.exit_code = 1 return verinfo = ".".join(map(str, sys.version_info[:3])) num_transcripts = len(transcripts_expanded) plural = '' if len(transcripts_expanded) == 1 else 's' self.poutput(ansi.style(utils.align_center(' cmd2 transcript test ', fill_char='='), bold=True)) self.poutput(f'platform {sys.platform} -- Python {verinfo}, cmd2-{cmd2.__version__}, readline-{rl_type}') self.poutput(f'cwd: {os.getcwd()}') self.poutput(f'cmd2 app: {sys.argv[0]}') self.poutput(ansi.style(f'collected {num_transcripts} transcript{plural}', bold=True)) setattr(self.__class__, 'testfiles', transcripts_expanded) sys.argv = [sys.argv[0]] # the --test argument upsets unittest.main() testcase = TestMyAppCase() stream = cast(TextIO, utils.StdSim(sys.stderr)) # noinspection PyTypeChecker runner = unittest.TextTestRunner(stream=stream) start_time = time.time() test_results = runner.run(testcase) execution_time = time.time() - start_time if test_results.wasSuccessful(): ansi.style_aware_write(sys.stderr, stream.read()) finish_msg = f' {num_transcripts} transcript{plural} passed in {execution_time:.3f} seconds ' finish_msg = ansi.style_success(utils.align_center(finish_msg, fill_char='=')) self.poutput(finish_msg) else: # Strip off the initial traceback which isn't particularly useful for end users error_str = stream.read() end_of_trace = error_str.find('AssertionError:') file_offset = error_str[end_of_trace:].find('File ') start = end_of_trace + file_offset # But print the transcript file name and line number followed by what was expected and what was observed self.perror(error_str[start:]) # Return a failure error code to support automated transcript-based testing self.exit_code = 1 def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None: # pragma: no cover """ Display an important message to the user while they are at a command line prompt. To the user it appears as if an alert message is printed above the prompt and their current input text and cursor location is left alone. IMPORTANT: This function will not print an alert unless it can acquire self.terminal_lock to ensure a prompt is onscreen. Therefore it is best to acquire the lock before calling this function to guarantee the alert prints and to avoid raising a RuntimeError. :param alert_msg: the message to display to the user :param new_prompt: If you also want to change the prompt that is displayed, then include it here. See async_update_prompt() docstring for guidance on updating a prompt. :raises RuntimeError: if called while another thread holds `terminal_lock` """ if not (vt100_support and self.use_rawinput): return # Sanity check that can't fail if self.terminal_lock was acquired before calling this function if self.terminal_lock.acquire(blocking=False): # Windows terminals tend to flicker when we redraw the prompt and input lines. # To reduce how often this occurs, only update terminal if there are changes. update_terminal = False if alert_msg: alert_msg += '\n' update_terminal = True if new_prompt is not None: self.prompt = new_prompt # Check if the prompt to display has changed from what's currently displayed cur_onscreen_prompt = rl_get_prompt() new_onscreen_prompt = self.continuation_prompt if self._at_continuation_prompt else self.prompt if new_onscreen_prompt != cur_onscreen_prompt: update_terminal = True if update_terminal: import shutil # Generate the string which will replace the current prompt and input lines with the alert terminal_str = ansi.async_alert_str( terminal_columns=shutil.get_terminal_size().columns, prompt=cur_onscreen_prompt, line=readline.get_line_buffer(), cursor_offset=rl_get_point(), alert_msg=alert_msg, ) if rl_type == RlType.GNU: sys.stderr.write(terminal_str) sys.stderr.flush() elif rl_type == RlType.PYREADLINE: # noinspection PyUnresolvedReferences readline.rl.mode.console.write(terminal_str) # Update Readline's prompt before we redraw it rl_set_prompt(new_onscreen_prompt) # Redraw the prompt and input lines below the alert rl_force_redisplay() self.terminal_lock.release() else: raise RuntimeError("another thread holds terminal_lock") def async_update_prompt(self, new_prompt: str) -> None: # pragma: no cover """ Update the command line prompt while the user is still typing at it. This is good for alerting the user to system changes dynamically in between commands. For instance you could alter the color of the prompt to indicate a system status or increase a counter to report an event. If you do alter the actual text of the prompt, it is best to keep the prompt the same width as what's on screen. Otherwise the user's input text will be shifted and the update will not be seamless. IMPORTANT: This function will not update the prompt unless it can acquire self.terminal_lock to ensure a prompt is onscreen. Therefore it is best to acquire the lock before calling this function to guarantee the prompt changes and to avoid raising a RuntimeError. If user is at a continuation prompt while entering a multiline command, the onscreen prompt will not change. However self.prompt will still be updated and display immediately after the multiline line command completes. :param new_prompt: what to change the prompt to :raises RuntimeError: if called while another thread holds `terminal_lock` """ self.async_alert('', new_prompt) @staticmethod def set_window_title(title: str) -> None: # pragma: no cover """ Set the terminal window title. NOTE: This function writes to stderr. Therefore if you call this during a command run by a pyscript, the string which updates the title will appear in that command's CommandResult.stderr data. :param title: the new window title """ if not vt100_support: return try: sys.stderr.write(ansi.set_title(title)) sys.stderr.flush() except AttributeError: # Debugging in Pycharm has issues with setting terminal title pass def enable_command(self, command: str) -> None: """ Enable a command by restoring its functions :param command: the command being enabled """ # If the commands is already enabled, then return if command not in self.disabled_commands: return help_func_name = constants.HELP_FUNC_PREFIX + command completer_func_name = constants.COMPLETER_FUNC_PREFIX + command # Restore the command function to its original value dc = self.disabled_commands[command] setattr(self, self._cmd_func_name(command), dc.command_function) # Restore the help function to its original value if dc.help_function is None: delattr(self, help_func_name) else: setattr(self, help_func_name, dc.help_function) # Restore the completer function to its original value if dc.completer_function is None: delattr(self, completer_func_name) else: setattr(self, completer_func_name, dc.completer_function) # Remove the disabled command entry del self.disabled_commands[command] def enable_category(self, category: str) -> None: """ Enable an entire category of commands :param category: the category to enable """ for cmd_name in list(self.disabled_commands): func = self.disabled_commands[cmd_name].command_function if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category: self.enable_command(cmd_name) def disable_command(self, command: str, message_to_print: str) -> None: """ Disable a command and overwrite its functions :param command: the command being disabled :param message_to_print: what to print when this command is run or help is called on it while disabled The variable cmd2.COMMAND_NAME can be used as a placeholder for the name of the command being disabled. ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled" """ # If the commands is already disabled, then return if command in self.disabled_commands: return # Make sure this is an actual command command_function = self.cmd_func(command) if command_function is None: raise AttributeError(f"'{command}' does not refer to a command") help_func_name = constants.HELP_FUNC_PREFIX + command completer_func_name = constants.COMPLETER_FUNC_PREFIX + command # Add the disabled command record self.disabled_commands[command] = DisabledCommand( command_function=command_function, help_function=getattr(self, help_func_name, None), completer_function=getattr(self, completer_func_name, None), ) # Overwrite the command and help functions to print the message new_func = functools.partial( self._report_disabled_command_usage, message_to_print=message_to_print.replace(constants.COMMAND_NAME, command) ) setattr(self, self._cmd_func_name(command), new_func) setattr(self, help_func_name, new_func) # Set the completer to a function that returns a blank list setattr(self, completer_func_name, lambda *args, **kwargs: []) def disable_category(self, category: str, message_to_print: str) -> None: """Disable an entire category of commands. :param category: the category to disable :param message_to_print: what to print when anything in this category is run or help is called on it while disabled. The variable cmd2.COMMAND_NAME can be used as a placeholder for the name of the command being disabled. ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled" """ all_commands = self.get_all_commands() for cmd_name in all_commands: func = self.cmd_func(cmd_name) if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category: self.disable_command(cmd_name, message_to_print) def _report_disabled_command_usage(self, *_args: Any, message_to_print: str, **_kwargs: Any) -> None: """ Report when a disabled command has been run or had help called on it :param args: not used :param message_to_print: the message reporting that the command is disabled :param kwargs: not used """ # Set apply_style to False so message_to_print's style is not overridden self.perror(message_to_print, apply_style=False) def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override] """This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2. _cmdloop() provides the main loop equivalent to cmd.cmdloop(). This is a wrapper around that which deals with the following extra features provided by cmd2: - transcript testing - intro banner - exit code :param intro: if provided this overrides self.intro and serves as the intro banner printed once at start """ # cmdloop() expects to be run in the main thread to support extensive use of KeyboardInterrupts throughout the # other built-in functions. You are free to override cmdloop, but much of cmd2's features will be limited. if not threading.current_thread() is threading.main_thread(): raise RuntimeError("cmdloop must be run in the main thread") # Register a SIGINT signal handler for Ctrl+C import signal original_sigint_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, self.sigint_handler) # Grab terminal lock before the command line prompt has been drawn by readline self.terminal_lock.acquire() # Always run the preloop first for func in self._preloop_hooks: func() self.preloop() # If transcript-based regression testing was requested, then do that instead of the main loop if self._transcript_files is not None: self._run_transcript_tests([os.path.expanduser(tf) for tf in self._transcript_files]) else: # If an intro was supplied in the method call, allow it to override the default if intro is not None: self.intro = intro # Print the intro, if there is one, right after the preloop if self.intro is not None: self.poutput(self.intro) # And then call _cmdloop() to enter the main loop self._cmdloop() # Run the postloop() no matter what for func in self._postloop_hooks: func() self.postloop() # Release terminal lock now that postloop code should have stopped any terminal updater threads # This will also zero the lock count in case cmdloop() is called again self.terminal_lock.release() # Restore the original signal handler signal.signal(signal.SIGINT, original_sigint_handler) return self.exit_code ### # # plugin related functions # ### def _initialize_plugin_system(self) -> None: """Initialize the plugin system""" self._preloop_hooks: List[Callable[[], None]] = [] self._postloop_hooks: List[Callable[[], None]] = [] self._postparsing_hooks: List[Callable[[plugin.PostparsingData], plugin.PostparsingData]] = [] self._precmd_hooks: List[Callable[[plugin.PrecommandData], plugin.PrecommandData]] = [] self._postcmd_hooks: List[Callable[[plugin.PostcommandData], plugin.PostcommandData]] = [] self._cmdfinalization_hooks: List[Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData]] = [] @classmethod def _validate_callable_param_count(cls, func: Callable[..., Any], count: int) -> None: """Ensure a function has the given number of parameters.""" signature = inspect.signature(func) # validate that the callable has the right number of parameters nparam = len(signature.parameters) if nparam != count: plural = '' if nparam == 1 else 's' raise TypeError(f'{func.__name__} has {nparam} positional argument{plural}, expected {count}') @classmethod def _validate_prepostloop_callable(cls, func: Callable[[], None]) -> None: """Check parameter and return types for preloop and postloop hooks.""" cls._validate_callable_param_count(func, 0) # make sure there is no return notation signature = inspect.signature(func) if signature.return_annotation is not None: raise TypeError(f"{func.__name__} must declare return a return type of 'None'") def register_preloop_hook(self, func: Callable[[], None]) -> None: """Register a function to be called at the beginning of the command loop.""" self._validate_prepostloop_callable(func) self._preloop_hooks.append(func) def register_postloop_hook(self, func: Callable[[], None]) -> None: """Register a function to be called at the end of the command loop.""" self._validate_prepostloop_callable(func) self._postloop_hooks.append(func) @classmethod def _validate_postparsing_callable(cls, func: Callable[[plugin.PostparsingData], plugin.PostparsingData]) -> None: """Check parameter and return types for postparsing hooks""" cls._validate_callable_param_count(cast(Callable[..., Any], func), 1) signature = inspect.signature(func) _, param = list(signature.parameters.items())[0] if param.annotation != plugin.PostparsingData: raise TypeError(f"{func.__name__} must have one parameter declared with type 'cmd2.plugin.PostparsingData'") if signature.return_annotation != plugin.PostparsingData: raise TypeError(f"{func.__name__} must declare return a return type of 'cmd2.plugin.PostparsingData'") def register_postparsing_hook(self, func: Callable[[plugin.PostparsingData], plugin.PostparsingData]) -> None: """Register a function to be called after parsing user input but before running the command""" self._validate_postparsing_callable(func) self._postparsing_hooks.append(func) CommandDataType = TypeVar('CommandDataType') @classmethod def _validate_prepostcmd_hook( cls, func: Callable[[CommandDataType], CommandDataType], data_type: Type[CommandDataType] ) -> None: """Check parameter and return types for pre and post command hooks.""" signature = inspect.signature(func) # validate that the callable has the right number of parameters cls._validate_callable_param_count(cast(Callable[..., Any], func), 1) # validate the parameter has the right annotation paramname = list(signature.parameters.keys())[0] param = signature.parameters[paramname] if param.annotation != data_type: raise TypeError(f'argument 1 of {func.__name__} has incompatible type {param.annotation}, expected {data_type}') # validate the return value has the right annotation if signature.return_annotation == signature.empty: raise TypeError(f'{func.__name__} does not have a declared return type, expected {data_type}') if signature.return_annotation != data_type: raise TypeError( f'{func.__name__} has incompatible return type {signature.return_annotation}, expected ' f'{data_type}' ) def register_precmd_hook(self, func: Callable[[plugin.PrecommandData], plugin.PrecommandData]) -> None: """Register a hook to be called before the command function.""" self._validate_prepostcmd_hook(func, plugin.PrecommandData) self._precmd_hooks.append(func) def register_postcmd_hook(self, func: Callable[[plugin.PostcommandData], plugin.PostcommandData]) -> None: """Register a hook to be called after the command function.""" self._validate_prepostcmd_hook(func, plugin.PostcommandData) self._postcmd_hooks.append(func) @classmethod def _validate_cmdfinalization_callable( cls, func: Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData] ) -> None: """Check parameter and return types for command finalization hooks.""" cls._validate_callable_param_count(func, 1) signature = inspect.signature(func) _, param = list(signature.parameters.items())[0] if param.annotation != plugin.CommandFinalizationData: raise TypeError(f"{func.__name__} must have one parameter declared with type {plugin.CommandFinalizationData}") if signature.return_annotation != plugin.CommandFinalizationData: raise TypeError("{func.__name__} must declare return a return type of {plugin.CommandFinalizationData}") def register_cmdfinalization_hook( self, func: Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData] ) -> None: """Register a hook to be called after a command is completed, whether it completes successfully or not.""" self._validate_cmdfinalization_callable(func) self._cmdfinalization_hooks.append(func) def _resolve_func_self( self, cmd_support_func: Callable[..., Any], cmd_self: Union[CommandSet, 'Cmd', None], ) -> Optional[object]: """ Attempt to resolve a candidate instance to pass as 'self' for an unbound class method that was used when defining command's argparse object. Since we restrict registration to only a single CommandSet instance of each type, using type is a reasonably safe way to resolve the correct object instance :param cmd_support_func: command support function. This could be a completer or namespace provider :param cmd_self: The `self` associated with the command or subcommand """ # figure out what class the command support function was defined in func_class: Optional[Type[Any]] = get_defining_class(cmd_support_func) # Was there a defining class identified? If so, is it a sub-class of CommandSet? if func_class is not None and issubclass(func_class, CommandSet): # Since the support function is provided as an unbound function, we need to locate the instance # of the CommandSet to pass in as `self` to emulate a bound method call. # We're searching for candidates that match the support function's defining class type in this order: # 1. Is the command's CommandSet a sub-class of the support function's class? # 2. Do any of the registered CommandSets in the Cmd2 application exactly match the type? # 3. Is there a registered CommandSet that is is the only matching subclass? func_self: Optional[Union[CommandSet, 'Cmd']] # check if the command's CommandSet is a sub-class of the support function's defining class if isinstance(cmd_self, func_class): # Case 1: Command's CommandSet is a sub-class of the support function's CommandSet func_self = cmd_self else: # Search all registered CommandSets func_self = None candidate_sets: List[CommandSet] = [] for installed_cmd_set in self._installed_command_sets: if type(installed_cmd_set) == func_class: # Case 2: CommandSet is an exact type match for the function's CommandSet func_self = installed_cmd_set break # Add candidate for Case 3: if isinstance(installed_cmd_set, func_class): candidate_sets.append(installed_cmd_set) if func_self is None and len(candidate_sets) == 1: # Case 3: There exists exactly 1 CommandSet that is a sub-class match of the function's CommandSet func_self = candidate_sets[0] return func_self else: return self cmd2-2.3.3/cmd2/command_definition.py000066400000000000000000000145201416142110700173700ustar00rootroot00000000000000# coding=utf-8 """ Supports the definition of commands in separate classes to be composed into cmd2.Cmd """ from typing import ( Callable, Dict, Mapping, Optional, Type, ) from .constants import ( CLASS_ATTR_DEFAULT_HELP_CATEGORY, COMMAND_FUNC_PREFIX, ) from .exceptions import ( CommandSetRegistrationError, ) from .utils import ( Settable, ) # Allows IDEs to resolve types without impacting imports at runtime, breaking circular dependency issues try: # pragma: no cover from typing import ( TYPE_CHECKING, ) if TYPE_CHECKING: import cmd2 except ImportError: # pragma: no cover pass #: Callable signature for a basic command function #: Further refinements are needed to define the input parameters CommandFunc = Callable[..., Optional[bool]] def with_default_category(category: str, *, heritable: bool = True) -> Callable[[Type['CommandSet']], Type['CommandSet']]: """ Decorator that applies a category to all ``do_*`` command methods in a class that do not already have a category specified. CommandSets that are decorated by this with `heritable` set to True (default) will set a class attribute that is inherited by all subclasses unless overridden. All commands of this CommandSet and all subclasses of this CommandSet that do not declare an explicit category will be placed in this category. Subclasses may use this decorator to override the default category. If `heritable` is set to False, then only the commands declared locally to this CommandSet will be placed in the specified category. Dynamically created commands, and commands declared in sub-classes will not receive this category. :param category: category to put all uncategorized commands in :param heritable: Flag whether this default category should apply to sub-classes. Defaults to True :return: decorator function """ def decorate_class(cls: Type[CommandSet]) -> Type[CommandSet]: if heritable: setattr(cls, CLASS_ATTR_DEFAULT_HELP_CATEGORY, category) import inspect from .constants import ( CMD_ATTR_HELP_CATEGORY, ) from .decorators import ( with_category, ) # get members of the class that meet the following criteria: # 1. Must be a function # 2. Must start with COMMAND_FUNC_PREFIX (do_) # 3. Must be a member of the class being decorated and not one inherited from a parent declaration methods = inspect.getmembers( cls, predicate=lambda meth: inspect.isfunction(meth) and meth.__name__.startswith(COMMAND_FUNC_PREFIX) and meth in inspect.getmro(cls)[0].__dict__.values(), ) category_decorator = with_category(category) for method in methods: if not hasattr(method[1], CMD_ATTR_HELP_CATEGORY): setattr(cls, method[0], category_decorator(method[1])) return cls return decorate_class class CommandSet(object): """ Base class for defining sets of commands to load in cmd2. ``with_default_category`` can be used to apply a default category to all commands in the CommandSet. ``do_``, ``help_``, and ``complete_`` functions differ only in that self is the CommandSet instead of the cmd2 app """ def __init__(self) -> None: self._cmd: Optional[cmd2.Cmd] = None self._settables: Dict[str, Settable] = {} self._settable_prefix = self.__class__.__name__ def on_register(self, cmd: 'cmd2.Cmd') -> None: """ Called by cmd2.Cmd as the first step to registering a CommandSet. The commands defined in this class have not be added to the CLI object at this point. Subclasses can override this to perform any initialization requiring access to the Cmd object (e.g. configure commands and their parsers based on CLI state data). :param cmd: The cmd2 main application """ if self._cmd is None: self._cmd = cmd else: raise CommandSetRegistrationError('This CommandSet has already been registered') def on_registered(self) -> None: """ Called by cmd2.Cmd after a CommandSet is registered and all its commands have been added to the CLI. Subclasses can override this to perform custom steps related to the newly added commands (e.g. setting them to a disabled state). """ pass def on_unregister(self) -> None: """ Called by ``cmd2.Cmd`` as the first step to unregistering a CommandSet. Subclasses can override this to perform any cleanup steps which require their commands being registered in the CLI. """ pass def on_unregistered(self) -> None: """ Called by ``cmd2.Cmd`` after a CommandSet has been unregistered and all its commands removed from the CLI. Subclasses can override this to perform remaining cleanup steps. """ self._cmd = None @property def settable_prefix(self) -> str: return self._settable_prefix @property def settables(self) -> Mapping[str, Settable]: return self._settables def add_settable(self, settable: Settable) -> None: """ Convenience method to add a settable parameter to the CommandSet :param settable: Settable object being added """ if self._cmd: if not self._cmd.always_prefix_settables: if settable.name in self._cmd.settables.keys() and settable.name not in self._settables.keys(): raise KeyError(f'Duplicate settable: {settable.name}') else: prefixed_name = f'{self._settable_prefix}.{settable.name}' if prefixed_name in self._cmd.settables.keys() and settable.name not in self._settables.keys(): raise KeyError(f'Duplicate settable: {settable.name}') self._settables[settable.name] = settable def remove_settable(self, name: str) -> None: """ Convenience method for removing a settable parameter from the CommandSet :param name: name of the settable being removed :raises: KeyError if the Settable matches this name """ try: del self._settables[name] except KeyError: raise KeyError(name + " is not a settable parameter") cmd2-2.3.3/cmd2/constants.py000066400000000000000000000035111416142110700155540ustar00rootroot00000000000000# # coding=utf-8 """This module contains constants used throughout ``cmd2``.""" # Unless documented in https://cmd2.readthedocs.io/en/latest/api/index.html # nothing here should be considered part of the public API of this module INFINITY = float('inf') # Used for command parsing, output redirection, tab completion and word # breaks. Do not change. QUOTES = ['"', "'"] REDIRECTION_PIPE = '|' REDIRECTION_OUTPUT = '>' REDIRECTION_APPEND = '>>' REDIRECTION_CHARS = [REDIRECTION_PIPE, REDIRECTION_OUTPUT] REDIRECTION_TOKENS = [REDIRECTION_PIPE, REDIRECTION_OUTPUT, REDIRECTION_APPEND] COMMENT_CHAR = '#' MULTILINE_TERMINATOR = ';' LINE_FEED = '\n' # One character ellipsis HORIZONTAL_ELLIPSIS = '…' DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'run_script', '@@': '_relative_run_script'} # Used as the command name placeholder in disabled command messages. COMMAND_NAME = "" # All command functions start with this COMMAND_FUNC_PREFIX = 'do_' # All help functions start with this HELP_FUNC_PREFIX = 'help_' # All command completer functions start with this COMPLETER_FUNC_PREFIX = 'complete_' # The custom help category a command belongs to CMD_ATTR_HELP_CATEGORY = 'help_category' CLASS_ATTR_DEFAULT_HELP_CATEGORY = 'cmd2_default_help_category' # The argparse parser for the command CMD_ATTR_ARGPARSER = 'argparser' # Whether or not tokens are unquoted before sending to argparse CMD_ATTR_PRESERVE_QUOTES = 'preserve_quotes' # subcommand attributes for the base command name and the subcommand name SUBCMD_ATTR_COMMAND = 'parent_command' SUBCMD_ATTR_NAME = 'subcommand_name' SUBCMD_ATTR_ADD_PARSER_KWARGS = 'subcommand_add_parser_kwargs' # arpparse attribute linking to command set instance PARSER_ATTR_COMMANDSET = 'command_set' # custom attributes added to argparse Namespaces NS_ATTR_SUBCMD_HANDLER = '__subcmd_handler__' cmd2-2.3.3/cmd2/decorators.py000066400000000000000000000460531416142110700157150ustar00rootroot00000000000000# coding=utf-8 """Decorators for ``cmd2`` commands""" import argparse from typing import ( TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, ) from . import ( constants, ) from .argparse_custom import ( Cmd2AttributeWrapper, ) from .command_definition import ( CommandFunc, CommandSet, ) from .exceptions import ( Cmd2ArgparseError, ) from .parsing import ( Statement, ) if TYPE_CHECKING: # pragma: no cover import cmd2 def with_category(category: str) -> Callable[[CommandFunc], CommandFunc]: """A decorator to apply a category to a ``do_*`` command method. :param category: the name of the category in which this command should be grouped when displaying the list of commands. :Example: >>> class MyApp(cmd2.Cmd): >>> @cmd2.with_category('Text Functions') >>> def do_echo(self, args) >>> self.poutput(args) For an alternative approach to categorizing commands using a function, see :func:`~cmd2.utils.categorize` """ def cat_decorator(func: CommandFunc) -> CommandFunc: from .utils import ( categorize, ) categorize(func, category) return func return cat_decorator ########################## # The _parse_positionals and _arg_swap functions allow for additional positional args to be preserved # in cmd2 command functions/callables. As long as the 2-ple of arguments we expect to be there can be # found we can swap out the statement with each decorator's specific parameters ########################## RawCommandFuncOptionalBoolReturn = Callable[[Union[CommandSet, 'cmd2.Cmd'], Union[Statement, str]], Optional[bool]] def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Statement, str]]: """ Helper function for cmd2 decorators to inspect the positional arguments until the cmd2.Cmd argument is found Assumes that we will find cmd2.Cmd followed by the command statement object or string. :arg args: The positional arguments to inspect :return: The cmd2.Cmd reference and the command line statement """ for pos, arg in enumerate(args): from cmd2 import ( Cmd, ) if (isinstance(arg, Cmd) or isinstance(arg, CommandSet)) and len(args) > pos: if isinstance(arg, CommandSet): arg = arg._cmd next_arg = args[pos + 1] if isinstance(next_arg, (Statement, str)): return arg, args[pos + 1] # This shouldn't happen unless we forget to pass statement in `Cmd.onecmd` or # somehow call the unbound class method. raise TypeError('Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found') # pragma: no cover def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) -> List[Any]: """ Helper function for cmd2 decorators to swap the Statement parameter with one or more decorator-specific parameters :param args: The original positional arguments :param search_arg: The argument to search for (usually the Statement) :param replace_arg: The arguments to substitute in :return: The new set of arguments to pass to the command function """ index = args.index(search_arg) args_list = list(args) args_list[index : index + 1] = replace_arg return args_list #: Function signature for an Command Function that accepts a pre-processed argument list from user input #: and optionally returns a boolean ArgListCommandFuncOptionalBoolReturn = Union[ Callable[['cmd2.Cmd', List[str]], Optional[bool]], Callable[[CommandSet, List[str]], Optional[bool]], ] #: Function signature for an Command Function that accepts a pre-processed argument list from user input #: and returns a boolean ArgListCommandFuncBoolReturn = Union[ Callable[['cmd2.Cmd', List[str]], bool], Callable[[CommandSet, List[str]], bool], ] #: Function signature for an Command Function that accepts a pre-processed argument list from user input #: and returns Nothing ArgListCommandFuncNoneReturn = Union[ Callable[['cmd2.Cmd', List[str]], None], Callable[[CommandSet, List[str]], None], ] #: Aggregate of all accepted function signatures for Command Functions that accept a pre-processed argument list ArgListCommandFunc = Union[ArgListCommandFuncOptionalBoolReturn, ArgListCommandFuncBoolReturn, ArgListCommandFuncNoneReturn] def with_argument_list( func_arg: Optional[ArgListCommandFunc] = None, *, preserve_quotes: bool = False, ) -> Union[RawCommandFuncOptionalBoolReturn, Callable[[ArgListCommandFunc], RawCommandFuncOptionalBoolReturn]]: """ A decorator to alter the arguments passed to a ``do_*`` method. Default passes a string of whatever the user typed. With this decorator, the decorated method will receive a list of arguments parsed from user input. :param func_arg: Single-element positional argument list containing ``do_*`` method this decorator is wrapping :param preserve_quotes: if ``True``, then argument quotes will not be stripped :return: function that gets passed a list of argument strings :Example: >>> class MyApp(cmd2.Cmd): >>> @cmd2.with_argument_list >>> def do_echo(self, arglist): >>> self.poutput(' '.join(arglist) """ import functools def arg_decorator(func: ArgListCommandFunc) -> RawCommandFuncOptionalBoolReturn: """ Decorator function that ingests an Argument List function and returns a raw command function. The returned function will process the raw input into an argument list to be passed to the wrapped function. :param func: The defined argument list command function :return: Function that takes raw input and converts to an argument list to pass to the wrapped function. """ @functools.wraps(func) def cmd_wrapper(*args: Any, **kwargs: Any) -> Optional[bool]: """ Command function wrapper which translates command line into an argument list and calls actual command function :param args: All positional arguments to this function. We're expecting there to be: cmd2_app, statement: Union[Statement, str] contiguously somewhere in the list :param kwargs: any keyword arguments being passed to command function :return: return value of command function """ cmd2_app, statement = _parse_positionals(args) _, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes) args_list = _arg_swap(args, statement, parsed_arglist) return func(*args_list, **kwargs) # type: ignore[call-arg] command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :] cmd_wrapper.__doc__ = func.__doc__ return cmd_wrapper if callable(func_arg): # noinspection PyTypeChecker return arg_decorator(func_arg) else: # noinspection PyTypeChecker return arg_decorator # noinspection PyProtectedMember def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None: """ Recursively set prog attribute of a parser and all of its subparsers so that the root command is a command name and not sys.argv[0]. :param parser: the parser being edited :param prog: new value for the parser's prog attribute """ # Set the prog value for this parser parser.prog = prog # Set the prog value for the parser's subcommands for action in parser._actions: if isinstance(action, argparse._SubParsersAction): # Set the _SubParsersAction's _prog_prefix value. That way if its add_parser() method is called later, # the correct prog value will be set on the parser being added. action._prog_prefix = parser.prog # The keys of action.choices are subcommand names as well as subcommand aliases. The aliases point to the # same parser as the actual subcommand. We want to avoid placing an alias into a parser's prog value. # Unfortunately there is nothing about an action.choices entry which tells us it's an alias. In most cases # we can filter out the aliases by checking the contents of action._choices_actions. This list only contains # help information and names for the subcommands and not aliases. However, subcommands without help text # won't show up in that list. Since dictionaries are ordered in Python 3.6 and above and argparse inserts the # subcommand name into choices dictionary before aliases, we should be OK assuming the first time we see a # parser, the dictionary key is a subcommand and not alias. processed_parsers = [] # Set the prog value for each subcommand's parser for subcmd_name, subcmd_parser in action.choices.items(): # Check if we've already edited this parser if subcmd_parser in processed_parsers: continue subcmd_prog = parser.prog + ' ' + subcmd_name _set_parser_prog(subcmd_parser, subcmd_prog) processed_parsers.append(subcmd_parser) # We can break since argparse only allows 1 group of subcommands per level break #: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input #: and optionally returns a boolean ArgparseCommandFuncOptionalBoolReturn = Union[ Callable[['cmd2.Cmd', argparse.Namespace], Optional[bool]], Callable[[CommandSet, argparse.Namespace], Optional[bool]], ] #: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input #: and returns a boolean ArgparseCommandFuncBoolReturn = Union[ Callable[['cmd2.Cmd', argparse.Namespace], bool], Callable[[CommandSet, argparse.Namespace], bool], ] #: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input #: and returns nothing ArgparseCommandFuncNoneReturn = Union[ Callable[['cmd2.Cmd', argparse.Namespace], None], Callable[[CommandSet, argparse.Namespace], None], ] #: Aggregate of all accepted function signatures for an argparse Command Function ArgparseCommandFunc = Union[ ArgparseCommandFuncOptionalBoolReturn, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn, ] def with_argparser( parser: argparse.ArgumentParser, *, ns_provider: Optional[Callable[..., argparse.Namespace]] = None, preserve_quotes: bool = False, with_unknown_args: bool = False, ) -> Callable[[ArgparseCommandFunc], RawCommandFuncOptionalBoolReturn]: """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given instance of argparse.ArgumentParser. :param parser: unique instance of ArgumentParser :param ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that affects parsing. :param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes :param with_unknown_args: if true, then capture unknown args :return: function that gets passed argparse-parsed args in a ``Namespace`` A :class:`cmd2.argparse_custom.Cmd2AttributeWrapper` called ``cmd2_statement`` is included in the ``Namespace`` to provide access to the :class:`cmd2.Statement` object that was created when parsing the command line. This can be useful if the command function needs to know the command line. :Example: >>> parser = cmd2.Cmd2ArgumentParser() >>> parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') >>> parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') >>> parser.add_argument('-r', '--repeat', type=int, help='output [n] times') >>> parser.add_argument('words', nargs='+', help='words to print') >>> >>> class MyApp(cmd2.Cmd): >>> @cmd2.with_argparser(parser, preserve_quotes=True) >>> def do_argprint(self, args): >>> "Print the options and argument list this options command was called with." >>> self.poutput(f'args: {args!r}') :Example with unknown args: >>> parser = cmd2.Cmd2ArgumentParser() >>> parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') >>> parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') >>> parser.add_argument('-r', '--repeat', type=int, help='output [n] times') >>> >>> class MyApp(cmd2.Cmd): >>> @cmd2.with_argparser(parser, with_unknown_args=True) >>> def do_argprint(self, args, unknown): >>> "Print the options and argument list this options command was called with." >>> self.poutput(f'args: {args!r}') >>> self.poutput(f'unknowns: {unknown}') """ import functools def arg_decorator(func: ArgparseCommandFunc) -> RawCommandFuncOptionalBoolReturn: """ Decorator function that ingests an Argparse Command Function and returns a raw command function. The returned function will process the raw input into an argparse Namespace to be passed to the wrapped function. :param func: The defined argparse command function :return: Function that takes raw input and converts to an argparse Namespace to passed to the wrapped function. """ @functools.wraps(func) def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]: """ Command function wrapper which translates command line into argparse Namespace and calls actual command function :param args: All positional arguments to this function. We're expecting there to be: cmd2_app, statement: Union[Statement, str] contiguously somewhere in the list :param kwargs: any keyword arguments being passed to command function :return: return value of command function :raises: Cmd2ArgparseError if argparse has error parsing command line """ cmd2_app, statement_arg = _parse_positionals(args) statement, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list( command_name, statement_arg, preserve_quotes ) if ns_provider is None: namespace = None else: # The namespace provider may or may not be defined in the same class as the command. Since provider # functions are registered with the command argparser before anything is instantiated, we # need to find an instance at runtime that matches the types during declaration provider_self = cmd2_app._resolve_func_self(ns_provider, args[0]) namespace = ns_provider(provider_self if provider_self is not None else cmd2_app) try: new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]] if with_unknown_args: new_args = parser.parse_known_args(parsed_arglist, namespace) else: new_args = (parser.parse_args(parsed_arglist, namespace),) ns = new_args[0] except SystemExit: raise Cmd2ArgparseError else: # Add wrapped statement to Namespace as cmd2_statement setattr(ns, 'cmd2_statement', Cmd2AttributeWrapper(statement)) # Add wrapped subcmd handler (which can be None) to Namespace as cmd2_handler handler = getattr(ns, constants.NS_ATTR_SUBCMD_HANDLER, None) setattr(ns, 'cmd2_handler', Cmd2AttributeWrapper(handler)) # Remove the subcmd handler attribute from the Namespace # since cmd2_handler is how a developer accesses it. if hasattr(ns, constants.NS_ATTR_SUBCMD_HANDLER): delattr(ns, constants.NS_ATTR_SUBCMD_HANDLER) args_list = _arg_swap(args, statement_arg, *new_args) return func(*args_list, **kwargs) # type: ignore[call-arg] # argparser defaults the program name to sys.argv[0], but we want it to be the name of our command command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :] _set_parser_prog(parser, command_name) # If the description has not been set, then use the method docstring if one exists if parser.description is None and func.__doc__: parser.description = func.__doc__ # Set the command's help text as argparser.description (which can be None) cmd_wrapper.__doc__ = parser.description # Set some custom attributes for this command setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser) setattr(cmd_wrapper, constants.CMD_ATTR_PRESERVE_QUOTES, preserve_quotes) return cmd_wrapper # noinspection PyTypeChecker return arg_decorator def as_subcommand_to( command: str, subcommand: str, parser: argparse.ArgumentParser, *, help: Optional[str] = None, aliases: Optional[List[str]] = None, ) -> Callable[[ArgparseCommandFunc], ArgparseCommandFunc]: """ Tag this method as a subcommand to an existing argparse decorated command. :param command: Command Name. Space-delimited subcommands may optionally be specified :param subcommand: Subcommand name :param parser: argparse Parser for this subcommand :param help: Help message for this subcommand which displays in the list of subcommands of the command we are adding to. This is passed as the help argument to ArgumentParser.add_subparser(). :param aliases: Alternative names for this subcommand. This is passed as the alias argument to ArgumentParser.add_subparser(). :return: Wrapper function that can receive an argparse.Namespace """ def arg_decorator(func: ArgparseCommandFunc) -> ArgparseCommandFunc: _set_parser_prog(parser, command + ' ' + subcommand) # If the description has not been set, then use the method docstring if one exists if parser.description is None and func.__doc__: parser.description = func.__doc__ # Set some custom attributes for this command setattr(func, constants.SUBCMD_ATTR_COMMAND, command) setattr(func, constants.CMD_ATTR_ARGPARSER, parser) setattr(func, constants.SUBCMD_ATTR_NAME, subcommand) # Keyword arguments for ArgumentParser.add_subparser() add_parser_kwargs: Dict[str, Any] = dict() if help is not None: add_parser_kwargs['help'] = help if aliases: add_parser_kwargs['aliases'] = aliases[:] setattr(func, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, add_parser_kwargs) return func # noinspection PyTypeChecker return arg_decorator cmd2-2.3.3/cmd2/exceptions.py000066400000000000000000000070661416142110700157320ustar00rootroot00000000000000# coding=utf-8 """Custom exceptions for cmd2""" from typing import ( Any, ) ############################################################################################################ # The following exceptions are part of the public API ############################################################################################################ class SkipPostcommandHooks(Exception): """ Custom exception class for when a command has a failure bad enough to skip post command hooks, but not bad enough to print the exception to the user. """ pass class Cmd2ArgparseError(SkipPostcommandHooks): """ A ``SkipPostcommandHooks`` exception for when a command fails to parse its arguments. Normally argparse raises a SystemExit exception in these cases. To avoid stopping the command loop, catch the SystemExit and raise this instead. If you still need to run post command hooks after parsing fails, just return instead of raising an exception. """ pass class CommandSetRegistrationError(Exception): """ Exception that can be thrown when an error occurs while a CommandSet is being added or removed from a cmd2 application. """ pass class CompletionError(Exception): """ Raised during tab completion operations to report any sort of error you want printed. This can also be used just to display a message, even if it's not an error. For instance, ArgparseCompleter raises CompletionErrors to display tab completion hints and sets apply_style to False so hints aren't colored like error text. Example use cases - Reading a database to retrieve a tab completion data set failed - A previous command line argument that determines the data set being completed is invalid - Tab completion hints """ def __init__(self, *args: Any, apply_style: bool = True) -> None: """ Initializer for CompletionError :param apply_style: If True, then ansi.style_error will be applied to the message text when printed. Set to False in cases where the message text already has the desired style. Defaults to True. """ self.apply_style = apply_style # noinspection PyArgumentList super().__init__(*args) class PassThroughException(Exception): """ Normally all unhandled exceptions raised during commands get printed to the user. This class is used to wrap an exception that should be raised instead of printed. """ def __init__(self, *args: Any, wrapped_ex: BaseException) -> None: """ Initializer for PassThroughException :param wrapped_ex: the exception that will be raised """ self.wrapped_ex = wrapped_ex super().__init__(*args) ############################################################################################################ # The following exceptions are NOT part of the public API and are intended for internal use only. ############################################################################################################ class Cmd2ShlexError(Exception): """Raised when shlex fails to parse a command line string in StatementParser""" pass class EmbeddedConsoleExit(SystemExit): """Custom exception class for use with the py command.""" pass class EmptyStatement(Exception): """Custom exception class for handling behavior when the user just presses .""" pass class RedirectionError(Exception): """Custom exception class for when redirecting or piping output fails""" pass cmd2-2.3.3/cmd2/history.py000066400000000000000000000342741416142110700152530ustar00rootroot00000000000000# coding=utf-8 """ History management classes """ import json import re from collections import ( OrderedDict, ) from typing import ( Any, Callable, Dict, Iterable, List, Optional, Union, overload, ) import attr from . import ( utils, ) from .parsing import ( Statement, ) @attr.s(auto_attribs=True, frozen=True) class HistoryItem: """Class used to represent one command in the history list""" _listformat = ' {:>4} {}' _ex_listformat = ' {:>4}x {}' # Used in JSON dictionaries _statement_field = 'statement' statement: Statement = attr.ib(default=None, validator=attr.validators.instance_of(Statement)) def __str__(self) -> str: """A convenient human readable representation of the history item""" return self.statement.raw @property def raw(self) -> str: """The raw input from the user for this item. Proxy property for ``self.statement.raw`` """ return self.statement.raw @property def expanded(self) -> str: """Return the command as run which includes shortcuts and aliases resolved plus any changes made in hooks Proxy property for ``self.statement.expanded_command_line`` """ return self.statement.expanded_command_line def pr(self, idx: int, script: bool = False, expanded: bool = False, verbose: bool = False) -> str: """Represent this item in a pretty fashion suitable for printing. If you pass verbose=True, script and expanded will be ignored :param idx: The 1-based index of this item in the history list :param script: True if formatting for a script (No item numbers) :param expanded: True if expanded command line should be printed :param verbose: True if expanded and raw should both appear when they are different :return: pretty print string version of a HistoryItem """ if verbose: raw = self.raw.rstrip() expanded_command = self.expanded ret_str = self._listformat.format(idx, raw) if raw != expanded_command: ret_str += '\n' + self._ex_listformat.format(idx, expanded_command) else: if expanded: ret_str = self.expanded else: ret_str = self.raw.rstrip() # In non-verbose mode, display raw multiline commands on 1 line if self.statement.multiline_command: # This is an approximation and not meant to be a perfect piecing together of lines. # All newlines will be converted to spaces, including the ones in quoted strings that # are considered literals. Also if the final line starts with a terminator, then the # terminator will have an extra space before it in the 1 line version. ret_str = ret_str.replace('\n', ' ') # Display a numbered list if not writing to a script if not script: ret_str = self._listformat.format(idx, ret_str) return ret_str def to_dict(self) -> Dict[str, Any]: """Utility method to convert this HistoryItem into a dictionary for use in persistent JSON history files""" return {HistoryItem._statement_field: self.statement.to_dict()} @staticmethod def from_dict(source_dict: Dict[str, Any]) -> 'HistoryItem': """ Utility method to restore a HistoryItem from a dictionary :param source_dict: source data dictionary (generated using to_dict()) :return: HistoryItem object :raises KeyError: if source_dict is missing required elements """ statement_dict = source_dict[HistoryItem._statement_field] return HistoryItem(Statement.from_dict(statement_dict)) class History(List[HistoryItem]): """A list of :class:`~cmd2.history.HistoryItem` objects with additional methods for searching and managing the list. :class:`~cmd2.Cmd` instantiates this class into the :data:`~cmd2.Cmd.history` attribute, and adds commands to it as a user enters them. See :ref:`features/history:History` for information about the built-in command which allows users to view, search, run, and save previously entered commands. Developers interested in accessing previously entered commands can use this class to gain access to the historical record. """ # Used in JSON dictionaries _history_version = '1.0.0' _history_version_field = 'history_version' _history_items_field = 'history_items' def __init__(self, seq: Iterable[HistoryItem] = ()) -> None: super(History, self).__init__(seq) self.session_start_index = 0 def start_session(self) -> None: """Start a new session, thereby setting the next index as the first index in the new session.""" self.session_start_index = len(self) # noinspection PyMethodMayBeStatic def _zero_based_index(self, onebased: Union[int, str]) -> int: """Convert a one-based index to a zero-based index.""" result = int(onebased) if result > 0: result -= 1 return result @overload def append(self, new: HistoryItem) -> None: ... # pragma: no cover @overload def append(self, new: Statement) -> None: ... # pragma: no cover def append(self, new: Union[Statement, HistoryItem]) -> None: """Append a new statement to the end of the History list. :param new: Statement object which will be composed into a HistoryItem and added to the end of the list """ history_item = HistoryItem(new) if isinstance(new, Statement) else new super(History, self).append(history_item) def clear(self) -> None: """Remove all items from the History list.""" super().clear() self.start_session() def get(self, index: int) -> HistoryItem: """Get item from the History list using 1-based indexing. :param index: optional item to get :return: a single :class:`~cmd2.history.HistoryItem` """ if index == 0: raise IndexError('The first command in history is command 1.') elif index < 0: return self[index] else: return self[index - 1] # This regular expression parses input for the span() method. There are five parts: # # ^\s* matches any whitespace at the beginning of the # input. This is here so you don't have to trim the input # # (?P-?[1-9]{1}\d*)? create a capture group named 'start' which matches an # optional minus sign, followed by exactly one non-zero # digit, and as many other digits as you want. This group # is optional so that we can match an input string like '..2'. # This regex will match 1, -1, 10, -10, but not 0 or -0. # # (?P:|(\.{2,}))? create a capture group named 'separator' which matches either # a colon or two periods. # # (?P-?[1-9]{1}\d*)? create a capture group named 'end' which matches an # optional minus sign, followed by exactly one non-zero # digit, and as many other digits as you want. This group is # optional so that we can match an input string like ':' # or '5:'. This regex will match 1, -1, 10, -10, but not # 0 or -0. # # \s*$ match any whitespace at the end of the input. This is here so # you don't have to trim the input # spanpattern = re.compile(r'^\s*(?P-?[1-9]\d*)?(?P:|(\.{2,}))(?P-?[1-9]\d*)?\s*$') def span(self, span: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]': """Return a slice of the History list :param span: string containing an index or a slice :param include_persisted: if True, then retrieve full results including from persisted history :return: a dictionary of history items keyed by their 1-based index in ascending order, or an empty dictionary if no results were found This method can accommodate input in any of these forms: a..b or a:b a.. or a: ..a or :a -a.. or -a: ..-a or :-a Different from native python indexing and slicing of arrays, this method uses 1-based array numbering. Users who are not programmers can't grok zero based numbering. Programmers can sometimes grok zero based numbering. Which reminds me, there are only two hard problems in programming: - naming - cache invalidation - off by one errors """ results = self.spanpattern.search(span) if not results: # our regex doesn't match the input, bail out raise ValueError('History indices must be positive or negative integers, and may not be zero.') start_token = results.group('start') if start_token: start = min(self._zero_based_index(start_token), len(self) - 1) if start < 0: start = max(0, len(self) + start) else: start = 0 if include_persisted else self.session_start_index end_token = results.group('end') if end_token: end = min(int(end_token), len(self)) if end < 0: end = max(0, len(self) + end + 1) else: end = len(self) return self._build_result_dictionary(start, end) def str_search(self, search: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]': """Find history items which contain a given string :param search: the string to search for :param include_persisted: if True, then search full history including persisted history :return: a dictionary of history items keyed by their 1-based index in ascending order, or an empty dictionary if the string was not found """ def isin(history_item: HistoryItem) -> bool: """filter function for string search of history""" sloppy = utils.norm_fold(search) inraw = sloppy in utils.norm_fold(history_item.raw) inexpanded = sloppy in utils.norm_fold(history_item.expanded) return inraw or inexpanded start = 0 if include_persisted else self.session_start_index return self._build_result_dictionary(start, len(self), isin) def regex_search(self, regex: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]': """Find history items which match a given regular expression :param regex: the regular expression to search for. :param include_persisted: if True, then search full history including persisted history :return: a dictionary of history items keyed by their 1-based index in ascending order, or an empty dictionary if the regex was not matched """ regex = regex.strip() if regex.startswith(r'/') and regex.endswith(r'/'): regex = regex[1:-1] finder = re.compile(regex, re.DOTALL | re.MULTILINE) def isin(hi: HistoryItem) -> bool: """filter function for doing a regular expression search of history""" return bool(finder.search(hi.raw) or finder.search(hi.expanded)) start = 0 if include_persisted else self.session_start_index return self._build_result_dictionary(start, len(self), isin) def truncate(self, max_length: int) -> None: """Truncate the length of the history, dropping the oldest items if necessary :param max_length: the maximum length of the history, if negative, all history items will be deleted :return: nothing """ if max_length <= 0: # remove all history del self[:] elif len(self) > max_length: last_element = len(self) - max_length del self[0:last_element] def _build_result_dictionary( self, start: int, end: int, filter_func: Optional[Callable[[HistoryItem], bool]] = None ) -> 'OrderedDict[int, HistoryItem]': """ Build history search results :param start: start index to search from :param end: end index to stop searching (exclusive) """ results: OrderedDict[int, HistoryItem] = OrderedDict() for index in range(start, end): if filter_func is None or filter_func(self[index]): results[index + 1] = self[index] return results def to_json(self) -> str: """Utility method to convert this History into a JSON string for use in persistent history files""" json_dict = { History._history_version_field: History._history_version, History._history_items_field: [hi.to_dict() for hi in self], } return json.dumps(json_dict, ensure_ascii=False, indent=2) @staticmethod def from_json(history_json: str) -> 'History': """ Utility method to restore History from a JSON string :param history_json: history data as JSON string (generated using to_json()) :return: History object :raises json.JSONDecodeError: if passed invalid JSON string :raises KeyError: if JSON is missing required elements :raises ValueError: if history version in JSON isn't supported """ json_dict = json.loads(history_json) version = json_dict[History._history_version_field] if version != History._history_version: raise ValueError( f"Unsupported history file version: {version}. This application uses version {History._history_version}." ) items = json_dict[History._history_items_field] history = History() for hi_dict in items: history.append(HistoryItem.from_dict(hi_dict)) return history cmd2-2.3.3/cmd2/parsing.py000077500000000000000000000705441416142110700152200ustar00rootroot00000000000000# # -*- coding: utf-8 -*- """Statement parsing classes for cmd2""" import re import shlex from typing import ( Any, Dict, Iterable, List, Optional, Tuple, Union, ) import attr from . import ( constants, utils, ) from .exceptions import ( Cmd2ShlexError, ) def shlex_split(str_to_split: str) -> List[str]: """ A wrapper around shlex.split() that uses cmd2's preferred arguments. This allows other classes to easily call split() the same way StatementParser does. :param str_to_split: the string being split :return: A list of tokens """ return shlex.split(str_to_split, comments=False, posix=False) @attr.s(auto_attribs=True, frozen=True) class MacroArg: """ Information used to replace or unescape arguments in a macro value when the macro is resolved Normal argument syntax: {5} Escaped argument syntax: {{5}} """ # The starting index of this argument in the macro value start_index: int = attr.ib(validator=attr.validators.instance_of(int)) # The number string that appears between the braces # This is a string instead of an int because we support unicode digits and must be able # to reproduce this string later number_str: str = attr.ib(validator=attr.validators.instance_of(str)) # Tells if this argument is escaped and therefore needs to be unescaped is_escaped: bool = attr.ib(validator=attr.validators.instance_of(bool)) # Pattern used to find normal argument # Digits surrounded by exactly 1 brace on a side and 1 or more braces on the opposite side # Match strings like: {5}, {{{{{4}, {2}}}}} macro_normal_arg_pattern = re.compile(r'(?`_ is your friend for anything complex. ``cmd2`` has the decorator (:func:`~cmd2.decorators.with_argparser`) which you can use to make your command method receive a namespace of parsed arguments, whether positional or denoted with switches. 2. For commands with simple positional arguments, use :attr:`~cmd2.Statement.args` or :attr:`~cmd2.Statement.arg_list` 3. If you don't want to have to worry about quoted arguments, see :attr:`argv` for a trick which strips quotes off for you. """ # the arguments, but not the command, nor the output redirection clauses. args: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # string containing exactly what we input by the user raw: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # the command, i.e. the first whitespace delimited word command: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # list of arguments to the command, not including any output redirection or terminators; quoted args remain quoted arg_list: List[str] = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list)) # if the command is a multiline command, the name of the command, otherwise empty multiline_command: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # the character which terminated the multiline command, if there was one terminator: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # characters appearing after the terminator but before output redirection, if any suffix: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # if output was piped to a shell command, the shell command as a string pipe_to: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # if output was redirected, the redirection token, i.e. '>>' output: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # if output was redirected, the destination file token (quotes preserved) output_to: str = attr.ib(default='', validator=attr.validators.instance_of(str)) # Used in JSON dictionaries _args_field = 'args' def __new__(cls, value: object, *pos_args: Any, **kw_args: Any) -> 'Statement': """Create a new instance of Statement. We must override __new__ because we are subclassing `str` which is immutable and takes a different number of arguments as Statement. NOTE: attrs takes care of initializing other members in the __init__ it generates. """ stmt = super().__new__(cls, value) return stmt @property def command_and_args(self) -> str: """Combine command and args with a space separating them. Quoted arguments remain quoted. Output redirection and piping are excluded, as are any command terminators. """ if self.command and self.args: rtn = f'{self.command} {self.args}' elif self.command: # there were no arguments to the command rtn = self.command else: rtn = '' return rtn @property def post_command(self) -> str: """A string containing any ending terminator, suffix, and redirection chars""" rtn = '' if self.terminator: rtn += self.terminator if self.suffix: rtn += ' ' + self.suffix if self.pipe_to: rtn += ' | ' + self.pipe_to if self.output: rtn += ' ' + self.output if self.output_to: rtn += ' ' + self.output_to return rtn @property def expanded_command_line(self) -> str: """Concatenate :meth:`~cmd2.Statement.command_and_args` and :meth:`~cmd2.Statement.post_command`""" return self.command_and_args + self.post_command @property def argv(self) -> List[str]: """a list of arguments a-la ``sys.argv``. The first element of the list is the command after shortcut and macro expansion. Subsequent elements of the list contain any additional arguments, with quotes removed, just like bash would. This is very useful if you are going to use ``argparse.parse_args()``. If you want to strip quotes from the input, you can use ``argv[1:]``. """ if self.command: rtn = [utils.strip_quotes(self.command)] for cur_token in self.arg_list: rtn.append(utils.strip_quotes(cur_token)) else: rtn = [] return rtn def to_dict(self) -> Dict[str, Any]: """Utility method to convert this Statement into a dictionary for use in persistent JSON history files""" return self.__dict__.copy() @staticmethod def from_dict(source_dict: Dict[str, Any]) -> 'Statement': """ Utility method to restore a Statement from a dictionary :param source_dict: source data dictionary (generated using to_dict()) :return: Statement object :raises KeyError: if source_dict is missing required elements """ # value needs to be passed as a positional argument. It corresponds to the args field. try: value = source_dict[Statement._args_field] except KeyError as ex: raise KeyError(f"Statement dictionary is missing {ex} field") # Pass the rest at kwargs (minus args) kwargs = source_dict.copy() del kwargs[Statement._args_field] return Statement(value, **kwargs) class StatementParser: """Parse user input as a string into discrete command components.""" def __init__( self, terminators: Optional[Iterable[str]] = None, multiline_commands: Optional[Iterable[str]] = None, aliases: Optional[Dict[str, str]] = None, shortcuts: Optional[Dict[str, str]] = None, ) -> None: """Initialize an instance of StatementParser. The following will get converted to an immutable tuple before storing internally: terminators, multiline commands, and shortcuts. :param terminators: iterable containing strings which should terminate commands :param multiline_commands: iterable containing the names of commands that accept multiline input :param aliases: dictionary containing aliases :param shortcuts: dictionary containing shortcuts """ self.terminators: Tuple[str, ...] if terminators is None: self.terminators = (constants.MULTILINE_TERMINATOR,) else: self.terminators = tuple(terminators) self.multiline_commands: Tuple[str, ...] = tuple(multiline_commands) if multiline_commands is not None else () self.aliases: Dict[str, str] = aliases if aliases is not None else {} if shortcuts is None: shortcuts = constants.DEFAULT_SHORTCUTS # Sort the shortcuts in descending order by name length because the longest match # should take precedence. (e.g., @@file should match '@@' and not '@'. self.shortcuts = tuple(sorted(shortcuts.items(), key=lambda x: len(x[0]), reverse=True)) # commands have to be a word, so make a regular expression # that matches the first word in the line. This regex has three # parts: # - the '\A\s*' matches the beginning of the string (even # if contains multiple lines) and gobbles up any leading # whitespace # - the first parenthesis enclosed group matches one # or more non-whitespace characters with a non-greedy match # (that's what the '+?' part does). The non-greedy match # ensures that this first group doesn't include anything # matched by the second group # - the second parenthesis group must be dynamically created # because it needs to match either whitespace, something in # REDIRECTION_CHARS, one of the terminators, or the end of # the string (\Z matches the end of the string even if it # contains multiple lines) # invalid_command_chars = [] invalid_command_chars.extend(constants.QUOTES) invalid_command_chars.extend(constants.REDIRECTION_CHARS) invalid_command_chars.extend(self.terminators) # escape each item so it will for sure get treated as a literal second_group_items = [re.escape(x) for x in invalid_command_chars] # add the whitespace and end of string, not escaped because they # are not literals second_group_items.extend([r'\s', r'\Z']) # join them up with a pipe second_group = '|'.join(second_group_items) # build the regular expression expr = rf'\A\s*(\S*?)({second_group})' self._command_pattern = re.compile(expr) def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> Tuple[bool, str]: """Determine whether a word is a valid name for a command. Commands cannot include redirection characters, whitespace, or termination characters. They also cannot start with a shortcut. :param word: the word to check as a command :param is_subcommand: Flag whether this command name is a subcommand name :return: a tuple of a boolean and an error string If word is not a valid command, return ``False`` and an error string suitable for inclusion in an error message of your choice:: checkit = '>' valid, errmsg = statement_parser.is_valid_command(checkit) if not valid: errmsg = f"alias: {errmsg}" """ valid = False if not isinstance(word, str): return False, f'must be a string. Received {str(type(word))} instead' # type: ignore[unreachable] if not word: return False, 'cannot be an empty string' if word.startswith(constants.COMMENT_CHAR): return False, 'cannot start with the comment character' if not is_subcommand: for (shortcut, _) in self.shortcuts: if word.startswith(shortcut): # Build an error string with all shortcuts listed errmsg = 'cannot start with a shortcut: ' errmsg += ', '.join(shortcut for (shortcut, _) in self.shortcuts) return False, errmsg errmsg = 'cannot contain: whitespace, quotes, ' errchars = [] errchars.extend(constants.REDIRECTION_CHARS) errchars.extend(self.terminators) errmsg += ', '.join([shlex.quote(x) for x in errchars]) match = self._command_pattern.search(word) if match: if word == match.group(1): valid = True errmsg = '' return valid, errmsg def tokenize(self, line: str) -> List[str]: """ Lex a string into a list of tokens. Shortcuts and aliases are expanded and comments are removed. :param line: the command line being lexed :return: A list of tokens :raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation) """ # expand shortcuts and aliases line = self._expand(line) # check if this line is a comment if line.lstrip().startswith(constants.COMMENT_CHAR): return [] # split on whitespace try: tokens = shlex_split(line) except ValueError as ex: raise Cmd2ShlexError(ex) # custom lexing tokens = self.split_on_punctuation(tokens) return tokens def parse(self, line: str) -> Statement: """ Tokenize the input and parse it into a :class:`~cmd2.Statement` object, stripping comments, expanding aliases and shortcuts, and extracting output redirection directives. :param line: the command line being parsed :return: a new :class:`~cmd2.Statement` object :raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation) """ # handle the special case/hardcoded terminator of a blank line # we have to do this before we tokenize because tokenizing # destroys all unquoted whitespace in the input terminator = '' if line[-1:] == constants.LINE_FEED: terminator = constants.LINE_FEED command = '' args = '' arg_list = [] # lex the input into a list of tokens tokens = self.tokenize(line) # of the valid terminators, find the first one to occur in the input terminator_pos = len(tokens) + 1 for pos, cur_token in enumerate(tokens): for test_terminator in self.terminators: if cur_token.startswith(test_terminator): terminator_pos = pos terminator = test_terminator # break the inner loop, and we want to break the # outer loop too break else: # this else clause is only run if the inner loop # didn't execute a break. If it didn't, then # continue to the next iteration of the outer loop continue # inner loop was broken, break the outer break if terminator: if terminator == constants.LINE_FEED: terminator_pos = len(tokens) + 1 # everything before the first terminator is the command and the args (command, args) = self._command_and_args(tokens[:terminator_pos]) arg_list = tokens[1:terminator_pos] # we will set the suffix later # remove all the tokens before and including the terminator tokens = tokens[terminator_pos + 1 :] else: (testcommand, testargs) = self._command_and_args(tokens) if testcommand in self.multiline_commands: # no terminator on this line but we have a multiline command # everything else on the line is part of the args # because redirectors can only be after a terminator command = testcommand args = testargs arg_list = tokens[1:] tokens = [] pipe_to = '' output = '' output_to = '' # Find which redirector character appears first in the command try: pipe_index = tokens.index(constants.REDIRECTION_PIPE) except ValueError: pipe_index = len(tokens) try: redir_index = tokens.index(constants.REDIRECTION_OUTPUT) except ValueError: redir_index = len(tokens) try: append_index = tokens.index(constants.REDIRECTION_APPEND) except ValueError: append_index = len(tokens) # Check if output should be piped to a shell command if pipe_index < redir_index and pipe_index < append_index: # Get the tokens for the pipe command and expand ~ where needed pipe_to_tokens = tokens[pipe_index + 1 :] utils.expand_user_in_tokens(pipe_to_tokens) # Build the pipe command line string pipe_to = ' '.join(pipe_to_tokens) # remove all the tokens after the pipe tokens = tokens[:pipe_index] # Check for output redirect/append elif redir_index != append_index: if redir_index < append_index: output = constants.REDIRECTION_OUTPUT output_index = redir_index else: output = constants.REDIRECTION_APPEND output_index = append_index # Check if we are redirecting to a file if len(tokens) > output_index + 1: unquoted_path = utils.strip_quotes(tokens[output_index + 1]) if unquoted_path: output_to = utils.expand_user(tokens[output_index + 1]) # remove all the tokens after the output redirect tokens = tokens[:output_index] if terminator: # whatever is left is the suffix suffix = ' '.join(tokens) else: # no terminator, so whatever is left is the command and the args suffix = '' if not command: # command could already have been set, if so, don't set it again (command, args) = self._command_and_args(tokens) arg_list = tokens[1:] # set multiline if command in self.multiline_commands: multiline_command = command else: multiline_command = '' # build the statement statement = Statement( args, raw=line, command=command, arg_list=arg_list, multiline_command=multiline_command, terminator=terminator, suffix=suffix, pipe_to=pipe_to, output=output, output_to=output_to, ) return statement def parse_command_only(self, rawinput: str) -> Statement: """Partially parse input into a :class:`~cmd2.Statement` object. The command is identified, and shortcuts and aliases are expanded. Multiline commands are identified, but terminators and output redirection are not parsed. This method is used by tab completion code and therefore must not generate an exception if there are unclosed quotes. The :class:`~cmd2.Statement` object returned by this method can at most contain values in the following attributes: :attr:`~cmd2.Statement.args`, :attr:`~cmd2.Statement.raw`, :attr:`~cmd2.Statement.command`, :attr:`~cmd2.Statement.multiline_command` :attr:`~cmd2.Statement.args` will include all output redirection clauses and command terminators. Different from :meth:`~cmd2.parsing.StatementParser.parse` this method does not remove redundant whitespace within args. However, it does ensure args has no leading or trailing whitespace. :param rawinput: the command line as entered by the user :return: a new :class:`~cmd2.Statement` object """ # expand shortcuts and aliases line = self._expand(rawinput) command = '' args = '' match = self._command_pattern.search(line) if match: # we got a match, extract the command command = match.group(1) # take everything from the end of the first match group to # the end of the line as the arguments (stripping leading # and trailing spaces) args = line[match.end(1) :].strip() # if the command is empty that means the input was either empty # or something weird like '>'. args should be empty if we couldn't # parse a command if not command or not args: args = '' # set multiline if command in self.multiline_commands: multiline_command = command else: multiline_command = '' # build the statement statement = Statement(args, raw=rawinput, command=command, multiline_command=multiline_command) return statement def get_command_arg_list( self, command_name: str, to_parse: Union[Statement, str], preserve_quotes: bool ) -> Tuple[Statement, List[str]]: """ Convenience method used by the argument parsing decorators. Retrieves just the arguments being passed to their ``do_*`` methods as a list. :param command_name: name of the command being run :param to_parse: what is being passed to the ``do_*`` method. It can be one of two types: 1. An already parsed :class:`~cmd2.Statement` 2. An argument string in cases where a ``do_*`` method is explicitly called. Calling ``do_help('alias create')`` would cause ``to_parse`` to be 'alias create'. In this case, the string will be converted to a :class:`~cmd2.Statement` and returned along with the argument list. :param preserve_quotes: if ``True``, then quotes will not be stripped from the arguments :return: A tuple containing the :class:`~cmd2.Statement` and a list of strings representing the arguments """ # Check if to_parse needs to be converted to a Statement if not isinstance(to_parse, Statement): to_parse = self.parse(command_name + ' ' + to_parse) if preserve_quotes: return to_parse, to_parse.arg_list else: return to_parse, to_parse.argv[1:] def _expand(self, line: str) -> str: """Expand aliases and shortcuts""" # Make a copy of aliases so we can keep track of what aliases have been resolved to avoid an infinite loop remaining_aliases = list(self.aliases.keys()) keep_expanding = bool(remaining_aliases) while keep_expanding: keep_expanding = False # apply our regex to line match = self._command_pattern.search(line) if match: # we got a match, extract the command command = match.group(1) # Check if this command matches an alias that wasn't already processed if command in remaining_aliases: # rebuild line with the expanded alias line = self.aliases[command] + match.group(2) + line[match.end(2) :] remaining_aliases.remove(command) keep_expanding = bool(remaining_aliases) # expand shortcuts for (shortcut, expansion) in self.shortcuts: if line.startswith(shortcut): # If the next character after the shortcut isn't a space, then insert one shortcut_len = len(shortcut) if len(line) == shortcut_len or line[shortcut_len] != ' ': expansion += ' ' # Expand the shortcut line = line.replace(shortcut, expansion, 1) break return line @staticmethod def _command_and_args(tokens: List[str]) -> Tuple[str, str]: """Given a list of tokens, return a tuple of the command and the args as a string. """ command = '' args = '' if tokens: command = tokens[0] if len(tokens) > 1: args = ' '.join(tokens[1:]) return command, args def split_on_punctuation(self, tokens: List[str]) -> List[str]: """Further splits tokens from a command line using punctuation characters. Punctuation characters are treated as word breaks when they are in unquoted strings. Each run of punctuation characters is treated as a single token. :param tokens: the tokens as parsed by shlex :return: a new list of tokens, further split using punctuation """ punctuation: List[str] = [] punctuation.extend(self.terminators) punctuation.extend(constants.REDIRECTION_CHARS) punctuated_tokens = [] for cur_initial_token in tokens: # Save tokens up to 1 character in length or quoted tokens. No need to parse these. if len(cur_initial_token) <= 1 or cur_initial_token[0] in constants.QUOTES: punctuated_tokens.append(cur_initial_token) continue # Iterate over each character in this token cur_index = 0 cur_char = cur_initial_token[cur_index] # Keep track of the token we are building new_token = '' while True: if cur_char not in punctuation: # Keep appending to new_token until we hit a punctuation char while cur_char not in punctuation: new_token += cur_char cur_index += 1 if cur_index < len(cur_initial_token): cur_char = cur_initial_token[cur_index] else: break else: cur_punc = cur_char # Keep appending to new_token until we hit something other than cur_punc while cur_char == cur_punc: new_token += cur_char cur_index += 1 if cur_index < len(cur_initial_token): cur_char = cur_initial_token[cur_index] else: break # Save the new token punctuated_tokens.append(new_token) new_token = '' # Check if we've viewed all characters if cur_index >= len(cur_initial_token): break return punctuated_tokens cmd2-2.3.3/cmd2/plugin.py000066400000000000000000000015171416142110700150420ustar00rootroot00000000000000# # coding=utf-8 """Classes for the cmd2 plugin system""" from typing import ( Optional, ) import attr from .parsing import ( Statement, ) @attr.s(auto_attribs=True) class PostparsingData: """Data class containing information passed to postparsing hook methods""" stop: bool statement: Statement @attr.s(auto_attribs=True) class PrecommandData: """Data class containing information passed to precommand hook methods""" statement: Statement @attr.s(auto_attribs=True) class PostcommandData: """Data class containing information passed to postcommand hook methods""" stop: bool statement: Statement @attr.s(auto_attribs=True) class CommandFinalizationData: """Data class containing information passed to command finalization hook methods""" stop: bool statement: Optional[Statement] cmd2-2.3.3/cmd2/py.typed000066400000000000000000000000121416142110700146560ustar00rootroot00000000000000# PEP 561 cmd2-2.3.3/cmd2/py_bridge.py000066400000000000000000000107751416142110700155160ustar00rootroot00000000000000# coding=utf-8 """ Bridges calls made inside of a Python environment to the Cmd2 host app while maintaining a reasonable degree of isolation between the two. """ import sys from contextlib import ( redirect_stderr, redirect_stdout, ) from typing import ( IO, TYPE_CHECKING, Any, List, NamedTuple, Optional, TextIO, Union, cast, ) from .utils import ( # namedtuple_with_defaults, StdSim, ) if TYPE_CHECKING: # pragma: no cover import cmd2 class CommandResult(NamedTuple): """Encapsulates the results from a cmd2 app command :stdout: str - output captured from stdout while this command is executing :stderr: str - output captured from stderr while this command is executing :stop: bool - return value of onecmd_plus_hooks after it runs the given command line. :data: possible data populated by the command. Any combination of these fields can be used when developing a scripting API for a given command. By default stdout, stderr, and stop will be captured for you. If there is additional command specific data, then write that to cmd2's last_result member. That becomes the data member of this tuple. In some cases, the data member may contain everything needed for a command and storing stdout and stderr might just be a duplication of data that wastes memory. In that case, the StdSim can be told not to store output with its pause_storage member. While this member is True, any output sent to StdSim won't be saved in its buffer. The code would look like this:: if isinstance(self.stdout, StdSim): self.stdout.pause_storage = True if isinstance(sys.stderr, StdSim): sys.stderr.pause_storage = True See :class:`~cmd2.utils.StdSim` for more information. .. note:: Named tuples are immutable. The contents are there for access, not for modification. """ stdout: str = '' stderr: str = '' stop: bool = False data: Any = None def __bool__(self) -> bool: """Returns True if the command succeeded, otherwise False""" # If data was set, then use it to determine success if self.data is not None: return bool(self.data) # Otherwise check if stderr was filled out else: return not self.stderr class PyBridge: """Provides a Python API wrapper for application commands.""" def __init__(self, cmd2_app: 'cmd2.Cmd') -> None: self._cmd2_app = cmd2_app self.cmd_echo = False # Tells if any of the commands run via __call__ returned True for stop self.stop = False def __dir__(self) -> List[str]: """Return a custom set of attribute names""" attributes: List[str] = [] attributes.insert(0, 'cmd_echo') return attributes def __call__(self, command: str, *, echo: Optional[bool] = None) -> CommandResult: """ Provide functionality to call application commands by calling PyBridge ex: app('help') :param command: command line being run :param echo: If provided, this temporarily overrides the value of self.cmd_echo while the command runs. If True, output will be echoed to stdout/stderr. (Defaults to None) """ if echo is None: echo = self.cmd_echo # This will be used to capture _cmd2_app.stdout and sys.stdout copy_cmd_stdout = StdSim(cast(Union[TextIO, StdSim], self._cmd2_app.stdout), echo=echo) # Pause the storing of stdout until onecmd_plus_hooks enables it copy_cmd_stdout.pause_storage = True # This will be used to capture sys.stderr copy_stderr = StdSim(sys.stderr, echo=echo) self._cmd2_app.last_result = None stop = False try: self._cmd2_app.stdout = cast(TextIO, copy_cmd_stdout) with redirect_stdout(cast(IO[str], copy_cmd_stdout)): with redirect_stderr(cast(IO[str], copy_stderr)): stop = self._cmd2_app.onecmd_plus_hooks(command, py_bridge_call=True) finally: with self._cmd2_app.sigint_protection: self._cmd2_app.stdout = cast(IO[str], copy_cmd_stdout.inner_stream) self.stop = stop or self.stop # Save the result result = CommandResult( stdout=copy_cmd_stdout.getvalue(), stderr=copy_stderr.getvalue(), stop=stop, data=self._cmd2_app.last_result, ) return result cmd2-2.3.3/cmd2/rl_utils.py000066400000000000000000000222071416142110700154000ustar00rootroot00000000000000# coding=utf-8 """ Imports the proper Readline for the platform and provides utility functions for it """ import sys from enum import ( Enum, ) from typing import ( Union, ) # Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit) try: # noinspection PyPackageRequirements import gnureadline as readline # type: ignore[import] except ImportError: # Try to import readline, but allow failure for convenience in Windows unit testing # Note: If this actually fails, you should install readline on Linux or Mac or pyreadline on Windows try: # noinspection PyUnresolvedReferences import readline # type: ignore[no-redef] except ImportError: # pragma: no cover pass class RlType(Enum): """Readline library types we recognize""" GNU = 1 PYREADLINE = 2 NONE = 3 # Check what implementation of Readline we are using rl_type = RlType.NONE # Tells if the terminal we are running in supports vt100 control characters vt100_support = False # Explanation for why Readline wasn't loaded _rl_warn_reason = '' # The order of this check matters since importing pyreadline/pyreadline3 will also show readline in the modules list if 'pyreadline' in sys.modules or 'pyreadline3' in sys.modules: rl_type = RlType.PYREADLINE import atexit from ctypes import ( byref, ) from ctypes.wintypes import ( DWORD, HANDLE, ) # Check if we are running in a terminal if sys.stdout.isatty(): # pragma: no cover # noinspection PyPep8Naming,PyUnresolvedReferences def enable_win_vt100(handle: HANDLE) -> bool: """ Enables VT100 character sequences in a Windows console This only works on Windows 10 and up :param handle: the handle on which to enable vt100 :return: True if vt100 characters are enabled for the handle """ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 # Get the current mode for this handle in the console cur_mode = DWORD(0) readline.rl.console.GetConsoleMode(handle, byref(cur_mode)) retVal = False # Check if ENABLE_VIRTUAL_TERMINAL_PROCESSING is already enabled if (cur_mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0: retVal = True elif readline.rl.console.SetConsoleMode(handle, cur_mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING): # Restore the original mode when we exit atexit.register(readline.rl.console.SetConsoleMode, handle, cur_mode) retVal = True return retVal # Enable VT100 sequences for stdout and stderr STD_OUT_HANDLE = -11 STD_ERROR_HANDLE = -12 # noinspection PyUnresolvedReferences vt100_stdout_support = enable_win_vt100(readline.rl.console.GetStdHandle(STD_OUT_HANDLE)) # noinspection PyUnresolvedReferences vt100_stderr_support = enable_win_vt100(readline.rl.console.GetStdHandle(STD_ERROR_HANDLE)) vt100_support = vt100_stdout_support and vt100_stderr_support ############################################################################################################ # pyreadline is incomplete in terms of the Python readline API. Add the missing functions we need. ############################################################################################################ # readline.redisplay() try: getattr(readline, 'redisplay') except AttributeError: # noinspection PyProtectedMember,PyUnresolvedReferences readline.redisplay = readline.rl.mode._update_line # readline.remove_history_item() try: getattr(readline, 'remove_history_item') except AttributeError: # noinspection PyProtectedMember,PyUnresolvedReferences def pyreadline_remove_history_item(pos: int) -> None: """ An implementation of remove_history_item() for pyreadline :param pos: The 0-based position in history to remove """ # Save of the current location of the history cursor saved_cursor = readline.rl.mode._history.history_cursor # Delete the history item del readline.rl.mode._history.history[pos] # Update the cursor if needed if saved_cursor > pos: readline.rl.mode._history.history_cursor -= 1 readline.remove_history_item = pyreadline_remove_history_item elif 'gnureadline' in sys.modules or 'readline' in sys.modules: # We don't support libedit if 'libedit' not in readline.__doc__: try: # Load the readline lib so we can access members of it import ctypes readline_lib = ctypes.CDLL(readline.__file__) except (AttributeError, OSError): # pragma: no cover _rl_warn_reason = ( "this application is running in a non-standard Python environment in\n" "which GNU readline is not loaded dynamically from a shared library file." ) else: rl_type = RlType.GNU vt100_support = sys.stdout.isatty() # Check if readline was loaded if rl_type == RlType.NONE: # pragma: no cover if not _rl_warn_reason: _rl_warn_reason = ( "no supported version of readline was found. To resolve this, install\n" "pyreadline on Windows or gnureadline on Mac." ) rl_warning = "Readline features including tab completion have been disabled because\n" + _rl_warn_reason + '\n\n' else: rl_warning = '' # noinspection PyProtectedMember,PyUnresolvedReferences def rl_force_redisplay() -> None: # pragma: no cover """ Causes readline to display the prompt and input text wherever the cursor is and start reading input from this location. This is the proper way to restore the input line after printing to the screen """ if not sys.stdout.isatty(): return if rl_type == RlType.GNU: readline_lib.rl_forced_update_display() # After manually updating the display, readline asks that rl_display_fixed be set to 1 for efficiency display_fixed = ctypes.c_int.in_dll(readline_lib, "rl_display_fixed") display_fixed.value = 1 elif rl_type == RlType.PYREADLINE: # Call _print_prompt() first to set the new location of the prompt readline.rl.mode._print_prompt() readline.rl.mode._update_line() # noinspection PyProtectedMember, PyUnresolvedReferences def rl_get_point() -> int: # pragma: no cover """ Returns the offset of the current cursor position in rl_line_buffer """ if rl_type == RlType.GNU: return ctypes.c_int.in_dll(readline_lib, "rl_point").value elif rl_type == RlType.PYREADLINE: return int(readline.rl.mode.l_buffer.point) else: return 0 # noinspection PyUnresolvedReferences def rl_get_prompt() -> str: # pragma: no cover """Gets Readline's current prompt""" if rl_type == RlType.GNU: encoded_prompt = ctypes.c_char_p.in_dll(readline_lib, "rl_prompt").value if encoded_prompt is None: prompt = '' else: prompt = encoded_prompt.decode(encoding='utf-8') elif rl_type == RlType.PYREADLINE: prompt_data: Union[str, bytes] = readline.rl.prompt if isinstance(prompt_data, bytes): prompt = prompt_data.decode(encoding='utf-8') else: prompt = prompt_data else: prompt = '' return rl_unescape_prompt(prompt) # noinspection PyUnresolvedReferences def rl_set_prompt(prompt: str) -> None: # pragma: no cover """ Sets Readline's prompt :param prompt: the new prompt value """ escaped_prompt = rl_escape_prompt(prompt) if rl_type == RlType.GNU: encoded_prompt = bytes(escaped_prompt, encoding='utf-8') readline_lib.rl_set_prompt(encoded_prompt) elif rl_type == RlType.PYREADLINE: readline.rl.prompt = escaped_prompt def rl_escape_prompt(prompt: str) -> str: """Overcome bug in GNU Readline in relation to calculation of prompt length in presence of ANSI escape codes :param prompt: original prompt :return: prompt safe to pass to GNU Readline """ if rl_type == RlType.GNU: # start code to tell GNU Readline about beginning of invisible characters escape_start = "\x01" # end code to tell GNU Readline about end of invisible characters escape_end = "\x02" escaped = False result = "" for c in prompt: if c == "\x1b" and not escaped: result += escape_start + c escaped = True elif c.isalpha() and escaped: result += c + escape_end escaped = False else: result += c return result else: return prompt def rl_unescape_prompt(prompt: str) -> str: """Remove escape characters from a Readline prompt""" if rl_type == RlType.GNU: escape_start = "\x01" escape_end = "\x02" prompt = prompt.replace(escape_start, "").replace(escape_end, "") return prompt cmd2-2.3.3/cmd2/table_creator.py000066400000000000000000001344651416142110700163630ustar00rootroot00000000000000# coding=utf-8 """ cmd2 table creation API This API is built upon two core classes: Column and TableCreator The general use case is to inherit from TableCreator to create a table class with custom formatting options. There are already implemented and ready-to-use examples of this below TableCreator's code. """ import copy import io from collections import ( deque, ) from enum import ( Enum, ) from typing import ( Any, Deque, List, Optional, Sequence, Tuple, Union, ) from wcwidth import ( # type: ignore[import] wcwidth, ) from . import ( ansi, constants, utils, ) # Constants EMPTY = '' SPACE = ' ' class HorizontalAlignment(Enum): """Horizontal alignment of text in a cell""" LEFT = 1 CENTER = 2 RIGHT = 3 class VerticalAlignment(Enum): """Vertical alignment of text in a cell""" TOP = 1 MIDDLE = 2 BOTTOM = 3 class Column: """Table column configuration""" def __init__( self, header: str, *, width: Optional[int] = None, header_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT, header_vert_align: VerticalAlignment = VerticalAlignment.BOTTOM, style_header_text: bool = True, data_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT, data_vert_align: VerticalAlignment = VerticalAlignment.TOP, style_data_text: bool = True, max_data_lines: Union[int, float] = constants.INFINITY, ) -> None: """ Column initializer :param header: label for column header :param width: display width of column. This does not account for any borders or padding which may be added (e.g pre_line, inter_cell, and post_line). Header and data text wrap within this width using word-based wrapping (defaults to actual width of header or 1 if header is blank) :param header_horiz_align: horizontal alignment of header cells (defaults to left) :param header_vert_align: vertical alignment of header cells (defaults to bottom) :param style_header_text: if True, then the table is allowed to apply styles to the header text, which may conflict with any styles the header already has. If False, the header is printed as is. Table classes which apply style to headers must account for the value of this flag. (defaults to True) :param data_horiz_align: horizontal alignment of data cells (defaults to left) :param data_vert_align: vertical alignment of data cells (defaults to top) :param style_data_text: if True, then the table is allowed to apply styles to the data text, which may conflict with any styles the data already has. If False, the data is printed as is. Table classes which apply style to data must account for the value of this flag. (defaults to True) :param max_data_lines: maximum lines allowed in a data cell. If line count exceeds this, then the final line displayed will be truncated with an ellipsis. (defaults to INFINITY) :raises: ValueError if width is less than 1 :raises: ValueError if max_data_lines is less than 1 """ self.header = header if width is not None and width < 1: raise ValueError("Column width cannot be less than 1") else: self.width: int = width if width is not None else -1 self.header_horiz_align = header_horiz_align self.header_vert_align = header_vert_align self.style_header_text = style_header_text self.data_horiz_align = data_horiz_align self.data_vert_align = data_vert_align self.style_data_text = style_data_text if max_data_lines < 1: raise ValueError("Max data lines cannot be less than 1") self.max_data_lines = max_data_lines class TableCreator: """ Base table creation class. This class handles ANSI style sequences and characters with display widths greater than 1 when performing width calculations. It was designed with the ability to build tables one row at a time. This helps when you have large data sets that you don't want to hold in memory or when you receive portions of the data set incrementally. TableCreator has one public method: generate_row() This function and the Column class provide all features needed to build tables with headers, borders, colors, horizontal and vertical alignment, and wrapped text. However, it's generally easier to inherit from this class and implement a more granular API rather than use TableCreator directly. There are ready-to-use examples of this defined after this class. """ def __init__(self, cols: Sequence[Column], *, tab_width: int = 4) -> None: """ TableCreator initializer :param cols: column definitions for this table :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space. :raises: ValueError if tab_width is less than 1 """ if tab_width < 1: raise ValueError("Tab width cannot be less than 1") self.cols = copy.copy(cols) self.tab_width = tab_width for col in self.cols: # Replace tabs before calculating width of header strings col.header = col.header.replace('\t', SPACE * self.tab_width) # For headers with the width not yet set, use the width of the # widest line in the header or 1 if the header has no width if col.width <= 0: col.width = max(1, ansi.widest_line(col.header)) @staticmethod def _wrap_long_word(word: str, max_width: int, max_lines: Union[int, float], is_last_word: bool) -> Tuple[str, int, int]: """ Used by _wrap_text() to wrap a long word over multiple lines :param word: word being wrapped :param max_width: maximum display width of a line :param max_lines: maximum lines to wrap before ending the last line displayed with an ellipsis :param is_last_word: True if this is the last word of the total text being wrapped :return: Tuple(wrapped text, lines used, display width of last line) """ styles = utils.get_styles_in_text(word) wrapped_buf = io.StringIO() # How many lines we've used total_lines = 1 # Display width of the current line we are building cur_line_width = 0 char_index = 0 while char_index < len(word): # We've reached the last line. Let truncate_line do the rest. if total_lines == max_lines: # If this isn't the last word, but it's gonna fill the final line, then force truncate_line # to place an ellipsis at the end of it by making the word too wide. remaining_word = word[char_index:] if not is_last_word and ansi.style_aware_wcswidth(remaining_word) == max_width: remaining_word += "EXTRA" truncated_line = utils.truncate_line(remaining_word, max_width) cur_line_width = ansi.style_aware_wcswidth(truncated_line) wrapped_buf.write(truncated_line) break # Check if we're at a style sequence. These don't count toward display width. if char_index in styles: wrapped_buf.write(styles[char_index]) char_index += len(styles[char_index]) continue cur_char = word[char_index] cur_char_width = wcwidth(cur_char) if cur_char_width > max_width: # We have a case where the character is wider than max_width. This can happen if max_width # is 1 and the text contains wide characters (e.g. East Asian). Replace it with an ellipsis. cur_char = constants.HORIZONTAL_ELLIPSIS cur_char_width = wcwidth(cur_char) if cur_line_width + cur_char_width > max_width: # Adding this char will exceed the max_width. Start a new line. wrapped_buf.write('\n') total_lines += 1 cur_line_width = 0 continue # Add this character and move to the next one cur_line_width += cur_char_width wrapped_buf.write(cur_char) char_index += 1 return wrapped_buf.getvalue(), total_lines, cur_line_width @staticmethod def _wrap_text(text: str, max_width: int, max_lines: Union[int, float]) -> str: """ Wrap text into lines with a display width no longer than max_width. This function breaks words on whitespace boundaries. If a word is longer than the space remaining on a line, then it will start on a new line. ANSI escape sequences do not count toward the width of a line. :param text: text to be wrapped :param max_width: maximum display width of a line :param max_lines: maximum lines to wrap before ending the last line displayed with an ellipsis :return: wrapped text """ # MyPy Issue #7057 documents regression requiring nonlocals to be defined earlier cur_line_width = 0 total_lines = 0 def add_word(word_to_add: str, is_last_word: bool) -> None: """ Called from loop to add a word to the wrapped text :param word_to_add: the word being added :param is_last_word: True if this is the last word of the total text being wrapped """ nonlocal cur_line_width nonlocal total_lines # No more space to add word if total_lines == max_lines and cur_line_width == max_width: return word_width = ansi.style_aware_wcswidth(word_to_add) # If the word is wider than max width of a line, attempt to start it on its own line and wrap it if word_width > max_width: room_to_add = True if cur_line_width > 0: # The current line already has text, check if there is room to create a new line if total_lines < max_lines: wrapped_buf.write('\n') total_lines += 1 else: # We will truncate this word on the remaining line room_to_add = False if room_to_add: wrapped_word, lines_used, cur_line_width = TableCreator._wrap_long_word( word_to_add, max_width, max_lines - total_lines + 1, is_last_word ) # Write the word to the buffer wrapped_buf.write(wrapped_word) total_lines += lines_used - 1 return # We aren't going to wrap the word across multiple lines remaining_width = max_width - cur_line_width # Check if we need to start a new line if word_width > remaining_width and total_lines < max_lines: # Save the last character in wrapped_buf, which can't be empty at this point. seek_pos = wrapped_buf.tell() - 1 wrapped_buf.seek(seek_pos) last_char = wrapped_buf.read() wrapped_buf.write('\n') total_lines += 1 cur_line_width = 0 remaining_width = max_width # Only when a space is following a space do we want to start the next line with it. if word_to_add == SPACE and last_char != SPACE: return # Check if we've hit the last line we're allowed to create if total_lines == max_lines: # If this word won't fit, truncate it if word_width > remaining_width: word_to_add = utils.truncate_line(word_to_add, remaining_width) word_width = remaining_width # If this isn't the last word, but it's gonna fill the final line, then force truncate_line # to place an ellipsis at the end of it by making the word too wide. elif not is_last_word and word_width == remaining_width: word_to_add = utils.truncate_line(word_to_add + "EXTRA", remaining_width) cur_line_width += word_width wrapped_buf.write(word_to_add) ############################################################################################################ # _wrap_text() main code ############################################################################################################ # Buffer of the wrapped text wrapped_buf = io.StringIO() # How many lines we've used total_lines = 0 # Respect the existing line breaks data_str_lines = text.splitlines() for data_line_index, data_line in enumerate(data_str_lines): total_lines += 1 if data_line_index > 0: wrapped_buf.write('\n') # If the last line is empty, then add a newline and stop if data_line_index == len(data_str_lines) - 1 and not data_line: wrapped_buf.write('\n') break # Locate the styles in this line styles = utils.get_styles_in_text(data_line) # Display width of the current line we are building cur_line_width = 0 # Current word being built cur_word_buf = io.StringIO() char_index = 0 while char_index < len(data_line): if total_lines == max_lines and cur_line_width == max_width: break # Check if we're at a style sequence. These don't count toward display width. if char_index in styles: cur_word_buf.write(styles[char_index]) char_index += len(styles[char_index]) continue cur_char = data_line[char_index] if cur_char == SPACE: # If we've reached the end of a word, then add the word to the wrapped text if cur_word_buf.tell() > 0: # is_last_word is False since there is a space after the word add_word(cur_word_buf.getvalue(), is_last_word=False) cur_word_buf = io.StringIO() # Add the space to the wrapped text last_word = data_line_index == len(data_str_lines) - 1 and char_index == len(data_line) - 1 add_word(cur_char, last_word) else: # Add this character to the word buffer cur_word_buf.write(cur_char) char_index += 1 # Add the final word of this line if it's been started if cur_word_buf.tell() > 0: last_word = data_line_index == len(data_str_lines) - 1 and char_index == len(data_line) add_word(cur_word_buf.getvalue(), last_word) # Stop line loop if we've written to max_lines if total_lines == max_lines: # If this isn't the last data line and there is space # left on the final wrapped line, then add an ellipsis if data_line_index < len(data_str_lines) - 1 and cur_line_width < max_width: wrapped_buf.write(constants.HORIZONTAL_ELLIPSIS) break return wrapped_buf.getvalue() def _generate_cell_lines(self, cell_data: Any, is_header: bool, col: Column, fill_char: str) -> Tuple[Deque[str], int]: """ Generate the lines of a table cell :param cell_data: data to be included in cell :param is_header: True if writing a header cell, otherwise writing a data cell. This determines whether to use header or data alignment settings as well as maximum lines to wrap. :param col: Column definition for this cell :param fill_char: character that fills remaining space in a cell. If your text has a background color, then give fill_char the same background color. (Cannot be a line breaking character) :return: Tuple of cell lines deque and the display width of the cell """ # Convert data to string and replace tabs with spaces data_str = str(cell_data).replace('\t', SPACE * self.tab_width) # Wrap text in this cell max_lines = constants.INFINITY if is_header else col.max_data_lines wrapped_text = self._wrap_text(data_str, col.width, max_lines) # Align the text horizontally horiz_alignment = col.header_horiz_align if is_header else col.data_horiz_align if horiz_alignment == HorizontalAlignment.LEFT: text_alignment = utils.TextAlignment.LEFT elif horiz_alignment == HorizontalAlignment.CENTER: text_alignment = utils.TextAlignment.CENTER else: text_alignment = utils.TextAlignment.RIGHT aligned_text = utils.align_text(wrapped_text, fill_char=fill_char, width=col.width, alignment=text_alignment) lines = deque(aligned_text.splitlines()) cell_width = ansi.widest_line(aligned_text) return lines, cell_width def generate_row( self, row_data: Sequence[Any], is_header: bool, *, fill_char: str = SPACE, pre_line: str = EMPTY, inter_cell: str = (2 * SPACE), post_line: str = EMPTY, ) -> str: """ Generate a header or data table row :param row_data: data with an entry for each column in the row :param is_header: True if writing a header cell, otherwise writing a data cell. This determines whether to use header or data alignment settings as well as maximum lines to wrap. :param fill_char: character that fills remaining space in a cell. Defaults to space. If this is a tab, then it will be converted to one space. (Cannot be a line breaking character) :param pre_line: string to print before each line of a row. This can be used for a left row border and padding before the first cell's text. (Defaults to blank) :param inter_cell: string to print where two cells meet. This can be used for a border between cells and padding between it and the 2 cells' text. (Defaults to 2 spaces) :param post_line: string to print after each line of a row. This can be used for padding after the last cell's text and a right row border. (Defaults to blank) :return: row string :raises: ValueError if row_data isn't the same length as self.cols :raises: TypeError if fill_char is more than one character (not including ANSI style sequences) :raises: ValueError if fill_char, pre_line, inter_cell, or post_line contains an unprintable character like a newline """ class Cell: """Inner class which represents a table cell""" def __init__(self) -> None: # Data in this cell split into individual lines self.lines: Deque[str] = deque() # Display width of this cell self.width = 0 if len(row_data) != len(self.cols): raise ValueError("Length of row_data must match length of cols") # Replace tabs (tabs in data strings will be handled in _generate_cell_lines()) fill_char = fill_char.replace('\t', SPACE) pre_line = pre_line.replace('\t', SPACE * self.tab_width) inter_cell = inter_cell.replace('\t', SPACE * self.tab_width) post_line = post_line.replace('\t', SPACE * self.tab_width) # Validate fill_char character count if len(ansi.strip_style(fill_char)) != 1: raise TypeError("Fill character must be exactly one character long") # Look for unprintable characters validation_dict = {'fill_char': fill_char, 'pre_line': pre_line, 'inter_cell': inter_cell, 'post_line': post_line} for key, val in validation_dict.items(): if ansi.style_aware_wcswidth(val) == -1: raise ValueError(f"{key} contains an unprintable character") # Number of lines this row uses total_lines = 0 # Generate the cells for this row cells = list() for col_index, col in enumerate(self.cols): cell = Cell() cell.lines, cell.width = self._generate_cell_lines(row_data[col_index], is_header, col, fill_char) cells.append(cell) total_lines = max(len(cell.lines), total_lines) row_buf = io.StringIO() # Vertically align each cell for cell_index, cell in enumerate(cells): col = self.cols[cell_index] vert_align = col.header_vert_align if is_header else col.data_vert_align # Check if this cell need vertical filler line_diff = total_lines - len(cell.lines) if line_diff == 0: continue # Add vertical filler lines padding_line = utils.align_left(EMPTY, fill_char=fill_char, width=cell.width) if vert_align == VerticalAlignment.TOP: to_top = 0 to_bottom = line_diff elif vert_align == VerticalAlignment.MIDDLE: to_top = line_diff // 2 to_bottom = line_diff - to_top else: to_top = line_diff to_bottom = 0 for i in range(to_top): cell.lines.appendleft(padding_line) for i in range(to_bottom): cell.lines.append(padding_line) # Build this row one line at a time for line_index in range(total_lines): for cell_index, cell in enumerate(cells): if cell_index == 0: row_buf.write(pre_line) row_buf.write(cell.lines[line_index]) if cell_index < len(self.cols) - 1: row_buf.write(inter_cell) if cell_index == len(self.cols) - 1: row_buf.write(post_line) # Add a newline if this is not the last line if line_index < total_lines - 1: row_buf.write('\n') return row_buf.getvalue() ############################################################################################################ # The following are implementations of TableCreator which demonstrate how to make various types # of tables. They can be used as-is or serve as inspiration for other custom table classes. ############################################################################################################ class SimpleTable(TableCreator): """ Implementation of TableCreator which generates a borderless table with an optional divider row after the header. This class can be used to create the whole table at once or one row at a time. """ def __init__( self, cols: Sequence[Column], *, column_spacing: int = 2, tab_width: int = 4, divider_char: Optional[str] = '-', header_bg: Optional[ansi.BgColor] = None, data_bg: Optional[ansi.BgColor] = None, ) -> None: """ SimpleTable initializer :param cols: column definitions for this table :param column_spacing: how many spaces to place between columns. Defaults to 2. :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space. :param divider_char: optional character used to build the header divider row. Set this to blank or None if you don't want a divider row. Defaults to dash. (Cannot be a line breaking character) :param header_bg: optional background color for header cells (defaults to None) :param data_bg: optional background color for data cells (defaults to None) :raises: ValueError if tab_width is less than 1 :raises: ValueError if column_spacing is less than 0 :raises: TypeError if divider_char is longer than one character :raises: ValueError if divider_char is an unprintable character """ super().__init__(cols, tab_width=tab_width) if column_spacing < 0: raise ValueError("Column spacing cannot be less than 0") self.column_spacing = column_spacing if divider_char == '': divider_char = None if divider_char is not None: if len(ansi.strip_style(divider_char)) != 1: raise TypeError("Divider character must be exactly one character long") divider_char_width = ansi.style_aware_wcswidth(divider_char) if divider_char_width == -1: raise ValueError("Divider character is an unprintable character") self.divider_char = divider_char self.header_bg = header_bg self.data_bg = data_bg def apply_header_bg(self, value: Any) -> str: """ If defined, apply the header background color to header text :param value: object whose text is to be colored :return: formatted text """ if self.header_bg is None: return str(value) return ansi.style(value, bg=self.header_bg) def apply_data_bg(self, value: Any) -> str: """ If defined, apply the data background color to data text :param value: object whose text is to be colored :return: formatted data string """ if self.data_bg is None: return str(value) return ansi.style(value, bg=self.data_bg) @classmethod def base_width(cls, num_cols: int, *, column_spacing: int = 2) -> int: """ Utility method to calculate the display width required for a table before data is added to it. This is useful when determining how wide to make your columns to have a table be a specific width. :param num_cols: how many columns the table will have :param column_spacing: how many spaces to place between columns. Defaults to 2. :return: base width :raises: ValueError if column_spacing is less than 0 :raises: ValueError if num_cols is less than 1 """ if num_cols < 1: raise ValueError("Column count cannot be less than 1") data_str = SPACE data_width = ansi.style_aware_wcswidth(data_str) * num_cols tbl = cls([Column(data_str)] * num_cols, column_spacing=column_spacing) data_row = tbl.generate_data_row([data_str] * num_cols) return ansi.style_aware_wcswidth(data_row) - data_width def total_width(self) -> int: """Calculate the total display width of this table""" base_width = self.base_width(len(self.cols), column_spacing=self.column_spacing) data_width = sum(col.width for col in self.cols) return base_width + data_width def generate_header(self) -> str: """Generate table header with an optional divider row""" header_buf = io.StringIO() fill_char = self.apply_header_bg(SPACE) inter_cell = self.apply_header_bg(self.column_spacing * SPACE) # Apply background color to header text in Columns which allow it to_display: List[Any] = [] for col in self.cols: if col.style_header_text: to_display.append(self.apply_header_bg(col.header)) else: to_display.append(col.header) # Create the header labels header_labels = self.generate_row(to_display, is_header=True, fill_char=fill_char, inter_cell=inter_cell) header_buf.write(header_labels) # Add the divider if necessary divider = self.generate_divider() if divider: header_buf.write('\n' + divider) return header_buf.getvalue() def generate_divider(self) -> str: """Generate divider row""" if self.divider_char is None: return '' return utils.align_left('', fill_char=self.divider_char, width=self.total_width()) def generate_data_row(self, row_data: Sequence[Any]) -> str: """ Generate a data row :param row_data: data with an entry for each column in the row :return: data row string :raises: ValueError if row_data isn't the same length as self.cols """ if len(row_data) != len(self.cols): raise ValueError("Length of row_data must match length of cols") fill_char = self.apply_data_bg(SPACE) inter_cell = self.apply_data_bg(self.column_spacing * SPACE) # Apply background color to data text in Columns which allow it to_display: List[Any] = [] for index, col in enumerate(self.cols): if col.style_data_text: to_display.append(self.apply_data_bg(row_data[index])) else: to_display.append(row_data[index]) return self.generate_row(to_display, is_header=False, fill_char=fill_char, inter_cell=inter_cell) def generate_table(self, table_data: Sequence[Sequence[Any]], *, include_header: bool = True, row_spacing: int = 1) -> str: """ Generate a table from a data set :param table_data: Data with an entry for each data row of the table. Each entry should have data for each column in the row. :param include_header: If True, then a header will be included at top of table. (Defaults to True) :param row_spacing: A number 0 or greater specifying how many blank lines to place between each row (Defaults to 1) :raises: ValueError if row_spacing is less than 0 """ if row_spacing < 0: raise ValueError("Row spacing cannot be less than 0") table_buf = io.StringIO() if include_header: header = self.generate_header() table_buf.write(header) if len(table_data) > 0: table_buf.write('\n') row_divider = utils.align_left('', fill_char=self.apply_data_bg(SPACE), width=self.total_width()) + '\n' for index, row_data in enumerate(table_data): if index > 0 and row_spacing > 0: table_buf.write(row_spacing * row_divider) row = self.generate_data_row(row_data) table_buf.write(row) if index < len(table_data) - 1: table_buf.write('\n') return table_buf.getvalue() class BorderedTable(TableCreator): """ Implementation of TableCreator which generates a table with borders around the table and between rows. Borders between columns can also be toggled. This class can be used to create the whole table at once or one row at a time. """ def __init__( self, cols: Sequence[Column], *, tab_width: int = 4, column_borders: bool = True, padding: int = 1, border_fg: Optional[ansi.FgColor] = None, border_bg: Optional[ansi.BgColor] = None, header_bg: Optional[ansi.BgColor] = None, data_bg: Optional[ansi.BgColor] = None, ) -> None: """ BorderedTable initializer :param cols: column definitions for this table :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space. :param column_borders: if True, borders between columns will be included. This gives the table a grid-like appearance. Turning off column borders results in a unified appearance between a row's cells. (Defaults to True) :param padding: number of spaces between text and left/right borders of cell :param border_fg: optional foreground color for borders (defaults to None) :param border_bg: optional background color for borders (defaults to None) :param header_bg: optional background color for header cells (defaults to None) :param data_bg: optional background color for data cells (defaults to None) :raises: ValueError if tab_width is less than 1 :raises: ValueError if padding is less than 0 """ super().__init__(cols, tab_width=tab_width) self.empty_data = [EMPTY] * len(self.cols) self.column_borders = column_borders if padding < 0: raise ValueError("Padding cannot be less than 0") self.padding = padding self.border_fg = border_fg self.border_bg = border_bg self.header_bg = header_bg self.data_bg = data_bg def apply_border_color(self, value: Any) -> str: """ If defined, apply the border foreground and background colors :param value: object whose text is to be colored :return: formatted text """ if self.border_fg is None and self.border_bg is None: return str(value) return ansi.style(value, fg=self.border_fg, bg=self.border_bg) def apply_header_bg(self, value: Any) -> str: """ If defined, apply the header background color to header text :param value: object whose text is to be colored :return: formatted text """ if self.header_bg is None: return str(value) return ansi.style(value, bg=self.header_bg) def apply_data_bg(self, value: Any) -> str: """ If defined, apply the data background color to data text :param value: object whose text is to be colored :return: formatted data string """ if self.data_bg is None: return str(value) return ansi.style(value, bg=self.data_bg) @classmethod def base_width(cls, num_cols: int, *, column_borders: bool = True, padding: int = 1) -> int: """ Utility method to calculate the display width required for a table before data is added to it. This is useful when determining how wide to make your columns to have a table be a specific width. :param num_cols: how many columns the table will have :param column_borders: if True, borders between columns will be included in the calculation (Defaults to True) :param padding: number of spaces between text and left/right borders of cell :return: base width :raises: ValueError if num_cols is less than 1 """ if num_cols < 1: raise ValueError("Column count cannot be less than 1") data_str = SPACE data_width = ansi.style_aware_wcswidth(data_str) * num_cols tbl = cls([Column(data_str)] * num_cols, column_borders=column_borders, padding=padding) data_row = tbl.generate_data_row([data_str] * num_cols) return ansi.style_aware_wcswidth(data_row) - data_width def total_width(self) -> int: """Calculate the total display width of this table""" base_width = self.base_width(len(self.cols), column_borders=self.column_borders, padding=self.padding) data_width = sum(col.width for col in self.cols) return base_width + data_width def generate_table_top_border(self) -> str: """Generate a border which appears at the top of the header and data section""" fill_char = 'â•' pre_line = 'â•”' + self.padding * 'â•' inter_cell = self.padding * 'â•' if self.column_borders: inter_cell += "╤" inter_cell += self.padding * 'â•' post_line = self.padding * 'â•' + 'â•—' return self.generate_row( self.empty_data, is_header=False, fill_char=self.apply_border_color(fill_char), pre_line=self.apply_border_color(pre_line), inter_cell=self.apply_border_color(inter_cell), post_line=self.apply_border_color(post_line), ) def generate_header_bottom_border(self) -> str: """Generate a border which appears at the bottom of the header""" fill_char = 'â•' pre_line = 'â• ' + self.padding * 'â•' inter_cell = self.padding * 'â•' if self.column_borders: inter_cell += '╪' inter_cell += self.padding * 'â•' post_line = self.padding * 'â•' + 'â•£' return self.generate_row( self.empty_data, is_header=False, fill_char=self.apply_border_color(fill_char), pre_line=self.apply_border_color(pre_line), inter_cell=self.apply_border_color(inter_cell), post_line=self.apply_border_color(post_line), ) def generate_row_bottom_border(self) -> str: """Generate a border which appears at the bottom of rows""" fill_char = '─' pre_line = '╟' + self.padding * '─' inter_cell = self.padding * '─' if self.column_borders: inter_cell += '┼' inter_cell += self.padding * '─' inter_cell = inter_cell post_line = self.padding * '─' + 'â•¢' return self.generate_row( self.empty_data, is_header=False, fill_char=self.apply_border_color(fill_char), pre_line=self.apply_border_color(pre_line), inter_cell=self.apply_border_color(inter_cell), post_line=self.apply_border_color(post_line), ) def generate_table_bottom_border(self) -> str: """Generate a border which appears at the bottom of the table""" fill_char = 'â•' pre_line = '╚' + self.padding * 'â•' inter_cell = self.padding * 'â•' if self.column_borders: inter_cell += 'â•§' inter_cell += self.padding * 'â•' post_line = self.padding * 'â•' + 'â•' return self.generate_row( self.empty_data, is_header=False, fill_char=self.apply_border_color(fill_char), pre_line=self.apply_border_color(pre_line), inter_cell=self.apply_border_color(inter_cell), post_line=self.apply_border_color(post_line), ) def generate_header(self) -> str: """Generate table header""" fill_char = self.apply_header_bg(SPACE) pre_line = self.apply_border_color('â•‘') + self.apply_header_bg(self.padding * SPACE) inter_cell = self.apply_header_bg(self.padding * SPACE) if self.column_borders: inter_cell += self.apply_border_color('│') inter_cell += self.apply_header_bg(self.padding * SPACE) post_line = self.apply_header_bg(self.padding * SPACE) + self.apply_border_color('â•‘') # Apply background color to header text in Columns which allow it to_display: List[Any] = [] for col in self.cols: if col.style_header_text: to_display.append(self.apply_header_bg(col.header)) else: to_display.append(col.header) # Create the bordered header header_buf = io.StringIO() header_buf.write(self.generate_table_top_border()) header_buf.write('\n') header_buf.write( self.generate_row( to_display, is_header=True, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line ) ) header_buf.write('\n') header_buf.write(self.generate_header_bottom_border()) return header_buf.getvalue() def generate_data_row(self, row_data: Sequence[Any]) -> str: """ Generate a data row :param row_data: data with an entry for each column in the row :return: data row string :raises: ValueError if row_data isn't the same length as self.cols """ if len(row_data) != len(self.cols): raise ValueError("Length of row_data must match length of cols") fill_char = self.apply_data_bg(SPACE) pre_line = self.apply_border_color('â•‘') + self.apply_data_bg(self.padding * SPACE) inter_cell = self.apply_data_bg(self.padding * SPACE) if self.column_borders: inter_cell += self.apply_border_color('│') inter_cell += self.apply_data_bg(self.padding * SPACE) post_line = self.apply_data_bg(self.padding * SPACE) + self.apply_border_color('â•‘') # Apply background color to data text in Columns which allow it to_display: List[Any] = [] for index, col in enumerate(self.cols): if col.style_data_text: to_display.append(self.apply_data_bg(row_data[index])) else: to_display.append(row_data[index]) return self.generate_row( to_display, is_header=False, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line ) def generate_table(self, table_data: Sequence[Sequence[Any]], *, include_header: bool = True) -> str: """ Generate a table from a data set :param table_data: Data with an entry for each data row of the table. Each entry should have data for each column in the row. :param include_header: If True, then a header will be included at top of table. (Defaults to True) """ table_buf = io.StringIO() if include_header: header = self.generate_header() table_buf.write(header) else: top_border = self.generate_table_top_border() table_buf.write(top_border) table_buf.write('\n') for index, row_data in enumerate(table_data): if index > 0: row_bottom_border = self.generate_row_bottom_border() table_buf.write(row_bottom_border) table_buf.write('\n') row = self.generate_data_row(row_data) table_buf.write(row) table_buf.write('\n') table_buf.write(self.generate_table_bottom_border()) return table_buf.getvalue() class AlternatingTable(BorderedTable): """ Implementation of BorderedTable which uses background colors to distinguish between rows instead of row border lines. This class can be used to create the whole table at once or one row at a time. To nest an AlternatingTable within another AlternatingTable, set style_data_text to False on the Column which contains the nested table. That will prevent the current row's background color from affecting the colors of the nested table. """ def __init__( self, cols: Sequence[Column], *, tab_width: int = 4, column_borders: bool = True, padding: int = 1, border_fg: Optional[ansi.FgColor] = None, border_bg: Optional[ansi.BgColor] = None, header_bg: Optional[ansi.BgColor] = None, odd_bg: Optional[ansi.BgColor] = None, even_bg: Optional[ansi.BgColor] = ansi.Bg.DARK_GRAY, ) -> None: """ AlternatingTable initializer Note: Specify background colors using subclasses of BgColor (e.g. Bg, EightBitBg, RgbBg) :param cols: column definitions for this table :param tab_width: all tabs will be replaced with this many spaces. If a row's fill_char is a tab, then it will be converted to one space. :param column_borders: if True, borders between columns will be included. This gives the table a grid-like appearance. Turning off column borders results in a unified appearance between a row's cells. (Defaults to True) :param padding: number of spaces between text and left/right borders of cell :param border_fg: optional foreground color for borders (defaults to None) :param border_bg: optional background color for borders (defaults to None) :param header_bg: optional background color for header cells (defaults to None) :param odd_bg: optional background color for odd numbered data rows (defaults to None) :param even_bg: optional background color for even numbered data rows (defaults to StdBg.DARK_GRAY) :raises: ValueError if tab_width is less than 1 :raises: ValueError if padding is less than 0 """ super().__init__( cols, tab_width=tab_width, column_borders=column_borders, padding=padding, border_fg=border_fg, border_bg=border_bg, header_bg=header_bg, ) self.row_num = 1 self.odd_bg = odd_bg self.even_bg = even_bg def apply_data_bg(self, value: Any) -> str: """ Apply background color to data text based on what row is being generated and whether a color has been defined :param value: object whose text is to be colored :return: formatted data string """ if self.row_num % 2 == 0 and self.even_bg is not None: return ansi.style(value, bg=self.even_bg) elif self.row_num % 2 != 0 and self.odd_bg is not None: return ansi.style(value, bg=self.odd_bg) else: return str(value) def generate_data_row(self, row_data: Sequence[Any]) -> str: """ Generate a data row :param row_data: data with an entry for each column in the row :return: data row string """ row = super().generate_data_row(row_data) self.row_num += 1 return row def generate_table(self, table_data: Sequence[Sequence[Any]], *, include_header: bool = True) -> str: """ Generate a table from a data set :param table_data: Data with an entry for each data row of the table. Each entry should have data for each column in the row. :param include_header: If True, then a header will be included at top of table. (Defaults to True) """ table_buf = io.StringIO() if include_header: header = self.generate_header() table_buf.write(header) else: top_border = self.generate_table_top_border() table_buf.write(top_border) table_buf.write('\n') for row_data in table_data: row = self.generate_data_row(row_data) table_buf.write(row) table_buf.write('\n') table_buf.write(self.generate_table_bottom_border()) return table_buf.getvalue() cmd2-2.3.3/cmd2/transcript.py000066400000000000000000000217401416142110700157350ustar00rootroot00000000000000# # -*- coding: utf-8 -*- """Machinery for running and validating transcripts. If the user wants to run a transcript (see docs/transcript.rst), we need a mechanism to run each command in the transcript as a unit test, comparing the expected output to the actual output. This file contains the class necessary to make that work. This class is used in cmd2.py::run_transcript_tests() """ import re import unittest from typing import ( TYPE_CHECKING, Iterator, List, Optional, TextIO, Tuple, cast, ) from . import ( ansi, utils, ) if TYPE_CHECKING: # pragma: no cover from cmd2 import ( Cmd, ) class Cmd2TestCase(unittest.TestCase): """A unittest class used for transcript testing. Subclass this, setting CmdApp, to make a unittest.TestCase class that will execute the commands in a transcript file and expect the results shown. See example.py """ cmdapp: Optional['Cmd'] = None def setUp(self) -> None: if self.cmdapp: self._fetchTranscripts() # Trap stdout self._orig_stdout = self.cmdapp.stdout self.cmdapp.stdout = cast(TextIO, utils.StdSim(cast(TextIO, self.cmdapp.stdout))) def tearDown(self) -> None: if self.cmdapp: # Restore stdout self.cmdapp.stdout = self._orig_stdout def runTest(self) -> None: # was testall if self.cmdapp: its = sorted(self.transcripts.items()) for (fname, transcript) in its: self._test_transcript(fname, transcript) def _fetchTranscripts(self) -> None: self.transcripts = {} testfiles = cast(List[str], getattr(self.cmdapp, 'testfiles', [])) for fname in testfiles: tfile = open(fname) self.transcripts[fname] = iter(tfile.readlines()) tfile.close() def _test_transcript(self, fname: str, transcript: Iterator[str]) -> None: if self.cmdapp is None: return line_num = 0 finished = False line = ansi.strip_style(next(transcript)) line_num += 1 while not finished: # Scroll forward to where actual commands begin while not line.startswith(self.cmdapp.visible_prompt): try: line = ansi.strip_style(next(transcript)) except StopIteration: finished = True break line_num += 1 command_parts = [line[len(self.cmdapp.visible_prompt) :]] try: line = next(transcript) except StopIteration: line = '' line_num += 1 # Read the entirety of a multi-line command while line.startswith(self.cmdapp.continuation_prompt): command_parts.append(line[len(self.cmdapp.continuation_prompt) :]) try: line = next(transcript) except StopIteration as exc: msg = f'Transcript broke off while reading command beginning at line {line_num} with\n{command_parts[0]}' raise StopIteration(msg) from exc line_num += 1 command = ''.join(command_parts) # Send the command into the application and capture the resulting output stop = self.cmdapp.onecmd_plus_hooks(command) result = self.cmdapp.stdout.read() stop_msg = 'Command indicated application should quit, but more commands in transcript' # Read the expected result from transcript if ansi.strip_style(line).startswith(self.cmdapp.visible_prompt): message = f'\nFile {fname}, line {line_num}\nCommand was:\n{command}\nExpected: (nothing)\nGot:\n{result}\n' self.assertTrue(not (result.strip()), message) # If the command signaled the application to quit there should be no more commands self.assertFalse(stop, stop_msg) continue expected_parts = [] while not ansi.strip_style(line).startswith(self.cmdapp.visible_prompt): expected_parts.append(line) try: line = next(transcript) except StopIteration: finished = True break line_num += 1 if stop: # This should only be hit if the command that set stop to True had output text self.assertTrue(finished, stop_msg) # transform the expected text into a valid regular expression expected = ''.join(expected_parts) expected = self._transform_transcript_expected(expected) message = f'\nFile {fname}, line {line_num}\nCommand was:\n{command}\nExpected:\n{expected}\nGot:\n{result}\n' self.assertTrue(re.match(expected, result, re.MULTILINE | re.DOTALL), message) def _transform_transcript_expected(self, s: str) -> str: r"""Parse the string with slashed regexes into a valid regex. Given a string like: Match a 10 digit phone number: /\d{3}-\d{3}-\d{4}/ Turn it into a valid regular expression which matches the literal text of the string and the regular expression. We have to remove the slashes because they differentiate between plain text and a regular expression. Unless the slashes are escaped, in which case they are interpreted as plain text, or there is only one slash, which is treated as plain text also. Check the tests in tests/test_transcript.py to see all the edge cases. """ regex = '' start = 0 while True: (regex, first_slash_pos, start) = self._escaped_find(regex, s, start, False) if first_slash_pos == -1: # no more slashes, add the rest of the string and bail regex += re.escape(s[start:]) break else: # there is a slash, add everything we have found so far # add stuff before the first slash as plain text regex += re.escape(s[start:first_slash_pos]) start = first_slash_pos + 1 # and go find the next one (regex, second_slash_pos, start) = self._escaped_find(regex, s, start, True) if second_slash_pos > 0: # add everything between the slashes (but not the slashes) # as a regular expression regex += s[start:second_slash_pos] # and change where we start looking for slashed on the # turn through the loop start = second_slash_pos + 1 else: # No closing slash, we have to add the first slash, # and the rest of the text regex += re.escape(s[start - 1 :]) break return regex @staticmethod def _escaped_find(regex: str, s: str, start: int, in_regex: bool) -> Tuple[str, int, int]: """Find the next slash in {s} after {start} that is not preceded by a backslash. If we find an escaped slash, add everything up to and including it to regex, updating {start}. {start} therefore serves two purposes, tells us where to start looking for the next thing, and also tells us where in {s} we have already added things to {regex} {in_regex} specifies whether we are currently searching in a regex, we behave differently if we are or if we aren't. """ while True: pos = s.find('/', start) if pos == -1: # no match, return to caller break elif pos == 0: # slash at the beginning of the string, so it can't be # escaped. We found it. break else: # check if the slash is preceeded by a backslash if s[pos - 1 : pos] == '\\': # it is. if in_regex: # add everything up to the backslash as a # regular expression regex += s[start : pos - 1] # skip the backslash, and add the slash regex += s[pos] else: # add everything up to the backslash as escaped # plain text regex += re.escape(s[start : pos - 1]) # and then add the slash as escaped # plain text regex += re.escape(s[pos]) # update start to show we have handled everything # before it start = pos + 1 # and continue to look else: # slash is not escaped, this is what we are looking for break return regex, pos, start cmd2-2.3.3/cmd2/utils.py000066400000000000000000001236201416142110700147040ustar00rootroot00000000000000# coding=utf-8 """Shared utility functions""" import argparse import collections import functools import glob import inspect import itertools import os import re import subprocess import sys import threading import unicodedata from enum import ( Enum, ) from typing import ( TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, TextIO, Type, TypeVar, Union, cast, ) from . import ( constants, ) from .argparse_custom import ( ChoicesProviderFunc, CompleterFunc, ) if TYPE_CHECKING: # pragma: no cover import cmd2 # noqa: F401 PopenTextIO = subprocess.Popen[bytes] else: PopenTextIO = subprocess.Popen _T = TypeVar('_T') def is_quoted(arg: str) -> bool: """ Checks if a string is quoted :param arg: the string being checked for quotes :return: True if a string is quoted """ return len(arg) > 1 and arg[0] == arg[-1] and arg[0] in constants.QUOTES def quote_string(arg: str) -> str: """Quote a string""" if '"' in arg: quote = "'" else: quote = '"' return quote + arg + quote def quote_string_if_needed(arg: str) -> str: """Quote a string if it contains spaces and isn't already quoted""" if is_quoted(arg) or ' ' not in arg: return arg return quote_string(arg) def strip_quotes(arg: str) -> str: """Strip outer quotes from a string. Applies to both single and double quotes. :param arg: string to strip outer quotes from :return: same string with potentially outer quotes stripped """ if is_quoted(arg): arg = arg[1:-1] return arg def str_to_bool(val: str) -> bool: """Converts a string to a boolean based on its value. :param val: string being converted :return: boolean value expressed in the string :raises: ValueError if the string does not contain a value corresponding to a boolean value """ if isinstance(val, str): if val.capitalize() == str(True): return True elif val.capitalize() == str(False): return False raise ValueError("must be True or False (case-insensitive)") class Settable: """Used to configure an attribute to be settable via the set command in the CLI""" def __init__( self, name: str, val_type: Union[Type[Any], Callable[[Any], Any]], description: str, settable_object: object, *, settable_attrib_name: Optional[str] = None, onchange_cb: Optional[Callable[[str, _T, _T], Any]] = None, choices: Optional[Iterable[Any]] = None, choices_provider: Optional[ChoicesProviderFunc] = None, completer: Optional[CompleterFunc] = None, ) -> None: """ Settable Initializer :param name: name of the instance attribute being made settable :param val_type: callable used to cast the string value from the command line into its proper type and even validate its value. Setting this to bool provides tab completion for true/false and validation using str_to_bool(). The val_type function should raise an exception if it fails. This exception will be caught and printed by Cmd.do_set(). :param description: string describing this setting :param settable_object: object to which the instance attribute belongs (e.g. self) :param settable_attrib_name: name which displays to the user in the output of the set command. Defaults to `name` if not specified. :param onchange_cb: optional function or method to call when the value of this settable is altered by the set command. (e.g. onchange_cb=self.debug_changed) Cmd.do_set() passes the following 3 arguments to onchange_cb: param_name: str - name of the changed parameter old_value: Any - the value before being changed new_value: Any - the value after being changed The following optional settings provide tab completion for a parameter's values. They correspond to the same settings in argparse-based tab completion. A maximum of one of these should be provided. :param choices: iterable of accepted values :param choices_provider: function that provides choices for this argument :param completer: tab completion function that provides choices for this argument """ if val_type == bool: def get_bool_choices(_) -> List[str]: # type: ignore[no-untyped-def] """Used to tab complete lowercase boolean values""" return ['true', 'false'] val_type = str_to_bool choices_provider = cast(ChoicesProviderFunc, get_bool_choices) self.name = name self.val_type = val_type self.description = description self.settable_obj = settable_object self.settable_attrib_name = settable_attrib_name if settable_attrib_name is not None else name self.onchange_cb = onchange_cb self.choices = choices self.choices_provider = choices_provider self.completer = completer def get_value(self) -> Any: """ Get the value of the settable attribute :return: """ return getattr(self.settable_obj, self.settable_attrib_name) def set_value(self, value: Any) -> Any: """ Set the settable attribute on the specified destination object :param value: New value to set :return: New value that the attribute was set to """ # Run the value through its type function to handle any conversion or validation new_value = self.val_type(value) # Make sure new_value is a valid choice if self.choices is not None and new_value not in self.choices: choices_str = ', '.join(map(repr, self.choices)) raise ValueError(f"invalid choice: {new_value!r} (choose from {choices_str})") # Try to update the settable's value orig_value = self.get_value() setattr(self.settable_obj, self.settable_attrib_name, new_value) # Check if we need to call an onchange callback if orig_value != new_value and self.onchange_cb: self.onchange_cb(self.name, orig_value, new_value) return new_value def is_text_file(file_path: str) -> bool: """Returns if a file contains only ASCII or UTF-8 encoded text and isn't empty. :param file_path: path to the file being checked :return: True if the file is a non-empty text file, otherwise False :raises OSError if file can't be read """ import codecs expanded_path = os.path.abspath(os.path.expanduser(file_path.strip())) valid_text_file = False # Only need to check for utf-8 compliance since that covers ASCII, too try: with codecs.open(expanded_path, encoding='utf-8', errors='strict') as f: # Make sure the file has only utf-8 text and is not empty if sum(1 for _ in f) > 0: valid_text_file = True except OSError: raise except UnicodeDecodeError: # Not UTF-8 pass return valid_text_file def remove_duplicates(list_to_prune: List[_T]) -> List[_T]: """Removes duplicates from a list while preserving order of the items. :param list_to_prune: the list being pruned of duplicates :return: The pruned list """ temp_dict: collections.OrderedDict[_T, Any] = collections.OrderedDict() for item in list_to_prune: temp_dict[item] = None return list(temp_dict.keys()) def norm_fold(astr: str) -> str: """Normalize and casefold Unicode strings for saner comparisons. :param astr: input unicode string :return: a normalized and case-folded version of the input string """ return unicodedata.normalize('NFC', astr).casefold() def alphabetical_sort(list_to_sort: Iterable[str]) -> List[str]: """Sorts a list of strings alphabetically. For example: ['a1', 'A11', 'A2', 'a22', 'a3'] To sort a list in place, don't call this method, which makes a copy. Instead, do this: my_list.sort(key=norm_fold) :param list_to_sort: the list being sorted :return: the sorted list """ return sorted(list_to_sort, key=norm_fold) def try_int_or_force_to_lower_case(input_str: str) -> Union[int, str]: """ Tries to convert the passed-in string to an integer. If that fails, it converts it to lower case using norm_fold. :param input_str: string to convert :return: the string as an integer or a lower case version of the string """ try: return int(input_str) except ValueError: return norm_fold(input_str) def natural_keys(input_str: str) -> List[Union[int, str]]: """ Converts a string into a list of integers and strings to support natural sorting (see natural_sort). For example: natural_keys('abc123def') -> ['abc', '123', 'def'] :param input_str: string to convert :return: list of strings and integers """ return [try_int_or_force_to_lower_case(substr) for substr in re.split(r'(\d+)', input_str)] def natural_sort(list_to_sort: Iterable[str]) -> List[str]: """ Sorts a list of strings case insensitively as well as numerically. For example: ['a1', 'A2', 'a3', 'A11', 'a22'] To sort a list in place, don't call this method, which makes a copy. Instead, do this: my_list.sort(key=natural_keys) :param list_to_sort: the list being sorted :return: the list sorted naturally """ return sorted(list_to_sort, key=natural_keys) def quote_specific_tokens(tokens: List[str], tokens_to_quote: List[str]) -> None: """ Quote specific tokens in a list :param tokens: token list being edited :param tokens_to_quote: the tokens, which if present in tokens, to quote """ for i, token in enumerate(tokens): if token in tokens_to_quote: tokens[i] = quote_string(token) def unquote_specific_tokens(tokens: List[str], tokens_to_unquote: List[str]) -> None: """ Unquote specific tokens in a list :param tokens: token list being edited :param tokens_to_unquote: the tokens, which if present in tokens, to unquote """ for i, token in enumerate(tokens): unquoted_token = strip_quotes(token) if unquoted_token in tokens_to_unquote: tokens[i] = unquoted_token def expand_user(token: str) -> str: """ Wrap os.expanduser() to support expanding ~ in quoted strings :param token: the string to expand """ if token: if is_quoted(token): quote_char = token[0] token = strip_quotes(token) else: quote_char = '' token = os.path.expanduser(token) # Restore the quotes even if not needed to preserve what the user typed if quote_char: token = quote_char + token + quote_char return token def expand_user_in_tokens(tokens: List[str]) -> None: """ Call expand_user() on all tokens in a list of strings :param tokens: tokens to expand """ for index, _ in enumerate(tokens): tokens[index] = expand_user(tokens[index]) def find_editor() -> Optional[str]: """ Used to set cmd2.Cmd.DEFAULT_EDITOR. If EDITOR env variable is set, that will be used. Otherwise the function will look for a known editor in directories specified by PATH env variable. :return: Default editor or None """ editor = os.environ.get('EDITOR') if not editor: if sys.platform[:3] == 'win': editors = ['code.cmd', 'notepad++.exe', 'notepad.exe'] else: editors = ['vim', 'vi', 'emacs', 'nano', 'pico', 'joe', 'code', 'subl', 'atom', 'gedit', 'geany', 'kate'] # Get a list of every directory in the PATH environment variable and ignore symbolic links env_path = os.getenv('PATH') if env_path is None: paths = [] else: paths = [p for p in env_path.split(os.path.pathsep) if not os.path.islink(p)] for editor, path in itertools.product(editors, paths): editor_path = os.path.join(path, editor) if os.path.isfile(editor_path) and os.access(editor_path, os.X_OK): if sys.platform[:3] == 'win': # Remove extension from Windows file names editor = os.path.splitext(editor)[0] break else: editor = None return editor def files_from_glob_pattern(pattern: str, access: int = os.F_OK) -> List[str]: """Return a list of file paths based on a glob pattern. Only files are returned, not directories, and optionally only files for which the user has a specified access to. :param pattern: file name or glob pattern :param access: file access type to verify (os.* where * is F_OK, R_OK, W_OK, or X_OK) :return: list of files matching the name or glob pattern """ return [f for f in glob.glob(pattern) if os.path.isfile(f) and os.access(f, access)] def files_from_glob_patterns(patterns: List[str], access: int = os.F_OK) -> List[str]: """Return a list of file paths based on a list of glob patterns. Only files are returned, not directories, and optionally only files for which the user has a specified access to. :param patterns: list of file names and/or glob patterns :param access: file access type to verify (os.* where * is F_OK, R_OK, W_OK, or X_OK) :return: list of files matching the names and/or glob patterns """ files = [] for pattern in patterns: matches = files_from_glob_pattern(pattern, access=access) files.extend(matches) return files def get_exes_in_path(starts_with: str) -> List[str]: """Returns names of executables in a user's path :param starts_with: what the exes should start with. leave blank for all exes in path. :return: a list of matching exe names """ # Purposely don't match any executable containing wildcards wildcards = ['*', '?'] for wildcard in wildcards: if wildcard in starts_with: return [] # Get a list of every directory in the PATH environment variable and ignore symbolic links env_path = os.getenv('PATH') if env_path is None: paths = [] else: paths = [p for p in env_path.split(os.path.pathsep) if not os.path.islink(p)] # Use a set to store exe names since there can be duplicates exes_set = set() # Find every executable file in the user's path that matches the pattern for path in paths: full_path = os.path.join(path, starts_with) matches = files_from_glob_pattern(full_path + '*', access=os.X_OK) for match in matches: exes_set.add(os.path.basename(match)) return list(exes_set) class StdSim: """ Class to simulate behavior of sys.stdout or sys.stderr. Stores contents in internal buffer and optionally echos to the inner stream it is simulating. """ def __init__( self, inner_stream: Union[TextIO, 'StdSim'], *, echo: bool = False, encoding: str = 'utf-8', errors: str = 'replace', ) -> None: """ StdSim Initializer :param inner_stream: the wrapped stream. Should be a TextIO or StdSim instance. :param echo: if True, then all input will be echoed to inner_stream :param encoding: codec for encoding/decoding strings (defaults to utf-8) :param errors: how to handle encoding/decoding errors (defaults to replace) """ self.inner_stream = inner_stream self.echo = echo self.encoding = encoding self.errors = errors self.pause_storage = False self.buffer = ByteBuf(self) def write(self, s: str) -> None: """ Add str to internal bytes buffer and if echo is True, echo contents to inner stream :param s: String to write to the stream """ if not isinstance(s, str): raise TypeError(f'write() argument must be str, not {type(s)}') if not self.pause_storage: self.buffer.byte_buf += s.encode(encoding=self.encoding, errors=self.errors) if self.echo: self.inner_stream.write(s) def getvalue(self) -> str: """Get the internal contents as a str""" return self.buffer.byte_buf.decode(encoding=self.encoding, errors=self.errors) def getbytes(self) -> bytes: """Get the internal contents as bytes""" return bytes(self.buffer.byte_buf) def read(self, size: Optional[int] = -1) -> str: """ Read from the internal contents as a str and then clear them out :param size: Number of bytes to read from the stream """ if size is None or size == -1: result = self.getvalue() self.clear() else: result = self.buffer.byte_buf[:size].decode(encoding=self.encoding, errors=self.errors) self.buffer.byte_buf = self.buffer.byte_buf[size:] return result def readbytes(self) -> bytes: """Read from the internal contents as bytes and then clear them out""" result = self.getbytes() self.clear() return result def clear(self) -> None: """Clear the internal contents""" self.buffer.byte_buf.clear() def isatty(self) -> bool: """StdSim only considered an interactive stream if `echo` is True and `inner_stream` is a tty.""" if self.echo: return self.inner_stream.isatty() else: return False @property def line_buffering(self) -> bool: """ Handle when the inner stream doesn't have a line_buffering attribute which is the case when running unit tests because pytest sets stdout to a pytest EncodedFile object. """ try: return bool(self.inner_stream.line_buffering) except AttributeError: return False def __getattr__(self, item: str) -> Any: if item in self.__dict__: return self.__dict__[item] else: return getattr(self.inner_stream, item) class ByteBuf: """ Used by StdSim to write binary data and stores the actual bytes written """ # Used to know when to flush the StdSim NEWLINES = [b'\n', b'\r'] def __init__(self, std_sim_instance: StdSim) -> None: self.byte_buf = bytearray() self.std_sim_instance = std_sim_instance def write(self, b: bytes) -> None: """Add bytes to internal bytes buffer and if echo is True, echo contents to inner stream.""" if not isinstance(b, bytes): raise TypeError(f'a bytes-like object is required, not {type(b)}') if not self.std_sim_instance.pause_storage: self.byte_buf += b if self.std_sim_instance.echo: self.std_sim_instance.inner_stream.buffer.write(b) # Since StdSim wraps TextIO streams, we will flush the stream if line buffering is on # and the bytes being written contain a new line character. This is helpful when StdSim # is being used to capture output of a shell command because it causes the output to print # to the screen more often than if we waited for the stream to flush its buffer. if self.std_sim_instance.line_buffering: if any(newline in b for newline in ByteBuf.NEWLINES): self.std_sim_instance.flush() class ProcReader: """ Used to capture stdout and stderr from a Popen process if any of those were set to subprocess.PIPE. If neither are pipes, then the process will run normally and no output will be captured. """ def __init__(self, proc: PopenTextIO, stdout: Union[StdSim, TextIO], stderr: Union[StdSim, TextIO]) -> None: """ ProcReader initializer :param proc: the Popen process being read from :param stdout: the stream to write captured stdout :param stderr: the stream to write captured stderr """ self._proc = proc self._stdout = stdout self._stderr = stderr self._out_thread = threading.Thread(name='out_thread', target=self._reader_thread_func, kwargs={'read_stdout': True}) self._err_thread = threading.Thread(name='err_thread', target=self._reader_thread_func, kwargs={'read_stdout': False}) # Start the reader threads for pipes only if self._proc.stdout is not None: self._out_thread.start() if self._proc.stderr is not None: self._err_thread.start() def send_sigint(self) -> None: """Send a SIGINT to the process similar to if +C were pressed""" import signal if sys.platform.startswith('win'): # cmd2 started the Windows process in a new process group. Therefore we must send # a CTRL_BREAK_EVENT since CTRL_C_EVENT signals cannot be generated for process groups. self._proc.send_signal(signal.CTRL_BREAK_EVENT) else: # Since cmd2 uses shell=True in its Popen calls, we need to send the SIGINT to # the whole process group to make sure it propagates further than the shell try: group_id = os.getpgid(self._proc.pid) os.killpg(group_id, signal.SIGINT) except ProcessLookupError: return def terminate(self) -> None: """Terminate the process""" self._proc.terminate() def wait(self) -> None: """Wait for the process to finish""" if self._out_thread.is_alive(): self._out_thread.join() if self._err_thread.is_alive(): self._err_thread.join() # Handle case where the process ended before the last read could be done. # This will return None for the streams that weren't pipes. out, err = self._proc.communicate() if out: self._write_bytes(self._stdout, out) if err: self._write_bytes(self._stderr, err) def _reader_thread_func(self, read_stdout: bool) -> None: """ Thread function that reads a stream from the process :param read_stdout: if True, then this thread deals with stdout. Otherwise it deals with stderr. """ if read_stdout: read_stream = self._proc.stdout write_stream = self._stdout else: read_stream = self._proc.stderr write_stream = self._stderr # The thread should have been started only if this stream was a pipe assert read_stream is not None # Run until process completes while self._proc.poll() is None: # noinspection PyUnresolvedReferences available = read_stream.peek() # type: ignore[attr-defined] if available: read_stream.read(len(available)) self._write_bytes(write_stream, available) @staticmethod def _write_bytes(stream: Union[StdSim, TextIO], to_write: bytes) -> None: """ Write bytes to a stream :param stream: the stream being written to :param to_write: the bytes being written """ try: stream.buffer.write(to_write) except BrokenPipeError: # This occurs if output is being piped to a process that closed pass class ContextFlag: """A context manager which is also used as a boolean flag value within the default sigint handler. Its main use is as a flag to prevent the SIGINT handler in cmd2 from raising a KeyboardInterrupt while a critical code section has set the flag to True. Because signal handling is always done on the main thread, this class is not thread-safe since there is no need. """ def __init__(self) -> None: # When this flag has a positive value, it is considered set. # When it is 0, it is not set. It should never go below 0. self.__count = 0 def __bool__(self) -> bool: return self.__count > 0 def __enter__(self) -> None: self.__count += 1 def __exit__(self, *args: Any) -> None: self.__count -= 1 if self.__count < 0: raise ValueError("count has gone below 0") class RedirectionSavedState: """Created by each command to store information required to restore state after redirection""" def __init__( self, self_stdout: Union[StdSim, TextIO], sys_stdout: Union[StdSim, TextIO], pipe_proc_reader: Optional[ProcReader], saved_redirecting: bool, ) -> None: """ RedirectionSavedState initializer :param self_stdout: saved value of Cmd.stdout :param sys_stdout: saved value of sys.stdout :param pipe_proc_reader: saved value of Cmd._cur_pipe_proc_reader :param saved_redirecting: saved value of Cmd._redirecting """ # Tells if command is redirecting self.redirecting = False # Used to restore values after redirection ends self.saved_self_stdout = self_stdout self.saved_sys_stdout = sys_stdout # Used to restore values after command ends regardless of whether the command redirected self.saved_pipe_proc_reader = pipe_proc_reader self.saved_redirecting = saved_redirecting class TextAlignment(Enum): """Horizontal text alignment""" LEFT = 1 CENTER = 2 RIGHT = 3 def align_text( text: str, alignment: TextAlignment, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False, ) -> str: """ Align text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently. There are convenience wrappers around this function: align_left(), align_center(), and align_right() :param text: text to align (can contain multiple lines) :param alignment: how to align the text :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character) :param width: display width of the aligned text. Defaults to width of the terminal. :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space. :param truncate: if True, then each line will be shortened to fit within the display width. The truncated portions are replaced by a '…' character. Defaults to False. :return: aligned text :raises: TypeError if fill_char is more than one character (not including ANSI style sequences) :raises: ValueError if text or fill_char contains an unprintable character :raises: ValueError if width is less than 1 """ import io import shutil from . import ( ansi, ) if width is None: width = shutil.get_terminal_size().columns if width < 1: raise ValueError("width must be at least 1") # Convert tabs to spaces text = text.replace('\t', ' ' * tab_width) fill_char = fill_char.replace('\t', ' ') # Save fill_char with no styles for use later stripped_fill_char = ansi.strip_style(fill_char) if len(stripped_fill_char) != 1: raise TypeError("Fill character must be exactly one character long") fill_char_width = ansi.style_aware_wcswidth(fill_char) if fill_char_width == -1: raise (ValueError("Fill character is an unprintable character")) # Isolate the style chars before and after the fill character. We will use them when building sequences of # of fill characters. Instead of repeating the style characters for each fill character, we'll wrap each sequence. fill_char_style_begin, fill_char_style_end = fill_char.split(stripped_fill_char) if text: lines = text.splitlines() else: lines = [''] text_buf = io.StringIO() # ANSI style sequences that may affect future lines will be cancelled by the fill_char's style. # To avoid this, we save the state of a line's style so we can restore it when beginning the next line. # This also allows the lines to be used independently and still have their style. TableCreator does this. aggregate_styles = '' for index, line in enumerate(lines): if index > 0: text_buf.write('\n') if truncate: line = truncate_line(line, width) line_width = ansi.style_aware_wcswidth(line) if line_width == -1: raise (ValueError("Text to align contains an unprintable character")) # Get the styles in this line line_styles = get_styles_in_text(line) # Calculate how wide each side of filling needs to be if line_width >= width: # Don't return here even though the line needs no fill chars. # There may be styles sequences to restore. total_fill_width = 0 else: total_fill_width = width - line_width if alignment == TextAlignment.LEFT: left_fill_width = 0 right_fill_width = total_fill_width elif alignment == TextAlignment.CENTER: left_fill_width = total_fill_width // 2 right_fill_width = total_fill_width - left_fill_width else: left_fill_width = total_fill_width right_fill_width = 0 # Determine how many fill characters are needed to cover the width left_fill = (left_fill_width // fill_char_width) * stripped_fill_char right_fill = (right_fill_width // fill_char_width) * stripped_fill_char # In cases where the fill character display width didn't divide evenly into # the gap being filled, pad the remainder with space. left_fill += ' ' * (left_fill_width - ansi.style_aware_wcswidth(left_fill)) right_fill += ' ' * (right_fill_width - ansi.style_aware_wcswidth(right_fill)) # Don't allow styles in fill characters and text to affect one another if fill_char_style_begin or fill_char_style_end or aggregate_styles or line_styles: if left_fill: left_fill = ansi.TextStyle.RESET_ALL + fill_char_style_begin + left_fill + fill_char_style_end left_fill += ansi.TextStyle.RESET_ALL if right_fill: right_fill = ansi.TextStyle.RESET_ALL + fill_char_style_begin + right_fill + fill_char_style_end right_fill += ansi.TextStyle.RESET_ALL # Write the line and restore any styles from previous lines text_buf.write(left_fill + aggregate_styles + line + right_fill) # Update the aggregate with styles in this line aggregate_styles += ''.join(line_styles.values()) return text_buf.getvalue() def align_left( text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False ) -> str: """ Left align text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently. :param text: text to left align (can contain multiple lines) :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character) :param width: display width of the aligned text. Defaults to width of the terminal. :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space. :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is replaced by a '…' character. Defaults to False. :return: left-aligned text :raises: TypeError if fill_char is more than one character (not including ANSI style sequences) :raises: ValueError if text or fill_char contains an unprintable character :raises: ValueError if width is less than 1 """ return align_text(text, TextAlignment.LEFT, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate) def align_center( text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False ) -> str: """ Center text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently. :param text: text to center (can contain multiple lines) :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character) :param width: display width of the aligned text. Defaults to width of the terminal. :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space. :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is replaced by a '…' character. Defaults to False. :return: centered text :raises: TypeError if fill_char is more than one character (not including ANSI style sequences) :raises: ValueError if text or fill_char contains an unprintable character :raises: ValueError if width is less than 1 """ return align_text(text, TextAlignment.CENTER, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate) def align_right( text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False ) -> str: """ Right align text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently. :param text: text to right align (can contain multiple lines) :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character) :param width: display width of the aligned text. Defaults to width of the terminal. :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space. :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is replaced by a '…' character. Defaults to False. :return: right-aligned text :raises: TypeError if fill_char is more than one character (not including ANSI style sequences) :raises: ValueError if text or fill_char contains an unprintable character :raises: ValueError if width is less than 1 """ return align_text(text, TextAlignment.RIGHT, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate) def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str: """ Truncate a single line to fit within a given display width. Any portion of the string that is truncated is replaced by a '…' character. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If there are ANSI style sequences in the string after where truncation occurs, this function will append them to the returned string. This is done to prevent issues caused in cases like: truncate_line(Fg.BLUE + hello + Fg.RESET, 3) In this case, "hello" would be truncated before Fg.RESET resets the color from blue. Appending the remaining style sequences makes sure the style is in the same state had the entire string been printed. align_text() relies on this behavior when preserving style over multiple lines. :param line: text to truncate :param max_width: the maximum display width the resulting string is allowed to have :param tab_width: any tabs in the text will be replaced with this many spaces :return: line that has a display width less than or equal to width :raises: ValueError if text contains an unprintable character like a newline :raises: ValueError if max_width is less than 1 """ import io from . import ( ansi, ) # Handle tabs line = line.replace('\t', ' ' * tab_width) if ansi.style_aware_wcswidth(line) == -1: raise (ValueError("text contains an unprintable character")) if max_width < 1: raise ValueError("max_width must be at least 1") if ansi.style_aware_wcswidth(line) <= max_width: return line # Find all style sequences in the line styles = get_styles_in_text(line) # Add characters one by one and preserve all style sequences done = False index = 0 total_width = 0 truncated_buf = io.StringIO() while not done: # Check if a style sequence is at this index. These don't count toward display width. if index in styles: truncated_buf.write(styles[index]) style_len = len(styles[index]) styles.pop(index) index += style_len continue char = line[index] char_width = ansi.style_aware_wcswidth(char) # This char will make the text too wide, add the ellipsis instead if char_width + total_width >= max_width: char = constants.HORIZONTAL_ELLIPSIS char_width = ansi.style_aware_wcswidth(char) done = True total_width += char_width truncated_buf.write(char) index += 1 # Append remaining style sequences from original string truncated_buf.write(''.join(styles.values())) return truncated_buf.getvalue() def get_styles_in_text(text: str) -> Dict[int, str]: """ Return an OrderedDict containing all ANSI style sequences found in a string The structure of the dictionary is: key: index where sequences begins value: ANSI style sequence found at index in text Keys are in ascending order :param text: text to search for style sequences """ from . import ( ansi, ) start = 0 styles = collections.OrderedDict() while True: match = ansi.ANSI_STYLE_RE.search(text, start) if match is None: break styles[match.start()] = match.group() start += len(match.group()) return styles def categorize(func: Union[Callable[..., Any], Iterable[Callable[..., Any]]], category: str) -> None: """Categorize a function. The help command output will group the passed function under the specified category heading :param func: function or list of functions to categorize :param category: category to put it in :Example: >>> import cmd2 >>> class MyApp(cmd2.Cmd): >>> def do_echo(self, arglist): >>> self.poutput(' '.join(arglist) >>> >>> cmd2.utils.categorize(do_echo, "Text Processing") For an alternative approach to categorizing commands using a decorator, see :func:`~cmd2.decorators.with_category` """ if isinstance(func, Iterable): for item in func: setattr(item, constants.CMD_ATTR_HELP_CATEGORY, category) else: if inspect.ismethod(func): setattr(func.__func__, constants.CMD_ATTR_HELP_CATEGORY, category) # type: ignore[attr-defined] else: setattr(func, constants.CMD_ATTR_HELP_CATEGORY, category) def get_defining_class(meth: Callable[..., Any]) -> Optional[Type[Any]]: """ Attempts to resolve the class that defined a method. Inspired by implementation published here: https://stackoverflow.com/a/25959545/1956611 :param meth: method to inspect :return: class type in which the supplied method was defined. None if it couldn't be resolved. """ if isinstance(meth, functools.partial): return get_defining_class(meth.func) if inspect.ismethod(meth) or ( inspect.isbuiltin(meth) and getattr(meth, '__self__') is not None and getattr(meth.__self__, '__class__') # type: ignore[attr-defined] ): for cls in inspect.getmro(meth.__self__.__class__): # type: ignore[attr-defined] if meth.__name__ in cls.__dict__: return cls meth = getattr(meth, '__func__', meth) # fallback to __qualname__ parsing if inspect.isfunction(meth): cls = getattr(inspect.getmodule(meth), meth.__qualname__.split('.', 1)[0].rsplit('.', 1)[0]) if isinstance(cls, type): return cls return cast(type, getattr(meth, '__objclass__', None)) # handle special descriptor objects class CompletionMode(Enum): """Enum for what type of tab completion to perform in cmd2.Cmd.read_input()""" # Tab completion will be disabled during read_input() call # Use of custom up-arrow history supported NONE = 1 # read_input() will tab complete cmd2 commands and their arguments # cmd2's command line history will be used for up arrow if history is not provided. # Otherwise use of custom up-arrow history supported. COMMANDS = 2 # read_input() will tab complete based on one of its following parameters: # choices, choices_provider, completer, parser # Use of custom up-arrow history supported CUSTOM = 3 class CustomCompletionSettings: """Used by cmd2.Cmd.complete() to tab complete strings other than command arguments""" def __init__(self, parser: argparse.ArgumentParser, *, preserve_quotes: bool = False) -> None: """ Initializer :param parser: arg parser defining format of string being tab completed :param preserve_quotes: if True, then quoted tokens will keep their quotes when processed by ArgparseCompleter. This is helpful in cases when you're tab completing flag-like tokens (e.g. -o, --option) and you don't want them to be treated as argparse flags when quoted. Set this to True if you plan on passing the string to argparse with the tokens still quoted. """ self.parser = parser self.preserve_quotes = preserve_quotes cmd2-2.3.3/docs/000077500000000000000000000000001416142110700132715ustar00rootroot00000000000000cmd2-2.3.3/docs/Makefile000066400000000000000000000060701416142110700147340ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -nvWT SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/cmd2.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/cmd2.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." cmd2-2.3.3/docs/api/000077500000000000000000000000001416142110700140425ustar00rootroot00000000000000cmd2-2.3.3/docs/api/ansi.rst000066400000000000000000000000751416142110700155300ustar00rootroot00000000000000cmd2.ansi ========= .. automodule:: cmd2.ansi :members: cmd2-2.3.3/docs/api/argparse_completer.rst000066400000000000000000000001471416142110700204540ustar00rootroot00000000000000cmd2.argparse_completer ======================= .. automodule:: cmd2.argparse_completer :members: cmd2-2.3.3/docs/api/argparse_custom.rst000066400000000000000000000013761416142110700200010ustar00rootroot00000000000000cmd2.argparse_custom ==================== .. automodule:: cmd2.argparse_custom :members: Added Accessor Methods ---------------------- .. autofunction:: _action_get_choices_callable .. autofunction:: _action_set_choices_provider .. autofunction:: _action_set_completer .. autofunction:: _action_get_descriptive_header .. autofunction:: _action_set_descriptive_header .. autofunction:: _action_get_nargs_range .. autofunction:: _action_set_nargs_range .. autofunction:: _action_get_suppress_tab_hint .. autofunction:: _action_set_suppress_tab_hint .. autofunction:: _ArgumentParser_get_ap_completer_type .. autofunction:: _ArgumentParser_set_ap_completer_type Subcommand Removal ------------------ .. autofunction:: _SubParsersAction_remove_parser cmd2-2.3.3/docs/api/cmd.rst000066400000000000000000000041771416142110700153500ustar00rootroot00000000000000cmd2.Cmd ======== .. autoclass:: cmd2.Cmd :members: .. automethod:: __init__ .. attribute:: default_error The error message displayed when a non-existent command is run. Default: ``{} is not a recognized command, alias, or macro`` .. attribute:: help_error The error message displayed to the user when they request help for a command with no help defined. Default: ``No help on {}`` .. attribute:: prompt The prompt issued to solicit input. The default value is ``(Cmd)``. See :ref:`features/prompt:Prompt` for more information. .. attribute:: continuation_prompt The prompt issued to solicit input for the 2nd and subsequent lines of a :ref:`multiline command ` Default: ``>``. .. attribute:: echo If ``True``, output the prompt and user input before executing the command. When redirecting a series of commands to an output file, this allows you to see the command in the output. .. attribute:: settable This dictionary contains the name and description of all settings available to users. Users use the :ref:`features/builtin_commands:set` command to view and modify settings. Settings are stored in instance attributes with the same name as the setting. .. attribute:: history A record of previously entered commands. This attribute is an instance of :class:`cmd2.history.History`, and each command is an instance of :class:`cmd2.Statement`. .. attribute:: statement_parser An instance of :class:`cmd2.parsing.StatementParser` initialized and configured appropriately for parsing user input. .. attribute:: intro Set an introduction message which is displayed to the user before the :ref:`features/hooks:Command Processing Loop` begins. .. attribute:: py_bridge_name The symbol name which :ref:`features/scripting:Python Scripts` run using the :ref:`features/builtin_commands:run_pyscript` command can use to reference the parent ``cmd2`` application. cmd2-2.3.3/docs/api/command_definition.rst000066400000000000000000000001471416142110700204240ustar00rootroot00000000000000cmd2.command_definition ======================= .. automodule:: cmd2.command_definition :members: cmd2-2.3.3/docs/api/constants.rst000066400000000000000000000021041416142110700166050ustar00rootroot00000000000000cmd2.constants ============== .. automodule:: cmd2.constants .. data:: DEFAULT_SHORTCUTS If you do not supply shortcuts to :meth:`cmd2.Cmd.__init__`, the shortcuts defined here will be used instead. .. data:: COMMAND_NAME Used by :meth:`cmd2.Cmd.disable_command` and :meth:`cmd2.Cmd.disable_category`. Those methods allow you to selectively disable single commands or an entire category of commands. Should you want to include the name of the command in the error message displayed to the user when they try and run a disabled command, you can include this constant in the message where you would like the name of the command to appear. ``cmd2`` will replace this constant with the name of the command the user tried to run before displaying the error message. This constant is imported into the package namespace; the preferred syntax to import and reference it is:: import cmd2 errmsg = "The {} command is currently disabled.".format(cmd2.COMMAND_NAME) See ``src/examples/help_categories.py`` for an example. cmd2-2.3.3/docs/api/decorators.rst000066400000000000000000000001171416142110700167400ustar00rootroot00000000000000cmd2.decorators =============== .. automodule:: cmd2.decorators :members: cmd2-2.3.3/docs/api/exceptions.rst000066400000000000000000000006071416142110700167600ustar00rootroot00000000000000cmd2.exceptions =============== Custom cmd2 exceptions .. autoclass:: cmd2.exceptions.SkipPostcommandHooks :members: .. autoclass:: cmd2.exceptions.Cmd2ArgparseError :members: .. autoclass:: cmd2.exceptions.CommandSetRegistrationError :members: .. autoclass:: cmd2.exceptions.CompletionError :members: .. autoclass:: cmd2.exceptions.PassThroughException :members: cmd2-2.3.3/docs/api/history.rst000066400000000000000000000006011416142110700162720ustar00rootroot00000000000000cmd2.history =============== Classes for storing the history of previously entered commands. .. autoclass:: cmd2.history.History :members: .. autoclass:: cmd2.history.HistoryItem :members: .. attribute:: statement The :class:`~cmd2.Statement` object parsed from user input .. attribute:: idx The 1-based index of this statement in the history list cmd2-2.3.3/docs/api/index.rst000066400000000000000000000045071416142110700157110ustar00rootroot00000000000000API Reference ============= These pages document the public API for ``cmd2``. If a method, class, function, attribute, or constant is not documented here, consider it private and subject to change. There are many classes, methods, functions, and constants in the source code which do not begin with an underscore but are not documented here. When looking at the source code for this library, you cannot safely assume that because something doesn't start with an underscore, it is a public API. If a release of this library changes any of the items documented here, the version number will be incremented according to the `Semantic Version Specification `_. This documentation is for ``cmd2`` version |version|. .. toctree:: :maxdepth: 1 :hidden: cmd ansi argparse_completer argparse_custom constants command_definition decorators exceptions history parsing plugin py_bridge table_creator utils plugin_external_test **Modules** - :ref:`api/cmd:cmd2.Cmd` - functions and attributes of the main class in this library - :ref:`api/ansi:cmd2.ansi` - convenience classes and functions for generating ANSI escape sequences to style text in the terminal - :ref:`api/argparse_completer:cmd2.argparse_completer` - classes for ``argparse``-based tab completion - :ref:`api/argparse_custom:cmd2.argparse_custom` - classes and functions for extending ``argparse`` - :ref:`api/command_definition:cmd2.command_definition` - supports the definition of commands in separate classes to be composed into cmd2.Cmd - :ref:`api/constants:cmd2.constants` - just like it says on the tin - :ref:`api/decorators:cmd2.decorators` - decorators for ``cmd2`` commands - :ref:`api/exceptions:cmd2.exceptions` - custom ``cmd2`` exceptions - :ref:`api/history:cmd2.history` - classes for storing the history of previously entered commands - :ref:`api/parsing:cmd2.parsing` - classes for parsing and storing user input - :ref:`api/plugin:cmd2.plugin` - data classes for hook methods - :ref:`api/py_bridge:cmd2.py_bridge` - classes for bridging calls from the embedded python environment to the host app - :ref:`api/table_creator:cmd2.table_creator` - table creation module - :ref:`api/utils:cmd2.utils` - various utility classes and functions - :ref:`api/plugin_external_test:cmd2_ext_test` - External test plugin cmd2-2.3.3/docs/api/parsing.rst000066400000000000000000000045601416142110700162440ustar00rootroot00000000000000cmd2.parsing =============== Classes for parsing and storing user input. .. autoclass:: cmd2.parsing.StatementParser :members: .. automethod:: __init__ .. autoclass:: cmd2.Statement :members: .. attribute:: command The name of the command after shortcuts and macros have been expanded .. attribute:: args The arguments to the command as a string with spaces between the words, excluding output redirection and command terminators. If the user used quotes in their input, they remain here, and you will have to handle them on your own. .. attribute:: arg_list The arguments to the command as a list, excluding output redirection and command terminators. Each argument is represented as an element in the list. Quoted arguments remain quoted. If you want to remove the quotes, use :func:`cmd2.utils.strip_quotes` or use ``argv[1:]`` .. attribute:: raw If you want full access to exactly what the user typed at the input prompt you can get it, but you'll have to parse it on your own, including: - shortcuts and aliases - quoted commands and arguments - output redirection - multi-line command terminator handling If you use multiline commands, all the input will be passed to you in this string, but there will be embedded newlines where the user hit return to continue the command on the next line. .. attribute:: multiline_command If the command is a multi-line command, the name of the command will be in this attribute. Otherwise, it will be an empty string. .. attribute:: terminator If the command is a multi-line command, this attribute contains the termination character entered by the user to signal the end of input .. attribute:: suffix Any characters present between the input terminator and the output redirection tokens. .. attribute:: pipe_to If the user piped the output to a shell command, this attribute contains the entire shell command as a string. Otherwise it is an empty string. .. attribute:: output If output was redirected by the user, this contains the redirection token, i.e. ``>>``. .. attribute:: output_to If output was redirected by the user, this contains the requested destination with quotes preserved. cmd2-2.3.3/docs/api/plugin.rst000066400000000000000000000016171416142110700160770ustar00rootroot00000000000000cmd2.plugin =========== .. autoclass:: cmd2.plugin.PostparsingData :members: .. attribute:: stop Request the command loop terminate by setting ``True`` .. attribute:: statement The :class:`~cmd2.Statement` object parsed from user input .. autoclass:: cmd2.plugin.PrecommandData :members: .. attribute:: statement The :class:`~cmd2.Statement` object parsed from user input .. autoclass:: cmd2.plugin.PostcommandData :members: .. attribute:: stop Request the command loop terminate by setting ``True`` .. attribute:: statement The :class:`~cmd2.Statement` object parsed from user input .. autoclass:: cmd2.plugin.CommandFinalizationData :members: .. attribute:: stop Request the command loop terminate by setting ``True`` .. attribute:: statement The :class:`~cmd2.Statement` object parsed from user input cmd2-2.3.3/docs/api/plugin_external_test.rst000066400000000000000000000001621416142110700210320ustar00rootroot00000000000000cmd2_ext_test ============= External Test Plugin .. autoclass:: cmd2_ext_test.ExternalTestMixin :members: cmd2-2.3.3/docs/api/py_bridge.rst000066400000000000000000000001141416142110700165340ustar00rootroot00000000000000cmd2.py_bridge ============== .. automodule:: cmd2.py_bridge :members: cmd2-2.3.3/docs/api/table_creator.rst000066400000000000000000000012401416142110700173770ustar00rootroot00000000000000cmd2.table_creator ================== .. autoclass:: cmd2.table_creator.HorizontalAlignment :members: :undoc-members: .. autoclass:: cmd2.table_creator.VerticalAlignment :members: :undoc-members: .. autoclass:: cmd2.table_creator.Column :members: .. automethod:: __init__ .. autoclass:: cmd2.table_creator.TableCreator :members: .. automethod:: __init__ .. autoclass:: cmd2.table_creator.SimpleTable :members: .. automethod:: __init__ .. autoclass:: cmd2.table_creator.BorderedTable :members: .. automethod:: __init__ .. autoclass:: cmd2.table_creator.AlternatingTable :members: .. automethod:: __init__ cmd2-2.3.3/docs/api/utils.rst000066400000000000000000000036351416142110700157430ustar00rootroot00000000000000cmd2.utils ========== Settings -------- .. autoclass:: cmd2.utils.Settable :members: .. automethod:: __init__ Quote Handling -------------- .. autofunction:: cmd2.utils.is_quoted .. autofunction:: cmd2.utils.quote_string .. autofunction:: cmd2.utils.quote_string_if_needed .. autofunction:: cmd2.utils.strip_quotes .. autofunction:: cmd2.utils.quote_specific_tokens .. autofunction:: cmd2.utils.unquote_specific_tokens IO Handling ----------- .. autoclass:: cmd2.utils.StdSim :members: .. autoclass:: cmd2.utils.ByteBuf :members: .. autoclass:: cmd2.utils.ProcReader :members: Tab Completion -------------- .. autoclass:: cmd2.utils.CompletionMode .. attribute:: NONE Tab completion will be disabled during read_input() call. Use of custom up-arrow history supported. .. attribute:: COMMANDS read_input() will tab complete cmd2 commands and their arguments. cmd2's command line history will be used for up arrow if history is not provided. Otherwise use of custom up-arrow history supported. .. attribute:: CUSTOM read_input() will tab complete based on one of its following parameters (choices, choices_provider, completer, parser). Use of custom up-arrow history supported .. autoclass:: cmd2.utils.CustomCompletionSettings .. automethod:: __init__ Text Alignment -------------- .. autoclass:: cmd2.utils.TextAlignment :members: :undoc-members: .. autofunction:: cmd2.utils.align_text .. autofunction:: cmd2.utils.align_left .. autofunction:: cmd2.utils.align_right .. autofunction:: cmd2.utils.align_center .. autofunction:: cmd2.utils.truncate_line Miscellaneous ------------- .. autofunction:: cmd2.utils.str_to_bool .. autofunction:: cmd2.utils.categorize .. autofunction:: cmd2.utils.remove_duplicates .. autofunction:: cmd2.utils.alphabetical_sort .. autofunction:: cmd2.utils.natural_sort cmd2-2.3.3/docs/conf.py000066400000000000000000000137441416142110700146010ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ cmd2 documentation build configuration file, created by sphinx-quickstart on Wed Feb 10 12:05:28 2010. This file is execfile()d with the current directory set to its containing dir. Note that not all possible configuration values are present in this autogenerated file. All configuration values have a default; values that are commented out serve to show the default. If extensions (or modules to document with autodoc) are in another directory, add these directories to sys.path here. If the directory is relative to the documentation root, use os.path.abspath to make it absolute, like shown here. """ # Import for custom theme from Read the Docs import sphinx_rtd_theme import cmd2 # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosectionlabel', 'sphinx.ext.intersphinx', 'sphinx.ext.doctest', 'sphinx.ext.todo', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'cmd2' copyright = '2010-2021, cmd2 contributors' author = 'cmd2 contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # version will look like x.y.z version = cmd2.__version__ # release will look like x.y release = '.'.join(version.split('.')[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # configure autosectionlabel extension autosectionlabel_prefix_document = True # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # Custom theme from ReadTheDocs html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'cmd2doc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'cmd2.tex', 'cmd2 Documentation', 'Catherine Devlin and Todd Leonhardt', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [(master_doc, 'cmd2', 'cmd2 Documentation', [author], 1)] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( master_doc, 'cmd2', 'cmd2 Documentation', author, 'cmd2', 'A python package for building powerful command-line interpreter (CLI) programs.', 'Miscellaneous', ), ] # -- Options for Extensions ------------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} # options for autodoc autodoc_default_options = {'member-order': 'bysource'} # Ignore nitpicky warnings from autodoc which are occurring for very new versions of Sphinx and autodoc # They seem to be happening because autodoc is now trying to add hyperlinks to docs for typehint classes nitpick_ignore = [ ('py:class', 'Callable[[None], None]'), ('py:class', 'cmd2.cmd2.Cmd'), ('py:class', 'cmd2.parsing.Statement'), ('py:class', 'IO'), ('py:class', 'None'), ('py:class', 'Optional[Callable[[...], argparse.Namespace]]'), ('py:class', 'TextIO'), ('py:class', 'Union[None, Iterable, Callable]'), ('py:class', 'argparse._SubParsersAction'), ('py:class', 'cmd2.utils._T'), ('py:class', 'StdSim'), ('py:class', 'frame'), ] cmd2-2.3.3/docs/doc_conventions.rst000066400000000000000000000157741416142110700172330ustar00rootroot00000000000000Documentation Conventions ========================= Guiding Principles ------------------ Follow the `Documentation Principles `_ described by `Write The Docs `_ In addition: - We have gone to great lengths to retain compatibility with the standard library cmd, the documentation should make it easy for developers to understand how to move from cmd to cmd2, and what benefits that will provide - We should provide both descriptive and reference documentation. - API reference documentation should be generated from docstrings in the code - Documentation should include rich hyperlinking to other areas of the documentation, and to the API reference Style Checker ------------- Use `doc8 `_ to check the style of the documentation. This tool can be invoked using the proper options by typing: .. code-block:: shell $ invoke doc8 Naming Files ------------ All source files in the documentation must: - have all lower case file names - if the name has multiple words, separate them with an underscore - end in '.rst' Indenting --------- In reStructuredText all indenting is significant. Use 2 spaces per indenting level. Wrapping -------- Hard wrap all text so that line lengths are no greater than 79 characters. It makes everything easier when editing documentation, and has no impact on reading documentation because we render to html. Titles and Headings ------------------- reStructuredText allows flexibility in how headings are defined. You only have to worry about the heirarchy of headings within a single file. Sphinx magically handles the intra-file heirarchy on it's own. This magic means that no matter how you style titles and headings in the various files that comprise the documentation, Sphinx will render properly structured output. To ensure we have a similar consistency when viewing the source files, we use the following conventions for titles and headings: 1. When creating a heading for a section, do not use the overline and underline syntax. Use the underline syntax only:: Document Title ============== 2. The string of adornment characters on the line following the heading should be the same length as the title. 3. The title of a document should use the '=' adornment character on the next line and only one heading of this level should appear in each file. 4. Sections within a document should have their titles adorned with the '-' character:: Section Title ------------- 5. Subsections within a section should have their titles adorned with the '~' character:: Subsection Title ~~~~~~~~~~~~~~~~ 6. Use two blank lines before every title unless it's the first heading in the file. Use one blank line after every heading. 7. If your document needs more than three levels of sections, break it into separate documents. Inline Code ----------- This documentation declares ``python`` as the default Sphinx domain. Python code or interactive Python sessions can be presented by either: - finishing the preceding paragraph with a ``::`` and indenting the code - use the ``.. code-block::`` directive If you want to show non-Python code, like shell commands, then use ``.. code-block: shell``. External Hyperlinks ------------------- If you want to use an external hyperlink target, define the target at the top of the page or the top of the section, not the bottom. The target definition should always appear before it is referenced. Links To Other Documentation Pages and Sections ----------------------------------------------- We use the Sphinx `autosectionlabel `_ extension. This allows you to reference any header in any document by:: See :ref:`features/argument_processing:Help Messages` or:: See :ref:`custom title` Which render like See :ref:`features/argument_processing:Help Messages` and See :ref:`custom title` API Documentation ----------------- The API documentation is mostly pulled from docstrings in the source code using the Sphinx `autodoc `_ extension. However, Sphinx has issues generating documentation for instance attributes (see `cmd2 issue 821 `_ for the full discussion). We have chosen to not use code as the source of instance attribute documentation. Instead, it is added manually to the documentation files in ``cmd2/docs/api``. See ``cmd2/docs/api/cmd.rst`` to see how to add documentation for an attribute. For module data members and class attributes, the ``autodoc`` extension allows documentation in a comment with special formatting (using a #: to start the comment instead of just #), or in a docstring after the definition. This project has standardized on the docstring after the definition approach. Do not use the specially formatted comment approach. When using the Sphix ``autoclass`` directive, it must be preceded by two blank lines like so: .. code-block:: rst Classes for storing the history of previously entered commands. .. autoclass:: cmd2.history.History :members: .. autoclass:: cmd2.history.HistoryItem :members: Links to API Reference ---------------------- To reference a method or function, use one of the following approaches: 1. Reference the full dotted path of the method:: The :meth:`cmd2.Cmd.poutput` method is similar to the Python built-in print function. Which renders as: The :meth:`cmd2.Cmd.poutput` method is similar to the Python built-in print function. 2. Reference the full dotted path to the method, but only display the method name:: The :meth:`~cmd2.Cmd.poutput` method is similar to the Python built-in print function. Which renders as: The :meth:`~cmd2.Cmd.poutput` method is similar to the Python built-in print function. Avoid either of these approaches: 1. Reference just the class name without enough dotted path:: The :meth:`.Cmd.poutput` method is similar to the Python built-in print function. Because ``cmd2.Cmd`` subclasses ``cmd.Cmd`` from the standard library, this approach does not clarify which class it is referring to. 2. Reference just a method name:: The :meth:`poutput` method is similar to the Python built-in print function. While Sphinx may be smart enough to generate the correct output, the potential for multiple matching references is high, which causes Sphinx to generate warnings. The build pipeline that renders the documentation treats warnings as fatal errors. It's best to just be specific about what you are referencing. See ``_ for the discussion of how we determined this approach. Referencing cmd2 ----------------- Whenever you reference ``cmd2`` in the documentation, enclose it in double backticks. This indicates an inline literal in restructured text, and makes it stand out when rendered as html. cmd2-2.3.3/docs/examples/000077500000000000000000000000001416142110700151075ustar00rootroot00000000000000cmd2-2.3.3/docs/examples/alternate_event_loops.rst000066400000000000000000000060261416142110700222410ustar00rootroot00000000000000Alternate Event Loops ===================== Throughout this documentation we have focused on the **90%** use case, that is the use case we believe around **90+%** of our user base is looking for. This focuses on ease of use and the best out-of-the-box experience where developers get the most functionality for the least amount of effort. We are talking about running ``cmd2`` applications with the ``cmdloop()`` method:: from cmd2 import Cmd class App(Cmd): # customized attributes and methods here app = App() app.cmdloop() However, there are some limitations to this way of using ``cmd2``, mainly that ``cmd2`` owns the inner loop of a program. This can be unnecessarily restrictive and can prevent using libraries which depend on controlling their own event loop. Many Python concurrency libraries involve or require an event loop which they are in control of such as asyncio_, gevent_, Twisted_, etc. .. _asyncio: https://docs.python.org/3/library/asyncio.html .. _gevent: http://www.gevent.org/ .. _Twisted: https://twistedmatrix.com ``cmd2`` applications can be executed in a fashion where ``cmd2`` doesn't own the main loop for the program by using code like the following:: import cmd2 class Cmd2EventBased(cmd2.Cmd): def __init__(self): cmd2.Cmd.__init__(self) # ... your class code here ... if __name__ == '__main__': app = Cmd2EventBased() app.preloop() # Do this within whatever event loop mechanism you wish to run a single command cmd_line_text = "help history" app.runcmds_plus_hooks([cmd_line_text]) app.postloop() The :meth:`~cmd2.Cmd.runcmds_plus_hooks()` method runs multiple commands via :meth:`~cmd2.Cmd.onecmd_plus_hooks`. The :meth:`~cmd2.Cmd.onecmd_plus_hooks()` method will do the following to execute a single command in a normal fashion: #. Parse user input into a :class:`~cmd2.Statement` object #. Call methods registered with :meth:`~cmd2.Cmd.register_postparsing_hook()` #. Redirect output, if user asked for it and it's allowed #. Start timer #. Call methods registered with :meth:`~cmd2.Cmd.register_precmd_hook` #. Call :meth:`~cmd2.Cmd.precmd` - for backwards compatibility with ``cmd.Cmd`` #. Add statement to :ref:`features/history:History` #. Call `do_command` method #. Call methods registered with :meth:`~cmd2.Cmd.register_postcmd_hook()` #. Call :meth:`~cmd2.Cmd.postcmd` - for backwards compatibility with ``cmd.Cmd`` #. Stop timer and display the elapsed time #. Stop redirecting output if it was redirected #. Call methods registered with :meth:`~cmd2.Cmd.register_cmdfinalization_hook()` Running in this fashion enables the ability to integrate with an external event loop. However, how to integrate with any specific event loop is beyond the scope of this documentation. Please note that running in this fashion comes with several disadvantages, including: * Requires the developer to write more code * Does not support transcript testing * Does not allow commands at invocation via command-line arguments cmd2-2.3.3/docs/examples/first_app.rst000066400000000000000000000302271416142110700176340ustar00rootroot00000000000000First Application ================= .. _cmd: https://docs.python.org/3/library/cmd.html Here's a quick walkthrough of a simple application which demonstrates 8 features of ``cmd2``: * :ref:`features/settings:Settings` * :ref:`features/commands:Commands` * :ref:`features/argument_processing:Argument Processing` * :ref:`features/generating_output:Generating Output` * :ref:`features/help:Help` * :ref:`features/shortcuts_aliases_macros:Shortcuts` * :ref:`features/multiline_commands:Multiline Commands` * :ref:`features/history:History` If you don't want to type as we go, you can download the complete source for this example. Basic Application ----------------- First we need to create a new ``cmd2`` application. Create a new file ``first_app.py`` with the following contents:: #!/usr/bin/env python """A simple cmd2 application.""" import cmd2 class FirstApp(cmd2.Cmd): """A simple cmd2 application.""" if __name__ == '__main__': import sys c = FirstApp() sys.exit(c.cmdloop()) We have a new class ``FirstApp`` which is a subclass of :class:`cmd2.Cmd`. When we tell python to run our file like this: .. code-block:: shell $ python first_app.py it creates an instance of our class, and calls the :meth:`~cmd2.Cmd.cmdloop` method. This method accepts user input and runs commands based on that input. Because we subclassed :class:`cmd2.Cmd`, our new app already has a bunch of features built in. Congratulations, you have a working ``cmd2`` app. You can run it, and then type ``quit`` to exit. Create a New Setting -------------------- Before we create our first command, we are going to add a setting to this app. ``cmd2`` includes robust support for :ref:`features/settings:Settings`. You configure settings during object initialization, so we need to add an initializer to our class:: def __init__(self): super().__init__() # Make maxrepeats settable at runtime self.maxrepeats = 3 self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) In that initializer, the first thing to do is to make sure we initialize ``cmd2``. That's what the ``super().__init__()`` line does. Next create an attribute to hold the setting. Finally, call the :meth:`~cmd2.Cmd.add_settable` method with a new instance of a :meth:`~cmd2.utils.Settable` class. Now if you run the script, and enter the ``set`` command to see the settings, like this: .. code-block:: shell $ python first_app.py (Cmd) set you will see our ``maxrepeats`` setting show up with it's default value of ``3``. Create A Command ---------------- Now we will create our first command, called ``speak`` which will echo back whatever we tell it to say. We are going to use an :ref:`argument processor ` so the ``speak`` command can shout and talk piglatin. We will also use some built in methods for :ref:`generating output `. Add this code to ``first_app.py``, so that the ``speak_parser`` attribute and the ``do_speak()`` method are part of the ``CmdLineApp()`` class:: speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) Up at the top of the script, you'll also need to add:: import argparse There's a bit to unpack here, so let's walk through it. We created ``speak_parser``, which uses the `argparse `_ module from the Python standard library to parse command line input from a user. There is nothing thus far that is specific to ``cmd2``. There is also a new method called ``do_speak()``. In both cmd_ and ``cmd2``, methods that start with ``do_`` become new commands, so by defining this method we have created a command called ``speak``. Note the :func:`~cmd2.decorators.with_argparser` decorator on the ``do_speak()`` method. This decorator does 3 useful things for us: 1. It tells ``cmd2`` to process all input for the ``speak`` command using the argparser we defined. If the user input doesn't meet the requirements defined by the argparser, then an error will be displayed for the user. 2. It alters our ``do_speak`` method so that instead of receiving the raw user input as a parameter, we receive the namespace from the argparser. 3. It creates a help message for us based on the argparser. You can see in the body of the method how we use the namespace from the argparser (passed in as the variable ``args``). We build an array of words which we will output, honoring both the ``--piglatin`` and ``--shout`` options. At the end of the method, we use our ``maxrepeats`` setting as an upper limit to the number of times we will print the output. The last thing you'll notice is that we used the ``self.poutput()`` method to display our output. ``poutput()`` is a method provided by ``cmd2``, which I strongly recommend you use anytime you want to :ref:`generate output `. It provides the following benefits: 1. Allows the user to redirect output to a text file or pipe it to a shell process 2. Gracefully handles ``BrokenPipeWarning`` exceptions for redirected output 3. Makes the output show up in a :ref:`transcript ` 4. Honors the setting to :ref:`strip embedded ansi sequences ` (typically used for background and foreground colors) Go run the script again, and try out the ``speak`` command. Try typing ``help speak``, and you will see a lovely usage message describing the various options for the command. With those few lines of code, we created a :ref:`command `, used an :ref:`Argument Processor `, added a nice :ref:`help message ` for our users, and :ref:`generated some output `. Shortcuts --------- ``cmd2`` has several capabilities to simplify repetitive user input: :ref:`Shortcuts, Aliases, and Macros `. Let's add a shortcut to our application. Shortcuts are character strings that can be used instead of a command name. For example, ``cmd2`` has support for a shortcut ``!`` which runs the ``shell`` command. So instead of typing this: .. code-block:: shell (Cmd) shell ls -al you can type this: .. code-block:: shell (Cmd) !ls -al Let's add a shortcut for our ``speak`` command. Change the ``__init__()`` method so it looks like this:: def __init__(self): shortcuts = cmd2.DEFAULT_SHORTCUTS shortcuts.update({'&': 'speak'}) super().__init__(shortcuts=shortcuts) # Make maxrepeats settable at runtime self.maxrepeats = 3 self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) Shortcuts are passed to the ``cmd2`` initializer, and if you want the built-in shortcuts of ``cmd2`` you have to pass them. These shortcuts are defined as a dictionary, with the key being the shortcut, and the value containing the command. When using the default shortcuts and also adding your own, it's a good idea to use the ``.update()`` method to modify the dictionary. This way if you add a shortcut that happens to already be in the default set, yours will override, and you won't get any errors at runtime. Run your app again, and type: .. code-block:: shell (Cmd) shortcuts to see the list of all of the shortcuts, including the one for speak that we just created. Multiline Commands ------------------ Some use cases benefit from the ability to have commands that span more than one line. For example, you might want the ability for your user to type in a SQL command, which can often span lines and which are terminated with a semicolon. Let's add a :ref:`multiline command ` to our application. First we'll create a new command called ``orate``. This code shows both the definition of our ``speak`` command, and the ``orate`` command:: @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) # orate is a synonym for speak which takes multiline input do_orate = do_speak With the new command created, we need to tell ``cmd2`` to treat that command as a multi-line command. Modify the super initialization line to look like this:: super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) Now when you run the example, you can type something like this: .. code-block:: shell (Cmd) orate O for a Muse of fire, that would ascend > The brightest heaven of invention, > A kingdom for a stage, princes to act > And monarchs to behold the swelling scene! ; Notice the prompt changes to indicate that input is still ongoing. ``cmd2`` will continue prompting for input until it sees an unquoted semicolon (the default multi-line command termination character). History ------- ``cmd2`` tracks the history of the commands that users enter. As a developer, you don't need to do anything to enable this functionality, you get it for free. If you want the history of commands to persist between invocations of your application, you'll need to do a little work. The :ref:`features/history:History` page has all the details. Users can access command history using two methods: - the `readline `_ library which provides a python interface to the `GNU readline library `_ - the ``history`` command which is built-in to ``cmd2`` From the prompt in a ``cmd2``-based application, you can press ``Control-p`` to move to the previously entered command, and ``Control-n`` to move to the next command. You can also search through the command history using ``Control-r``. The `GNU Readline User Manual `_ has all the details, including all the available commands, and instructions for customizing the key bindings. The ``history`` command allows a user to view the command history, and select commands from history by number, range, string search, or regular expression. With the selected commands, users can: - re-run the commands - edit the selected commands in a text editor, and run them after the text editor exits - save the commands to a file - run the commands, saving both the commands and their output to a file Learn more about the ``history`` command by typing ``history -h`` at any ``cmd2`` input prompt, or by exploring :ref:`Command History For Users `. Conclusion ---------- You've just created a simple, but functional command line application. With minimal work on your part, the application leverages many robust features of ``cmd2``. To learn more you can: - Dive into all of the :doc:`../features/index` that ``cmd2`` provides - Look at more :doc:`../examples/index` - Browse the :doc:`../api/index` cmd2-2.3.3/docs/examples/index.rst000066400000000000000000000001271416142110700167500ustar00rootroot00000000000000Examples ======== .. toctree:: :maxdepth: 1 first_app alternate_event_loops cmd2-2.3.3/docs/features/000077500000000000000000000000001416142110700151075ustar00rootroot00000000000000cmd2-2.3.3/docs/features/argument_processing.rst000066400000000000000000000325011416142110700217200ustar00rootroot00000000000000Argument Processing =================== ``cmd2`` makes it easy to add sophisticated argument processing to your commands using the `argparse `_ python module. ``cmd2`` handles the following for you: 1. Parsing input and quoted strings like the Unix shell 2. Parse the resulting argument list using an instance of ``argparse.ArgumentParser`` that you provide 3. Passes the resulting ``argparse.Namespace`` object to your command function. The ``Namespace`` includes the ``Statement`` object that was created when parsing the command line. It can be retrieved by calling ``cmd2_statement.get()`` on the ``Namespace``. 4. Adds the usage message from the argument parser to your command. 5. Checks if the ``-h/--help`` option is present, and if so, display the help message for the command These features are all provided by the ``@with_argparser`` decorator which is importable from ``cmd2``. See the either the argprint_ or decorator_ example to learn more about how to use the various ``cmd2`` argument processing decorators in your ``cmd2`` applications. .. _argprint: https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py .. _decorator: https://github.com/python-cmd2/cmd2/blob/master/examples/decorator_example.py ``cmd2`` provides the following decorators for assisting with parsing arguments passed to commands: * :func:`cmd2.decorators.with_argparser` * :func:`cmd2.decorators.with_argument_list` All of these decorators accept an optional **preserve_quotes** argument which defaults to ``False``. Setting this argument to ``True`` is useful for cases where you are passing the arguments to another command which might have its own argument parsing. Argument Parsing ---------------- For each command in the ``cmd2`` subclass which requires argument parsing, create a unique instance of ``argparse.ArgumentParser()`` which can parse the input appropriately for the command. Then decorate the command method with the ``@with_argparser`` decorator, passing the argument parser as the first parameter to the decorator. This changes the second argument to the command method, which will contain the results of ``ArgumentParser.parse_args()``. Here's what it looks like:: from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') argparser.add_argument('-r', '--repeat', type=int, help='output [n] times') argparser.add_argument('word', nargs='?', help='word to say') @with_argparser(argparser) def do_speak(self, opts) """Repeats what you tell me to.""" arg = opts.word if opts.piglatin: arg = '%s%say' % (arg[1:], arg[0]) if opts.shout: arg = arg.upper() repetitions = opts.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): self.poutput(arg) .. warning:: It is important that each command which uses the ``@with_argparser`` decorator be passed a unique instance of a parser. This limitation is due to bugs in CPython prior to Python 3.7 which make it impossible to make a deep copy of an instance of a ``argparse.ArgumentParser``. See the table_display_ example for a work-around that demonstrates how to create a function which returns a unique instance of the parser you want. .. note:: The ``@with_argparser`` decorator sets the ``prog`` variable in the argument parser based on the name of the method it is decorating. This will override anything you specify in ``prog`` variable when creating the argument parser. .. _table_display: https://github.com/python-cmd2/cmd2/blob/master/examples/table_display.py Help Messages ------------- By default, ``cmd2`` uses the docstring of the command method when a user asks for help on the command. When you use the ``@with_argparser`` decorator, the docstring for the ``do_*`` method is used to set the description for the ``argparse.ArgumentParser``. With this code:: from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): """create a html tag""" self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') the ``help tag`` command displays: .. code-block:: text usage: tag [-h] tag content [content ...] create a html tag positional arguments: tag tag content content to surround with tag optional arguments: -h, --help show this help message and exit If you would prefer you can set the ``description`` while instantiating the ``argparse.ArgumentParser`` and leave the docstring on your method empty:: from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser(description='create an html tag') argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') Now when the user enters ``help tag`` they see: .. code-block:: text usage: tag [-h] tag content [content ...] create an html tag positional arguments: tag tag content content to surround with tag optional arguments: -h, --help show this help message and exit To add additional text to the end of the generated help message, use the ``epilog`` variable:: from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser(description='create an html tag', epilog='This command cannot generate tags with no content, like
.') argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') Which yields: .. code-block:: text usage: tag [-h] tag content [content ...] create an html tag positional arguments: tag tag content content to surround with tag optional arguments: -h, --help show this help message and exit This command cannot generate tags with no content, like
.. warning:: If a command **foo** is decorated with one of cmd2's argparse decorators, then **help_foo** will not be invoked when ``help foo`` is called. The argparse_ module provides a rich API which can be used to tweak every aspect of the displayed help and we encourage ``cmd2`` developers to utilize that. .. _argparse: https://docs.python.org/3/library/argparse.html Argument List ------------- The default behavior of ``cmd2`` is to pass the user input directly to your ``do_*`` methods as a string. The object passed to your method is actually a ``Statement`` object, which has additional attributes that may be helpful, including ``arg_list`` and ``argv``:: class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ def do_say(self, statement): # statement contains a string self.poutput(statement) def do_speak(self, statement): # statement also has a list of arguments # quoted arguments remain quoted for arg in statement.arg_list: self.poutput(arg) def do_articulate(self, statement): # statement.argv contains the command # and the arguments, which have had quotes # stripped for arg in statement.argv: self.poutput(arg) If you don't want to access the additional attributes on the string passed to you``do_*`` method you can still have ``cmd2`` apply shell parsing rules to the user input and pass you a list of arguments instead of a string. Apply the ``@with_argument_list`` decorator to those methods that should receive an argument list instead of a string:: from cmd2 import with_argument_list class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ def do_say(self, cmdline): # cmdline contains a string pass @with_argument_list def do_speak(self, arglist): # arglist contains a list of arguments pass Unknown Positional Arguments ---------------------------- If you want all unknown arguments to be passed to your command as a list of strings, then decorate the command method with the ``@with_argparser(..., with_unknown_args=True)`` decorator. Here's what it looks like:: from cmd2 import Cmd2ArgumentParser, with_argparser dir_parser = Cmd2ArgumentParser() dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line") @with_argparser(dir_parser, with_unknown_args=True) def do_dir(self, args, unknown): """List contents of current directory.""" # No arguments for this command if unknown: self.perror("dir does not take any positional arguments:") self.do_help('dir') self.last_result = 'Bad arguments' return # Get the contents as a list contents = os.listdir(self.cwd) ... Using A Custom Namespace ------------------------ In some cases, it may be necessary to write custom ``argparse`` code that is dependent on state data of your application. To support this ability while still allowing use of the decorators, ``@with_argparser`` has an optional argument called ``ns_provider``. ``ns_provider`` is a Callable that accepts a ``cmd2.Cmd`` object as an argument and returns an ``argparse.Namespace``:: Callable[[cmd2.Cmd], argparse.Namespace] For example:: def settings_ns_provider(self) -> argparse.Namespace: """Populate an argparse Namespace with current settings""" ns = argparse.Namespace() ns.app_settings = self.settings return ns To use this function with the argparse decorators, do the following:: @with_argparser(my_parser, ns_provider=settings_ns_provider) The Namespace is passed by the decorators to the ``argparse`` parsing functions which gives your custom code access to the state data it needs for its parsing logic. Subcommands ------------ Subcommands are supported for commands using the ``@with_argparser`` decorator. The syntax is based on argparse sub-parsers. You may add multiple layers of subcommands for your command. ``cmd2`` will automatically traverse and tab complete subcommands for all commands using argparse. See the subcommands_ example to learn more about how to use subcommands in your ``cmd2`` application. .. _subcommands: https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py Argparse Extensions ------------------- ``cmd2`` augments the standard ``argparse.nargs`` with range tuple capability: - ``nargs=(5,)`` - accept 5 or more items - ``nargs=(8, 12)`` - accept 8 to 12 items ``cmd2`` also provides the :class:`cmd2.argparse_custom.Cmd2ArgumentParser` class which inherits from ``argparse.ArgumentParser`` and improves error and help output. Decorator Order --------------- If you are using custom decorators in combination with ``@cmd2.with_argparser``, then the order of your custom decorator(s) relative to the ``cmd2`` decorator matters when it comes to runtime behavior and ``argparse`` errors. There is nothing ``cmd2``-specific here, this is just a side-effect of how decorators work in Python. To learn more about how decorators work, see decorator_primer_. If you want your custom decorator's runtime behavior to occur in the case of an ``argparse`` error, then that decorator needs to go **after** the ``argparse`` one, e.g.:: @cmd2.with_argparser(foo_parser) @my_decorator def do_foo(self, args: argparse.Namespace) -> None: """foo docs""" pass However, if you do NOT want the custom decorator runtime behavior to occur even in the case of an `argparse` error, then that decorator needs to go **before** the ``arpgarse`` one, e.g.:: @my_decorator @cmd2.with_argparser(bar_parser) def do_bar(self, args: argparse.Namespace) -> None: """bar docs""" pass The help_categories_ example demonstrates both above cases in a concrete fashion. .. _decorator_primer: https://realpython.com/primer-on-python-decorators .. _help_categories: https://github.com/python-cmd2/cmd2/blob/master/examples/help_categories.py Reserved Argument Names ----------------------- ``cmd2`` argparse decorators add the following attributes to argparse Namespaces. To avoid naming collisions, do not use any of the names for your argparse arguments. - ``cmd2_statement`` - ``cmd2.Cmd2AttributeWrapper`` object containing ``cmd2.Statement`` object that was created when parsing the command line. - ``cmd2_handler`` - ``cmd2.Cmd2AttributeWrapper`` object containing a subcommand handler function or ``None`` if one was not set. - ``__subcmd_handler__`` - used by cmd2 to identify the handler for a subcommand created with ``@cmd2.as_subcommand_to`` decorator. cmd2-2.3.3/docs/features/builtin_commands.rst000066400000000000000000000116571416142110700212020ustar00rootroot00000000000000Builtin Commands ================ Applications which subclass :class:`cmd2.Cmd` inherit a number of commands which may be useful to your users. Developers can :ref:`features/builtin_commands:Remove Builtin Commands` if they do not want them to be part of the application. List of Builtin Commands ------------------------ alias ~~~~~ This command manages aliases via subcommands ``create``, ``delete``, and ``list``. See :ref:`features/shortcuts_aliases_macros:Aliases` for more information. edit ~~~~ This command launches an editor program and instructs it to open the given file name. Here's an example: .. code-block:: text (Cmd) edit ~/.ssh/config The program to be launched is determined by the value of the :ref:`features/settings:editor` setting. help ~~~~ This command lists available commands or provides detailed help for a specific command. When called with the ``-v/--verbose`` argument, it shows a brief description of each command. See :ref:`features/help:Help` for more information. history ~~~~~~~ This command allows you to view, run, edit, save, or clear previously entered commands from the history. See :ref:`features/history:History` for more information. ipy ~~~ This optional opt-in command enters an interactive IPython shell. See :ref:`features/embedded_python_shells:IPython (optional)` for more information. macro ~~~~~ This command manages macros via subcommands ``create``, ``delete``, and ``list``. A macro is similar to an alias, but it can contain argument placeholders. See :ref:`features/shortcuts_aliases_macros:Macros` for more information. py ~~ This command invokes a Python command or shell. See :ref:`features/embedded_python_shells:Embedded Python Shells` for more information. quit ~~~~ This command exits the ``cmd2`` application. .. _feature-builtin-commands-run-pyscript: run_pyscript ~~~~~~~~~~~~ This command runs a Python script file inside the ``cmd2`` application. See :ref:`features/scripting:Python Scripts` for more information. run_script ~~~~~~~~~~ This command runs commands in a script file that is encoded as either ASCII or UTF-8 text. See :ref:`features/scripting:Command Scripts` for more information. _relative_run_script ~~~~~~~~~~~~~~~~~~~~ This command is hidden from the help that's visible to end users. It runs a script like :ref:`features/builtin_commands:run_script` but does so using a path relative to the script that is currently executing. This is useful when you have scripts that run other scripts. See :ref:`features/scripting:Running Command Scripts` for more information. set ~~~ A list of all user-settable parameters, with brief comments, is viewable from within a running application: .. code-block:: text (Cmd) set Name Value Description ================================================================================================================== allow_style Terminal Allow ANSI text style sequences in output (valid values: Always, Never, Terminal) always_show_hint False Display tab completion hint even when completion suggestions print debug True Show full traceback on exception echo False Echo command issued into output editor vi Program used by 'edit' feedback_to_output False Include nonessentials in '|', '>' results max_completion_items 50 Maximum number of CompletionItems to display during tab completion quiet False Don't print nonessential feedback timing False Report execution times Any of these user-settable parameters can be set while running your app with the ``set`` command like so: .. code-block:: text (Cmd) set allow_style Never See :ref:`features/settings:Settings` for more information. shell ~~~~~ Execute a command as if at the operating system shell prompt: .. code-block:: text (Cmd) shell pwd -P /usr/local/bin shortcuts ~~~~~~~~~ This command lists available shortcuts. See :ref:`features/shortcuts_aliases_macros:Shortcuts` for more information. Remove Builtin Commands ----------------------- Developers may not want to offer the commands builtin to :class:`cmd2.Cmd` to users of their application. To remove a command you must delete the method implementing that command from the :class:`cmd2.Cmd` object at runtime. For example, if you wanted to remove the :ref:`features/builtin_commands:shell` command from your application:: class NoShellApp(cmd2.Cmd): """A simple cmd2 application.""" delattr(cmd2.Cmd, 'do_shell') cmd2-2.3.3/docs/features/clipboard.rst000066400000000000000000000020631416142110700176010ustar00rootroot00000000000000Clipboard Integration ===================== Nearly every operating system has some notion of a short-term storage area which can be accessed by any program. Usually this is called the clipboard, but sometimes people refer to it as the paste buffer. ``cmd2`` integrates with the operating system clipboard using the `pyperclip `_ module. Command output can be sent to the clipboard by ending the command with a greater than symbol: .. code-block:: text mycommand args > Think of it as though you are redirecting output to an unnamed, ephemeral place, you know, like the clipboard. You can also append output to the current contents of the clipboard by ending the command with two greater than symbols: .. code-block:: text mycommand arg1 arg2 >> Developers ---------- If you would like your ``cmd2`` based application to be able to use the clipboard in additional or alternative ways, you can use the following methods (which work uniformly on Windows, macOS, and Linux). .. automodule:: cmd2.clipboard :members: cmd2-2.3.3/docs/features/commands.rst000066400000000000000000000176731416142110700174600ustar00rootroot00000000000000Commands ======== .. _cmd: https://docs.python.org/3/library/cmd.html ``cmd2`` is designed to make it easy for you to create new commands. These commmands form the backbone of your application. If you started writing your application using cmd_, all the commands you have built will work when you move to ``cmd2``. However, there are many more capabilities available in ``cmd2`` which you can take advantage of to add more robust features to your commands, and which makes your commands easier to write. Before we get to all the good stuff, let's briefly discuss how to create a new command in your application. Basic Commands -------------- The simplest ``cmd2`` application looks like this:: #!/usr/bin/env python """A simple cmd2 application.""" import cmd2 class App(cmd2.Cmd): """A simple cmd2 application.""" if __name__ == '__main__': import sys c = App() sys.exit(c.cmdloop()) This application subclasses ``cmd2.Cmd`` but has no code of it's own, so all functionality (and there's quite a bit) is inherited. Lets create a simple command in this application called ``echo`` which outputs any arguments given to it. Add this method to the class:: def do_echo(self, line): self.poutput(line) When you type input into the ``cmd2`` prompt, the first space delimited word is treated as the command name. ``cmd2`` looks for a method called ``do_commandname``. If it exists, it calls the method, passing the rest of the user input as the first argument. If it doesn't exist ``cmd2`` prints an error message. As a result of this behavior, the only thing you have to do to create a new command is to define a new method in the class with the appropriate name. This is exactly how you would create a command using the cmd_ module which is part of the python standard library. .. note:: See :ref:`features/generating_output:Generating Output` if you are unfamiliar with the ``poutput()`` method. Statements ---------- A command is passed one argument: a string which contains all the rest of the user input. However, in ``cmd2`` this string is actually a ``Statement`` object, which is a subclass of ``str`` to retain backwards compatibility. ``cmd2`` has a much more sophsticated parsing engine than what's included in the cmd_ module. This parsing handles: - quoted arguments - output redirection and piping - multi-line commands - shortcut, macro, and alias expansion In addition to parsing all of these elements from the user input, ``cmd2`` also has code to make all of these items work; it's almost transparent to you and to the commands you write in your own application. However, by passing your command the ``Statement`` object instead of just a plain string, you can get visibility into what ``cmd2`` has done with the user input before your command got it. You can also avoid writing a bunch of parsing code, because ``cmd2`` gives you access to what it has already parsed. A ``Statement`` object is a subclass of ``str`` that contains the following attributes: command Name of the command called. You already know this because of the method ``cmd2`` called, but it can sometimes be nice to have it in a string, i.e. if you want your error messages to contain the command name. args A string containing the arguments to the command with output redirection or piping to shell commands removed. It turns out that the "string" value of the ``Statement`` object has all the output redirection and piping clauses removed as well. Quotes remain in the string. command_and_args A string of just the command and the arguments, with output redirection or piping to shell commands removed. argv A list of arguments a-la ``sys.argv``, including the command as ``argv[0]`` and the subsequent arguments as additional items in the list. Quotes around arguments will be stripped as will any output redirection or piping portions of the command. raw Full input exactly as typed by the user. terminator Character used to end a multiline command. You can configure multiple termination characters, and this attribute will tell you which one the user typed. For many simple commands, like the ``echo`` command above, you can ignore the ``Statement`` object and all of it's attributes and just use the passed value as a string. You might choose to use the ``argv`` attribute to do more sophisticated argument processing. Before you go too far down that path, you should check out the :ref:`features/argument_processing:Argument Processing` functionality included with ``cmd2``. Return Values ------------- Most commands should return nothing (either by omitting a ``return`` statement, or by ``return None``. This indicates that your command is finished (with or without errors), and that ``cmd2`` should prompt the user for more input. If you return ``True`` from a command method, that indicates to ``cmd2`` that it should stop prompting for user input and cleanly exit. ``cmd2`` already includes a ``quit`` command, but if you wanted to make another one called ``finish`` you could:: def do_finish(self, line): """Exit the application""" return True Exit Codes ---------- ``cmd2`` has basic infrastructure to support sh/ksh/csh/bash type exit codes. The ``cmd2.Cmd`` object sets an ``exit_code`` attribute to zero when it is instantiated. The value of this attribute is returned from the ``cmdloop()`` call. Therefore, if you don't do anything with this attribute in your code, ``cmdloop()`` will (almost) always return zero. There are a few built-in ``cmd2`` commands which set ``exit_code`` to ``1`` if an error occurs. You can use this capability to easily return your own values to the operating system shell:: #!/usr/bin/env python """A simple cmd2 application.""" import cmd2 class App(cmd2.Cmd): """A simple cmd2 application.""" def do_bail(self, line): """Exit the application""" self.perror("fatal error, exiting") self.exit_code = 2 return true if __name__ == '__main__': import sys c = App() sys.exit(c.cmdloop()) If the app was run from the `bash` operating system shell, then you would see the following interaction:: (Cmd) bail fatal error, exiting $ echo $? 2 Raising ``SystemExit(code)`` or calling ``sys.exit(code)`` in a command or hook function also sets ``self.exit_code`` and stops the program. Exception Handling ------------------ You may choose to catch and handle any exceptions which occur in a command method. If the command method raises an exception, ``cmd2`` will catch it and display it for you. The `debug` :ref:`setting ` controls how the exception is displayed. If `debug` is `false`, which is the default, ``cmd2`` will display the exception name and message. If `debug` is `true`, ``cmd2`` will display a traceback, and then display the exception name and message. There are a few exceptions which commands can raise that do not print as described above: - :attr:`cmd2.exceptions.SkipPostcommandHooks` - all postcommand hooks are skipped and no exception prints - :attr:`cmd2.exceptions.Cmd2ArgparseError` - behaves like ``SkipPostcommandHooks`` - ``SystemExit`` - ``stop`` will be set to ``True`` in an attempt to stop the command loop - ``KeyboardInterrupt`` - raised if running in a text script and ``stop`` isn't already True to stop the script All other ``BaseExceptions`` are not caught by ``cmd2`` and will be raised Disabling or Hiding Commands ---------------------------- See :ref:`features/disable_commands:Disabling Commands` for details of how to: - remove commands included in ``cmd2`` - hide commands from the help menu - disable and re-enable commands at runtime Modular Commands and Loading/Unloading Commands ----------------------------------------------- See :ref:`features/modular_commands:Modular Commands` for details of how to: - Define commands in separate CommandSet modules - Load or unload commands at runtime cmd2-2.3.3/docs/features/completion.rst000066400000000000000000000146761416142110700200300ustar00rootroot00000000000000Completion ========== :class:`cmd2.Cmd` adds tab completion of file system paths for all built-in commands where it makes sense, including: - :ref:`features/builtin_commands:edit` - :ref:`features/builtin_commands:run_pyscript` - :ref:`features/builtin_commands:run_script` - :ref:`features/builtin_commands:shell` :class:`cmd2.Cmd` also adds tab completion of shell commands to the :ref:`features/builtin_commands:shell` command. It is easy to add identical file system path completion to your own custom commands. Suppose you have defined a custom command ``foo`` by implementing the ``do_foo`` method. To enable path completion for the ``foo`` command, then add a line of code similar to the following to your class which inherits from :class:`cmd2.Cmd`:: complete_foo = cmd2.Cmd.path_complete This will effectively define the ``complete_foo`` readline completer method in your class and make it utilize the same path completion logic as the built-in commands. The built-in logic allows for a few more advanced path completion capabilities, such as cases where you only want to match directories. Suppose you have a custom command ``bar`` implemented by the ``do_bar`` method. You can enable path completion of directories only for this command by adding a line of code similar to the following to your class which inherits from :class:`cmd2.Cmd`:: # Make sure you have an "import functools" somewhere at the top complete_bar = functools.partialmethod(cmd2.Cmd.path_complete, path_filter=os.path.isdir) Included Tab Completion Functions --------------------------------- ``cmd2`` provides the following tab completion functions - :attr:`cmd2.Cmd.basic_complete` - helper method for tab completion against a list - :attr:`cmd2.Cmd.path_complete` - helper method provides flexible tab completion of file system paths - See the paged_output_ example for a simple use case - See the python_scripting_ example for a more full-featured use case - :attr:`cmd2.Cmd.delimiter_complete` - helper method for tab completion against a list but each match is split on a delimiter - See the basic_completion_ example for a demonstration of how to use this feature - :attr:`cmd2.Cmd.flag_based_complete` - helper method for tab completion based on a particular flag preceding the token being completed - :attr:`cmd2.Cmd.index_based_complete` - helper method for tab completion based on a fixed position in the input string - See the basic_completion_ example for a demonstration of how to use these features - ``flag_based_complete()`` and ``index_based_complete()`` are basic methods and should only be used if you are not familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use argparse-based_ completion. .. _paged_output: https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py .. _python_scripting: https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py .. _basic_completion: https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py Raising Exceptions During Completion ------------------------------------ There are times when an error occurs while tab completing and a message needs to be reported to the user. These include the following example cases: - Reading a database to retrieve a tab completion data set failed - A previous command line argument that determines the data set being completed is invalid - Tab completion hints ``cmd2`` provides the :class:`cmd2.exceptions.CompletionError` exception class for this capability. If an error occurs in which it is more desirable to display a message than a stack trace, then raise a ``CompletionError``. By default, the message displays in red like an error. However, ``CompletionError`` has a member called ``apply_style``. Set this False if the error style should not be applied. For instance, ``ArgparseCompleter`` sets it to False when displaying completion hints. .. _argparse-based: Tab Completion Using argparse Decorators ---------------------------------------- When using one the argparse-based :ref:`api/decorators:cmd2.decorators`, ``cmd2`` provides automatic tab completion of flag names. Tab completion of argument values can be configured by using one of three parameters to :meth:`argparse.ArgumentParser.add_argument` - ``choices`` - ``choices_provider`` - ``completer`` See the arg_decorators_ or colors_ example for a demonstration of how to use the ``choices`` parameter. See the argparse_completion_ example for a demonstration of how to use the ``choices_provider`` parameter. See the arg_decorators_ or argparse_completion_ example for a demonstration of how to use the ``completer`` parameter. When tab completing flags or argument values for a ``cmd2`` command using one of these decorators, ``cmd2`` keeps track of state so that once a flag has already previously been provided, it won't attempt to tab complete it again. When no completion results exists, a hint for the current argument will be displayed to help the user. .. _arg_decorators: https://github.com/python-cmd2/cmd2/blob/master/examples/arg_decorators.py .. _colors: https://github.com/python-cmd2/cmd2/blob/master/examples/colors.py .. _argparse_completion: https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_completion.py CompletionItem For Providing Extra Context ------------------------------------------ When tab completing things like a unique ID from a database, it can often be beneficial to provide the user with some extra context about the item being completed, such as a description. To facilitate this, ``cmd2`` defines the :class:`cmd2.argparse_custom.CompletionItem` class which can be returned from any of the 3 completion parameters: ``choices``, ``choices_provider``, and ``completer``. See the argparse_completion_ example or the implementation of the built-in :meth:`~cmd2.Cmd.do_set` command for demonstration of how this is used. Custom Completion with ``read_input()`` -------------------------------------------------- ``cmd2`` provides :attr:`cmd2.Cmd.read_input` as an alternative to Python's ``input()`` function. ``read_input`` supports configurable tab completion and up-arrow history at the prompt. See read_input_ example for a demonstration. .. _read_input: https://github.com/python-cmd2/cmd2/blob/master/examples/read_input.py For More Information -------------------- See :mod:`cmd2.argparse_custom` for a more detailed discussion of argparse completion. cmd2-2.3.3/docs/features/disable_commands.rst000066400000000000000000000071561416142110700211360ustar00rootroot00000000000000Disabling Commands ================== ``cmd2`` allows a developer to: - remove commands included in ``cmd2`` - prevent commands from appearing in the help menu (hide commands) - disable and re-enable commands at runtime Remove A Command ---------------- When a command has been removed, the command method has been deleted from the object. The command doesn't show up in help, and it can't be executed. This approach is appropriate if you never want a built-in command to be part of your application. Delete the command method in your initialization code:: class RemoveBuiltinCommand(cmd2.Cmd): """An app which removes a built-in command from cmd2""" def __init__(self): super().__init__() # To remove built-in commands entirely, delete # the "do_*" function from the cmd2.Cmd class del cmd2.Cmd.do_edit Hide A Command -------------- When a command is hidden, it won't show up in the help menu, but if the user knows it's there and types the command, it will be executed. You hide a command by adding it to the ``hidden_commands`` list:: class HiddenCommands(cmd2.Cmd): ""An app which demonstrates how to hide a command""" def __init__(self): super().__init__() self.hidden_commands.append('py') As shown above, you would typically do this as part of initializing your application. If you decide you want to unhide a command later in the execution of your application, you can by doing:: self.hidden_commands = [cmd for cmd in self.hidden_commands if cmd != 'py'] You might be thinking that the list comprehension is overkill and you'd rather do something like:: self.hidden_commands.remove('py') You may be right, but ``remove()`` will raise a ``ValueError`` if ``py`` isn't in the list, and it will only remove the first one if it's in the list multiple times. Disable A Command ----------------- One way to disable a command is to add code to the command method which determines whether the command should be executed or not. If the command should not be executed, your code can print an appropriate error message and return. ``cmd2`` also provides another way to accomplish the same thing. Here's a simple app which disables the ``open`` command if the door is locked:: class DisabledCommands(cmd2.Cmd): """An application which disables and enables commands""" def do_lock(self, line): self.disable_command('open', "you can't open the door because it is locked") self.poutput('the door is locked') def do_unlock(self, line): self.enable_command('open') self.poutput('the door is unlocked') def do_open(self, line): """open the door""" self.poutput('opening the door') This method has the added benefit of removing disabled commands from the help menu. But, this method only works if you know in advance that the command should be disabled, and if the conditions for re-enabling it are likewise known in advance. Disable A Category of Commands ------------------------------ You can group or categorize commands as shown in :ref:`features/help:Categorizing Commands`. If you do so, you can disable and enable all the commands in a category with a single method call. Say you have created a category of commands called "Server Information". You can disable all commands in that category:: not_connected_msg = 'You must be connected to use this command' self.disable_category('Server Information', not_connected_msg) Similarly, you can re-enable all the commands in a category:: self.enable_category('Server Information') cmd2-2.3.3/docs/features/embedded_python_shells.rst000066400000000000000000000072511416142110700223520ustar00rootroot00000000000000Embedded Python Shells ====================== Python (optional) ------------------ If the ``cmd2.Cmd`` class is instantiated with ``include_py=True``, then the optional ``py`` command will be present and run an interactive Python shell:: from cmd2 import Cmd class App(Cmd): def __init__(self): Cmd.__init__(self, include_py=True) The Python shell can run CLI commands from you application using the object named in ``self.pyscript_name`` (defaults to ``app``). This wrapper provides access to execute commands in your ``cmd2`` application while maintaining isolation from the full `Cmd` instance. For example, any application command can be run with ``app("command ...")``. You may optionally enable full access to to your application by setting ``self.self_in_py`` to ``True``. Enabling this flag adds ``self`` to the python session, which is a reference to your ``cmd2`` application. This can be useful for debugging your application. Any local or global variable created within the Python session will not persist in the CLI's environment. Anything in ``self.py_locals`` is always available in the Python environment. All of these parameters are also available to Python scripts which run in your application via the ``run_pyscript`` command: - supports tab completion of file system paths - has the ability to pass command-line arguments to the scripts invoked This command provides a more complicated and more powerful scripting capability than that provided by the simple text file scripts. Python scripts can include conditional control flow logic. See the **python_scripting.py** ``cmd2`` application and the **script_conditional.py** script in the ``examples`` source code directory for an example of how to achieve this in your own applications. See :ref:`features/scripting:Scripting` for an explanation of both scripting methods in **cmd2** applications. A simple example of using ``run_pyscript`` is shown below along with the arg_printer_ script:: (Cmd) run_pyscript examples/scripts/arg_printer.py foo bar baz Running Python script 'arg_printer.py' which was called with 3 arguments arg 1: 'foo' arg 2: 'bar' arg 3: 'baz' .. _arg_printer: https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/arg_printer.py IPython (optional) ------------------ **If** IPython_ is installed on the system **and** the ``cmd2.Cmd`` class is instantiated with ``include_ipy=True``, then the optional ``ipy`` command will run an interactive IPython shell:: from cmd2 import Cmd class App(Cmd): def __init__(self): Cmd.__init__(self, include_ipy=True) The ``ipy`` command enters an interactive IPython_ session. Similar to an interactive Python session, this shell can access your application instance via ``self`` if ``self.self_in_py`` is ``True`` and any changes to your application made via ``self`` will persist. However, any local or global variable created within the ``ipy`` shell will not persist in the CLI's environment Also, as in the interactive Python session, the ``ipy`` shell has access to the contents of ``self.py_locals`` and can call back into the application using the ``app`` object (or your custom name). IPython_ provides many advantages, including: * Comprehensive object introspection * Get help on objects with ``?`` * Extensible tab completion, with support by default for completion of python variables and keywords * Good built-in ipdb_ debugger The object introspection and tab completion make IPython particularly efficient for debugging as well as for interactive experimentation and data analysis. .. _IPython: http://ipython.readthedocs.io .. _ipdb: https://pypi.org/project/ipdb/ cmd2-2.3.3/docs/features/generating_output.rst000066400000000000000000000142751416142110700214150ustar00rootroot00000000000000Generating Output ================= A standard ``cmd`` application can produce output by using either of these methods:: print("Greetings, Professor Falken.", file=self.stdout) self.stdout.write("Shall we play a game?\n") While you could send output directly to ``sys.stdout``, :mod:`cmd2.Cmd` can be initialized with a ``stdin`` and ``stdout`` variables, which it stores as ``self.stdin`` and ``self.stdout``. By using these variables every time you produce output, you can trivially change where all the output goes by changing how you initialize your class. :mod:`cmd2.Cmd` extends this approach in a number of convenient ways. See :ref:`features/redirection:Output Redirection And Pipes` for information on how users can change where the output of a command is sent. In order for those features to work, the output you generate must be sent to ``self.stdout``. You can use the methods described above, and everything will work fine. :mod:`cmd2.Cmd` also includes a number of output related methods which you may use to enhance the output your application produces. Ordinary Output --------------- The :meth:`~cmd2.Cmd.poutput` method is similar to the Python `built-in print function `_. :meth:`~cmd2.Cmd.poutput` adds two conveniences: 1. Since users can pipe output to a shell command, it catches ``BrokenPipeError`` and outputs the contents of ``self.broken_pipe_warning`` to ``stderr``. ``self.broken_pipe_warning`` defaults to an empty string so this method will just swallow the exception. If you want to show an error message, put it in ``self.broken_pipe_warning`` when you initialize :mod:`~cmd2.Cmd`. 2. It examines and honors the :ref:`features/settings:allow_style` setting. See :ref:`features/generating_output:Colored Output` below for more details. Here's a simple command that shows this method in action:: def do_echo(self, args): """A simple command showing how poutput() works""" self.poutput(args) Error Messages -------------- When an error occurs in your program, you can display it on ``sys.stderr`` by calling the :meth:`~.cmd2.Cmd.perror` method. By default this method applies :meth:`cmd2.ansi.style_error` to the output. Warning Messages ---------------- :meth:`~.cmd2.Cmd.pwarning` is just like :meth:`~.cmd2.Cmd.perror` but applies :meth:`cmd2.ansi.style_warning` to the output. Feedback -------- You may have the need to display information to the user which is not intended to be part of the generated output. This could be debugging information or status information about the progress of long running commands. It's not output, it's not error messages, it's feedback. If you use the :ref:`features/settings:Timing` setting, the output of how long it took the command to run will be output as feedback. You can use the :meth:`~.cmd2.Cmd.pfeedback` method to produce this type of output, and several :ref:`features/settings:Settings` control how it is handled. If the :ref:`features/settings:quiet` setting is ``True``, then calling :meth:`~.cmd2.Cmd.pfeedback` produces no output. If :ref:`features/settings:quiet` is ``False``, the :ref:`features/settings:feedback_to_output` setting is consulted to determine whether to send the output to ``stdout`` or ``stderr``. Exceptions ---------- If your app catches an exception and you would like to display the exception to the user, the :meth:`~.cmd2.Cmd.pexcept` method can help. The default behavior is to just display the message contained within the exception. However, if the :ref:`features/settings:debug` setting is ``True``, then the entire stack trace will be displayed. Paging Output ------------- If you know you are going to generate a lot of output, you may want to display it in a way that the user can scroll forwards and backwards through it. If you pass all of the output to be displayed in a single call to :meth:`~.cmd2.Cmd.ppaged`, it will be piped to an operating system appropriate shell command to page the output. On Windows, the output is piped to ``more``; on Unix-like operating systems like MacOS and Linux, it is piped to ``less``. Colored Output -------------- You can add your own `ANSI escape sequences `_ to your output which tell the terminal to change the foreground and background colors. ``cmd2`` provides a number of convenience functions and classes for adding color and other styles to text. These are all documented in :mod:`cmd2.ansi`. After adding the desired escape sequences to your output, you should use one of these methods to present the output to the user: - :meth:`.cmd2.Cmd.poutput` - :meth:`.cmd2.Cmd.perror` - :meth:`.cmd2.Cmd.pwarning` - :meth:`.cmd2.Cmd.pexcept` - :meth:`.cmd2.Cmd.pfeedback` - :meth:`.cmd2.Cmd.ppaged` These methods all honor the :ref:`features/settings:allow_style` setting, which users can modify to control whether these escape codes are passed through to the terminal or not. Aligning Text -------------- If you would like to generate output which is left, center, or right aligned within a specified width or the terminal width, the following functions can help: - :meth:`cmd2.utils.align_left` - :meth:`cmd2.utils.align_center` - :meth:`cmd2.utils.align_right` These functions differ from Python's string justifying functions in that they support characters with display widths greater than 1. Additionally, ANSI style sequences are safely ignored and do not count toward the display width. This means colored text is supported. If text has line breaks, then each line is aligned independently. Columnar Output --------------- When generating output in multiple columns, you often need to calculate the width of each item so you can pad it appropriately with spaces. However, there are categories of Unicode characters that occupy 2 cells, and other that occupy 0. To further complicate matters, you might have included ANSI escape sequences in the output to generate colors on the terminal. The :meth:`cmd2.ansi.style_aware_wcswidth` function solves both of these problems. Pass it a string, and regardless of which Unicode characters and ANSI text style escape sequences it contains, it will tell you how many characters on the screen that string will consume when printed. cmd2-2.3.3/docs/features/help.rst000066400000000000000000000163271416142110700166020ustar00rootroot00000000000000Help ==== From our experience, end users rarely read documentation no matter how high- quality or useful that documentation might be. So it is important that you provide good built-in help within your application. Fortunately, ``cmd2`` makes this easy. Getting Help ------------ ``cmd2`` makes it easy for end users of ``cmd2`` applications to get help via the built-in ``help`` command. The ``help`` command by itself displays a list of the commands available: .. code-block:: text (Cmd) help Documented commands (use 'help -v' for verbose/'help ' for details): =========================================================================== alias help ipy py run_pyscript set shortcuts edit history macro quit run_script shell The ``help`` command can also be used to provide detailed help for a specific command: .. code-block:: text (Cmd) help quit Usage: quit [-h] Exit this application optional arguments: -h, --help show this help message and exit Providing Help -------------- ``cmd2`` makes it easy for developers of ``cmd2`` applications to provide this help. By default, the help for a command is the docstring for the ``do_*`` method defining the command - e.g. for a command **foo**, that command is implementd by defining the ``do_foo`` method and the docstring for that method is the help. For commands which use one of the ``argparse`` decorators to parse arguments, help is provided by ``argparse``. See :ref:`features/argument_processing:Help Messages` for more information. Occasionally there might be an unusual circumstance where providing static help text isn't good enough and you want to provide dynamic information in the help text for a command. To meet this need, if a ``help_foo`` method is defined to match the ``do_foo`` method, then that method will be used to provide the help for command **foo**. This dynamic help is only supported for commands which do not use an ``argparse`` decorator because didn't want different output for ``help cmd`` than for ``cmd -h``. Categorizing Commands --------------------- By default, the ``help`` command displays:: Documented commands (use 'help -v' for verbose/'help ' for details): =========================================================================== alias help ipy py run_pyscript set shortcuts edit history macro quit run_script shell If you have a large number of commands, you can optionally group your commands into categories. Here's the output from the example ``help_categories.py``:: Documented commands (use 'help -v' for verbose/'help ' for details): Application Management ====================== deploy findleakers redeploy sessions stop expire list restart start undeploy Command Management ================== disable_commands enable_commands Connecting ========== connect which Server Information ================== resources serverinfo sslconnectorciphers status thread_dump vminfo Other ===== alias edit history py run_pyscript set shortcuts config help macro quit run_script shell version There are 2 methods of specifying command categories, using the ``@with_category`` decorator or with the ``categorize()`` function. Once a single command category is detected, the help output switches to a categorized mode of display. All commands with an explicit category defined default to the category `Other`. Using the ``@with_category`` decorator:: @with_category(CMD_CAT_CONNECTING) def do_which(self, _): """Which command""" self.poutput('Which') Using the ``categorize()`` function: You can call with a single function:: def do_connect(self, _): """Connect command""" self.poutput('Connect') # Tag the above command functions under the category Connecting categorize(do_connect, CMD_CAT_CONNECTING) Or with an Iterable container of functions:: def do_undeploy(self, _): """Undeploy command""" self.poutput('Undeploy') def do_stop(self, _): """Stop command""" self.poutput('Stop') def do_findleakers(self, _): """Find Leakers command""" self.poutput('Find Leakers') # Tag the above command functions under the category Application Management categorize((do_undeploy, do_stop, do_findleakers), CMD_CAT_APP_MGMT) The ``help`` command also has a verbose option (``help -v`` or ``help --verbose``) that combines the help categories with per-command Help Messages:: Documented commands (use 'help -v' for verbose/'help ' for details): Application Management ================================================================================ deploy Deploy command expire Expire command findleakers Find Leakers command list List command redeploy Redeploy command restart usage: restart [-h] {now,later,sometime,whenever} sessions Sessions command start Start command stop Stop command undeploy Undeploy command Connecting ================================================================================ connect Connect command which Which command Server Information ================================================================================ resources Resources command serverinfo Server Info command sslconnectorciphers SSL Connector Ciphers command is an example of a command that contains multiple lines of help information for the user. Each line of help in a contiguous set of lines will be printed and aligned in the verbose output provided with 'help --verbose' status Status command thread_dump Thread Dump command vminfo VM Info command Other ================================================================================ alias Manage aliases config Config command edit Run a text editor and optionally open a file with it help List available commands or provide detailed help for a specific command history View, run, edit, save, or clear previously entered commands macro Manage macros py Invoke Python command or shell quit Exits this application run_pyscript Runs a python script file inside the console run_script Runs commands in script file that is encoded as either ASCII or UTF-8 text set Set a settable parameter or show current settings of parameters shell Execute a command as if at the OS prompt shortcuts List available shortcuts version Version command When called with the ``-v`` flag for verbose help, the one-line description for each command is provided by the first line of the docstring for that command's associated ``do_*`` method. cmd2-2.3.3/docs/features/history.rst000066400000000000000000000251401416142110700173440ustar00rootroot00000000000000History ======= For Developers -------------- The ``cmd`` module from the Python standard library includes ``readline`` history. :class:`cmd2.Cmd` offers the same ``readline`` capabilities, but also maintains its own data structures for the history of all commands entered by the user. When the class is initialized, it creates an instance of the :class:`cmd2.history.History` class (which is a subclass of ``list``) as :data:`cmd2.Cmd.history`. Each time a command is executed (this gets complex, see :ref:`features/hooks:Command Processing Loop` for exactly when) the parsed :class:`cmd2.Statement` is appended to :data:`cmd2.Cmd.history`. ``cmd2`` adds the option of making this history persistent via optional arguments to :meth:`cmd2.Cmd.__init__`. If you pass a filename in the ``persistent_history_file`` argument, the contents of :data:`cmd2.Cmd.history` will be written as compressed JSON to that history file. We chose this format instead of plain text to preserve the complete :class:`cmd2.Statement` object for each command. .. note:: ``readline`` saves everything you type, whether it is a valid command or not. ``cmd2`` only saves input to internal history if the command parses successfully and is a valid command. This design choice was intentional, because the contents of history can be saved to a file as a script, or can be re-run. Not saving invalid input reduces unintentional errors when doing so. However, this design choice causes an inconsistency between the ``readline`` history and the ``cmd2`` history when you enter an invalid command: it is saved to the ``readline`` history, but not to the ``cmd2`` history. The :data:`cmd2.Cmd.history` attribute, the :class:`cmd2.history.History` class, and the :class:`cmd2.history.HistoryItem` class are all part of the public API for :class:`cmd2.Cmd`. You could use these classes to implement write your own ``history`` command (see below for documentation on how the included ``history`` command works). For Users --------- You can use the up and down arrow keys to move through the history of previously entered commands. If the ``readline`` module is installed, you can press ``Control-p`` to move to the previously entered command, and ``Control-n`` to move to the next command. You can also search through the command history using ``Control-r``. Eric Johnson hosts a nice `readline cheat sheet `_, or you can dig into the `GNU Readline User Manual `_ for all the details, including instructions for customizing the key bindings. ``cmd2`` makes a third type of history access available with the ``history`` command. Each time the user enters a command, ``cmd2`` saves the input. The ``history`` command lets you do interesting things with that saved input. The examples to follow all assume that you have entered the following commands:: (Cmd) alias create one !echo one Alias 'one' created (Cmd) alias create two !echo two Alias 'two' created (Cmd) alias create three !echo three Alias 'three' created (Cmd) alias create four !echo four Alias 'four' created In it's simplest form, the ``history`` command displays previously entered commands. With no additional arguments, it displays all previously entered commands:: (Cmd) history 1 alias create one !echo one 2 alias create two !echo two 3 alias create three !echo three 4 alias create four !echo four If you give a positive integer as an argument, then it only displays the specified command:: (Cmd) history 4 4 alias create four !echo four If you give a negative integer *N* as an argument, then it display the *Nth* last command. For example, if you give ``-1`` it will display the last command you entered. If you give ``-2`` it will display the next to last command you entered, and so forth:: (Cmd) history -2 3 alias create three !echo three You can use a similar mechanism to display a range of commands. Simply give two command numbers separated by ``..`` or ``:``, and you will see all commands between, and including, those two numbers:: (Cmd) history 1:3 1 alias create one !echo one 2 alias create two !echo two 3 alias create three !echo three If you omit the first number, it will start at the beginning. If you omit the last number, it will continue to the end:: (Cmd) history :2 1 alias create one !echo one 2 alias create two !echo two (Cmd) history 2: 2 alias create two !echo two 3 alias create three !echo three 4 alias create four !echo four If you want to display the last three commands entered:: (Cmd) history -- -3: 2 alias create two !echo two 3 alias create three !echo three 4 alias create four !echo four Notice the double dashes. These are required because the history command uses ``argparse`` to parse the command line arguments. As described in the `argparse documentation `_ , ``-3:`` is an option, not an argument: If you have positional arguments that must begin with - and don’t look like negative numbers, you can insert the pseudo-argument '--' which tells parse_args() that everything after that is a positional argument: There is no zeroth command, so don't ask for it. If you are a python programmer, you've probably noticed this looks a lot like the slice syntax for lists and arrays. It is, with the exception that the first history command is 1, where the first element in a python array is 0. Besides selecting previous commands by number, you can also search for them. You can use a simple string search:: (Cmd) history two 2 alias create two !echo two Or a regular expression search by enclosing your regex in slashes:: (Cmd) history '/te\ +th/' 3 alias create three !echo three If your regular expression contains any characters that ``argparse`` finds interesting, like dash or plus, you also need to enclose your regular expression in quotation marks. This all sounds great, but doesn't it seem like a bit of overkill to have all these ways to select commands if all we can do is display them? Turns out, displaying history commands is just the beginning. The history command can perform many other actions: - running previously entered commands - saving previously entered commands to a text file - opening previously entered commands in your favorite text editor - running previously entered commands, saving the commands and their output to a text file - clearing the history of entered commands Each of these actions is invoked using a command line option. The ``-r`` or ``--run`` option runs one or more previously entered commands. To run command number 1:: (Cmd) history --run 1 To rerun the last two commands (there's that double dash again to make argparse stop looking for options):: (Cmd) history -r -- -2: Say you want to re-run some previously entered commands, but you would really like to make a few changes to them before doing so. When you use the ``-e`` or ``--edit`` option, ``history`` will write the selected commands out to a text file, and open that file with a text editor. You make whatever changes, additions, or deletions, you want. When you leave the text editor, all the commands in the file are executed. To edit and then re-run commands 2-4 you would:: (Cmd) history --edit 2:4 If you want to save the commands to a text file, but not edit and re-run them, use the ``-o`` or ``--output-file`` option. This is a great way to create :ref:`Scripts `, which can be executed using the ``run_script`` command. To save the first 5 commands entered in this session to a text file:: (Cmd) history :5 -o history.txt The ``history`` command can also save both the commands and their output to a text file. This is called a transcript. See :ref:`features/transcripts:Transcripts` for more information on how transcripts work, and what you can use them for. To create a transcript use the ``-t`` or ``--transcription`` option:: (Cmd) history 2:3 --transcript transcript.txt The ``--transcript`` option implies ``--run``: the commands must be re-run in order to capture their output to the transcript file. The last action the history command can perform is to clear the command history using ``-c`` or ``--clear``:: (Cmd) history -c In addition to these five actions, the ``history`` command also has some options to control how the output is formatted. With no arguments, the ``history`` command displays the command number before each command. This is great when displaying history to the screen because it gives you an easy reference to identify previously entered commands. However, when creating a script or a transcript, the command numbers would prevent the script from loading properly. The ``-s`` or ``--script`` option instructs the ``history`` command to suppress the line numbers. This option is automatically set by the ``--output_file``, ``--transcript``, and ``--edit`` options. If you want to output the history commands with line numbers to a file, you can do it with output redirection:: (Cmd) history 1:4 > history.txt You might use ``-s`` or ``--script`` on it's own if you want to display history commands to the screen without line numbers, so you can copy them to the clipboard:: (Cmd) history -s 1:3 ``cmd2`` supports both aliases and macros, which allow you to substitute a short, more convenient input string with a longer replacement string. Say we create an alias like this, and then use it:: (Cmd) alias create ls shell ls -aF Alias 'ls' created (Cmd) ls -d h* history.txt htmlcov/ By default, the ``history`` command shows exactly what we typed:: (Cmd) history 1 alias create ls shell ls -aF 2 ls -d h* There are two ways to modify that display so you can see what aliases and macros were expanded to. The first is to use ``-x`` or ``--expanded``. These options show the expanded command instead of the entered command:: (Cmd) history -x 1 alias create ls shell ls -aF 2 shell ls -aF -d h* If you want to see both the entered command and the expanded command, use the ``-v`` or ``--verbose`` option:: (Cmd) history -v 1 alias create ls shell ls -aF 2 ls -d h* 2x shell ls -aF -d h* If the entered command had no expansion, it is displayed as usual. However, if there is some change as the result of expanding macros and aliases, then the entered command is displayed with the number, and the expanded command is displayed with the number followed by an ``x``. cmd2-2.3.3/docs/features/hooks.rst000066400000000000000000000345241416142110700167740ustar00rootroot00000000000000Hooks ===== The typical way of starting a ``cmd2`` application is as follows:: import cmd2 class App(cmd2.Cmd): # customized attributes and methods here if __name__ == '__main__': app = App() app.cmdloop() There are several pre-existing methods and attributes which you can tweak to control the overall behavior of your application before, during, and after the command processing loop. Application Lifecycle Hooks --------------------------- You can run a script on initialization by passing the script filename in the ``startup_script`` parameter of :meth:`cmd2.Cmd.__init__`. You can also register methods to be called at the beginning of the command loop:: class App(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_preloop_hook(self.myhookmethod) def myhookmethod(self) -> None: self.poutput("before the loop begins") To retain backwards compatibility with ``cmd.Cmd``, after all registered preloop hooks have been called, the :meth:`~cmd2.Cmd.preloop` method is called. A similar approach allows you to register functions to be called after the command loop has finished:: class App(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_postloop_hook(self.myhookmethod) def myhookmethod(self) -> None: self.poutput("after the loop ends") To retain backwards compatibility with ``cmd.Cmd``, after all registered postloop hooks have been called, the :meth:`~cmd2.Cmd.postloop` method is called. Preloop and postloop hook methods are not passed any parameters and any return value is ignored. The approach of registering hooks instead of overriding methods allows multiple hooks to be called before the command loop begins or ends. Plugin authors should review :ref:`features/plugins:Hooks` for best practices writing hooks. Application Lifecycle Attributes -------------------------------- There are numerous attributes on :class:`cmd2.Cmd` which affect application behavior upon entering or during the command loop: - :data:`~cmd2.Cmd.intro` - if provided this serves as the intro banner printed once at start of application, after :meth:`~cmd2.Cmd.preloop` is called. - :data:`~cmd2.Cmd.prompt` - see :ref:`features/prompt:Prompt` for more information. - :data:`~cmd2.Cmd.continuation_prompt` - The prompt issued to solicit input for the 2nd and subsequent lines of a :ref:`multiline command ` - :data:`~cmd2.Cmd.echo` - if ``True`` write the prompt and the command into the output stream. In addition, several arguments to :meth:`cmd2.Cmd.__init__` also affect the command loop behavior: - ``allow_cli_args`` - allows commands to be specified on the operating system command line which are executed before the command processing loop begins. - ``transcript_files`` - see :ref:`features/transcripts:Transcripts` for more information - ``startup_script`` - run a script on initialization. See :ref:`features/scripting:Scripting` for more information. Command Processing Loop ----------------------- When you call :meth:`cmd2.Cmd.cmdloop`, the following sequence of events are repeated until the application exits: #. Output the prompt #. Accept user input #. Parse user input into a :class:`~cmd2.Statement` object #. Call methods registered with :meth:`~cmd2.Cmd.register_postparsing_hook()` #. Redirect output, if user asked for it and it's allowed #. Start timer #. Call methods registered with :meth:`~cmd2.Cmd.register_precmd_hook` #. Call :meth:`~cmd2.Cmd.precmd` - for backwards compatibility with ``cmd.Cmd`` #. Add statement to :ref:`features/history:History` #. Call `do_command` method #. Call methods registered with :meth:`~cmd2.Cmd.register_postcmd_hook()` #. Call :meth:`~cmd2.Cmd.postcmd` - for backwards compatibility with ``cmd.Cmd`` #. Stop timer and display the elapsed time #. Stop redirecting output if it was redirected #. Call methods registered with :meth:`~cmd2.Cmd.register_cmdfinalization_hook()` By registering hook methods, steps 4, 8, 12, and 16 allow you to run code during, and control the flow of the command processing loop. Be aware that plugins also utilize these hooks, so there may be code running that is not part of your application. Methods registered for a hook are called in the order they were registered. You can register a function more than once, and it will be called each time it was registered. Postparsing, precommand, and postcommand hook methods share some common ways to influence the command processing loop. If a hook raises an exception: - no more hooks (except command finalization hooks) of any kind will be called - if the command has not yet been executed, it will not be executed - the exception message will be displayed for the user. Specific types of hook methods have additional options as described below. Postparsing Hooks ----------------- Postparsing hooks are called after the user input has been parsed but before execution of the command. These hooks can be used to: - modify the user input - run code before every command executes - cancel execution of the current command - exit the application When postparsing hooks are called, output has not been redirected, nor has the timer for command execution been started. To define and register a postparsing hook, do the following:: class App(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_postparsing_hook(self.myhookmethod) def myhookmethod(self, params: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: # the statement object created from the user input # is available as params.statement return params :meth:`~cmd2.Cmd.register_postparsing_hook` checks the method signature of the passed callable, and raises a ``TypeError`` if it has the wrong number of parameters. It will also raise a ``TypeError`` if the passed parameter and return value are not annotated as ``PostparsingData``. The hook method will be passed one parameter, a :class:`~cmd2.plugin.PostparsingData` object which we will refer to as ``params``. ``params`` contains two attributes. ``params.statement`` is a :class:`~cmd2.Statement` object which describes the parsed user input. There are many useful attributes in the :class:`~cmd2.Statement` object, including ``.raw`` which contains exactly what the user typed. ``params.stop`` is set to ``False`` by default. The hook method must return a :class:`cmd2.plugin.PostparsingData` object, and it is very convenient to just return the object passed into the hook method. The hook method may modify the attributes of the object to influece the behavior of the application. If ``params.stop`` is set to true, a fatal failure is triggered prior to execution of the command, and the application exits. To modify the user input, you create a new :class:`~cmd2.Statement` object and return it in ``params.statement``. Don't try and directly modify the contents of a :class:`~cmd2.Statement` object, there be dragons. Instead, use the various attributes in a :class:`~cmd2.Statement` object to construct a new string, and then parse that string to create a new :class:`~cmd2.Statement` object. :class:`cmd2.Cmd` uses an instance of :class:`~cmd2.parsing.StatementParser` to parse user input. This instance has been configured with the proper command terminators, multiline commands, and other parsing related settings. This instance is available as the :data:`~cmd2.Cmd.statement_parser` attribute. Here's a simple example which shows the proper technique:: def myhookmethod(self, params: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: if not '|' in params.statement.raw: newinput = params.statement.raw + ' | less' params.statement = self.statement_parser.parse(newinput) return params If a postparsing hook returns a :class:`~cmd2.plugin.PostparsingData` object with the :data:`~cmd2.plugin.PostparsingData.stop` attribute set to ``True``: - no more hooks of any kind (except :ref:`features/hooks:Command Finalization Hooks`) will be called - the command will not be executed - no error message will be displayed to the user - the application will exit Precommand Hooks ---------------- Precommand hooks can modify the user input, but cannot request the application terminate. If your hook needs to be able to exit the application, you should implement it as a postparsing hook. Once output is redirected and the timer started, all the hooks registered with :meth:`~cmd2.Cmd.register_precmd_hook` are called. Here's how to do it:: class App(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_precmd_hook(self.myhookmethod) def myhookmethod(self, data: cmd2.plugin.PrecommandData) -> cmd2.plugin.PrecommandData: # the statement object created from the user input # is available as data.statement return data :meth:`~cmd2.Cmd.register_precmd_hook` checks the method signature of the passed callable, and raises a ``TypeError`` if it has the wrong number of parameters. It will also raise a ``TypeError`` if the parameters and return value are not annotated as ``PrecommandData``. You may choose to modify the user input by creating a new :class:`~cmd2.Statement` with different properties (see above). If you do so, assign your new :class:`~cmd2.Statement` object to ``data.statement``. The precommand hook must return a :class:`~cmd2.plugin.PrecommandData` object. You don't have to create this object from scratch, you can just return the one passed into the hook. After all registered precommand hooks have been called, :meth:`~cmd2.Cmd.precmd` will be called. To retain full backward compatibility with ``cmd.Cmd``, this method is passed a :class:`~cmd2.Statement`, not a :class:`~cmd2.plugin.PrecommandData` object. Postcommand Hooks ----------------- Once the command method has returned (i.e. the ``do_command(self, statement) method`` has been called and returns, all postcommand hooks are called. If output was redirected by the user, it is still redirected, and the command timer is still running. Here's how to define and register a postcommand hook:: class App(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_postcmd_hook(self.myhookmethod) def myhookmethod(self, data: cmd2.plugin.PostcommandData) -> cmd2.plugin.PostcommandData: return data Your hook will be passed a :class:`~cmd2.plugin.PostcommandData` object, which has a :data:`~cmd2.plugin.PostcommandData.statement` attribute that describes the command which was executed. If your postcommand hook method gets called, you are guaranteed that the command method was called, and that it didn't raise an exception. If any postcommand hook raises an exception, the exception will be displayed to the user, and no further postcommand hook methods will be called. Command finalization hooks, if any, will be called. After all registered postcommand hooks have been called, ``self.postcmd`` will be called to retain full backward compatibility with ``cmd.Cmd``. If any postcommand hook (registered or ``self.postcmd``) returns a :class:`~cmd2.plugin.PostcommandData` object with the stop attribute set to ``True``, subsequent postcommand hooks will still be called, as will the command finalization hooks, but once those hooks have all been called, the application will terminate. Likewise, if :``self.postcmd`` returns ``True``, the command finalization hooks will be called before the application terminates. Any postcommand hook can change the value of the ``stop`` attribute before returning it, and the modified value will be passed to the next postcommand hook. The value returned by the final postcommand hook will be passed to the command finalization hooks, which may further modify the value. If your hook blindly returns ``False``, a prior hook's requst to exit the application will not be honored. It's best to return the value you were passed unless you have a compelling reason to do otherwise. To purposefully and silently skip postcommand hooks, commands can raise any of of the following exceptions. - :attr:`cmd2.exceptions.SkipPostcommandHooks` - :attr:`cmd2.exceptions.Cmd2ArgparseError` Command Finalization Hooks -------------------------- Command finalization hooks are called even if one of the other types of hooks or the command method raise an exception. Here's how to create and register a command finalization hook:: class App(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_cmdfinalization_hook(self.myhookmethod) def myhookmethod(self, data: cmd2.plugin.CommandFinalizationData) -> cmd2.plugin.CommandFinalizationData: return data Command Finalization hooks must check whether the :data:`~cmd2.plugin.CommandFinalizationData.statement` attribute of the passed :class:`~cmd2.plugin.CommandFinalizationData` object contains a value. There are certain circumstances where these hooks may be called before the user input has been parsed, so you can't always rely on having a :data:`~cmd2.plugin.CommandFinalizationData.statement`. If any prior postparsing or precommand hook has requested the application to terminate, the value of the :data:`~cmd2.plugin.CommandFinalizationData.stop` attribute passed to the first command finalization hook will be ``True``. Any command finalization hook can change the value of the :data:`~cmd2.plugin.CommandFinalizationData.stop` attribute before returning it, and the modified value will be passed to the next command finalization hook. The value returned by the final command finalization hook will determine whether the application terminates or not. This approach to command finalization hooks can be powerful, but it can also cause problems. If your hook blindly returns ``False``, a prior hook's requst to exit the application will not be honored. It's best to return the value you were passed unless you have a compelling reason to do otherwise. If any command finalization hook raises an exception, no more command finalization hooks will be called. If the last hook to return a value returned ``True``, then the exception will be rendered, and the application will terminate. cmd2-2.3.3/docs/features/index.rst000066400000000000000000000007071416142110700167540ustar00rootroot00000000000000Features ======== .. toctree:: :maxdepth: 1 argument_processing builtin_commands clipboard commands completion disable_commands embedded_python_shells generating_output help history hooks initialization misc modular_commands multiline_commands os packaging plugins prompt redirection scripting settings shortcuts_aliases_macros startup_commands table_creation transcripts cmd2-2.3.3/docs/features/initialization.rst000066400000000000000000000155121416142110700206740ustar00rootroot00000000000000Initialization ============== Here is a basic example ``cmd2`` application which demonstrates many capabilities which you may wish to utilize while initializing the app:: #!/usr/bin/env python3 # coding=utf-8 """A simple example cmd2 application demonstrating the following: 1) Colorizing/stylizing output 2) Using multiline commands 3) Persistent history 4) How to run an initialization script at startup 5) How to group and categorize commands when displaying them in help 6) Opting-in to using the ipy command to run an IPython shell 7) Allowing access to your application in py and ipy 8) Displaying an intro banner upon starting your application 9) Using a custom prompt 10) How to make custom attributes settable at runtime """ import cmd2 from cmd2 import ( Bg, Fg, style, ) class BasicApp(cmd2.Cmd): CUSTOM_CATEGORY = 'My Custom Commands' def __init__(self): super().__init__( multiline_commands=['echo'], persistent_history_file='cmd2_history.dat', startup_script='scripts/startup.txt', include_ipy=True, ) # Prints an intro banner once upon application startup self.intro = style('Welcome to cmd2!', fg=Fg.RED, bg=Bg.WHITE, bold=True) # Show this as the prompt when asking for input self.prompt = 'myapp> ' # Used as prompt for multiline commands after the first line self.continuation_prompt = '... ' # Allow access to your application in py and ipy via self self.self_in_py = True # Set the default category name self.default_category = 'cmd2 Built-in Commands' # Color to output text in with echo command self.foreground_color = Fg.CYAN.name.lower() # Make echo_fg settable at runtime fg_colors = [c.name.lower() for c in Fg] self.add_settable( cmd2.Settable('foreground_color', str, 'Foreground color to use with echo command', self, choices=fg_colors) ) @cmd2.with_category(CUSTOM_CATEGORY) def do_intro(self, _): """Display the intro banner""" self.poutput(self.intro) @cmd2.with_category(CUSTOM_CATEGORY) def do_echo(self, arg): """Example of a multiline command""" fg_color = Fg[self.foreground_color.upper()] self.poutput(style(arg, fg=fg_color)) if __name__ == '__main__': app = BasicApp() app.cmdloop() Cmd class initializer --------------------- A ``cmd2.Cmd`` instance or subclass instance is an interactive CLI application framework. There is no good reason to instantiate ``Cmd`` itself; rather, it’s useful as a superclass of a class you define yourself in order to inherit ``Cmd``’s methods and encapsulate action methods. Certain things must be initialized within the ``__init__()`` method of your class derived from ``cmd2.Cmd``(all arguments to ``__init__()`` are optional): .. automethod:: cmd2.Cmd.__init__ :noindex: Cmd instance attributes ----------------------- The ``cmd2.Cmd`` class provides a large number of public instance attributes which allow developers to customize a ``cmd2`` application further beyond the options provided by the ``__init__()`` method. Public instance attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~ Here are instance attributes of ``cmd2.Cmd`` which developers might wish override: - **broken_pipe_warning**: if non-empty, this string will be displayed if a broken pipe error occurs - **continuation_prompt**: used for multiline commands on 2nd+ line of input - **debug**: if ``True`` show full stack trace on error (Default: ``False``) - **default_category**: if any command has been categorized, then all other commands that haven't been categorized will display under this section in the help output. - **default_error**: the error that prints when a non-existent command is run - **default_sort_key**: the default key for sorting string results. Its default value performs a case-insensitive alphabetical sort. - **default_to_shell**: if ``True`` attempt to run unrecognized commands as shell commands (Default: ``False``) - **disabled_commands**: commands that have been disabled from use. This is to support commands that are only available during specific states of the application. This dictionary's keys are the command names and its values are DisabledCommand objects. - **doc_header**: Set the header used for the help function's listing of documented functions - **echo**: if ``True``, each command the user issues will be repeated to the screen before it is executed. This is particularly useful when running scripts. This behavior does not occur when running a command at the prompt. (Default: ``False``) - **editor**: text editor program to use with *edit* command (e.g. ``vim``) - **exclude_from_history**: commands to exclude from the *history* command - **exit_code**: this determines the value returned by ``cmdloop()`` when exiting the application - **feedback_to_output**: if ``True`` send nonessential output to stdout, if ``False`` send them to stderr (Default: ``False``) - **help_error**: the error that prints when no help information can be found - **hidden_commands**: commands to exclude from the help menu and tab completion - **last_result**: stores results from the last command run to enable usage of results in a Python script or interactive console. Built-in commands don't make use of this. It is purely there for user-defined commands and convenience. - **self_in_py**: if ``True`` allow access to your application in *py* command via ``self`` (Default: ``False``) - **macros**: dictionary of macro names and their values - **max_completion_items**: max number of CompletionItems to display during tab completion (Default: 50) - **pager**: sets the pager command used by the ``Cmd.ppaged()`` method for displaying wrapped output using a pager - **pager_chop**: sets the pager command used by the ``Cmd.ppaged()`` method for displaying chopped/truncated output using a pager - **py_bridge_name**: name by which embedded Python environments and scripts refer to the ``cmd2`` application by in order to call commands (Default: ``app``) - **py_locals**: dictionary that defines specific variables/functions available in Python shells and scripts (provides more fine-grained control than making everything available with **self_in_py**) - **quiet**: if ``True`` then completely suppress nonessential output (Default: ``False``) - **settable**: dictionary that controls which of these instance attributes are settable at runtime using the *set* command - **timing**: if ``True`` display execution time for each command (Default: ``False``) cmd2-2.3.3/docs/features/misc.rst000066400000000000000000000050731416142110700166010ustar00rootroot00000000000000Miscellaneous Features ====================== Timer ----- Turn the timer setting on, and ``cmd2`` will show the wall time it takes for each command to execute. Exiting ------- Mention quit, and EOF handling built into ``cmd2``. select ------ Presents numbered options to user, as bash ``select``. ``app.select`` is called from within a method (not by the user directly; it is ``app.select``, not ``app.do_select``). .. automethod:: cmd2.Cmd.select :noindex: :: def do_eat(self, arg): sauce = self.select('sweet salty', 'Sauce? ') result = '{food} with {sauce} sauce, yum!' result = result.format(food=arg, sauce=sauce) self.stdout.write(result + '\n') :: (Cmd) eat wheaties 1. sweet 2. salty Sauce? 2 wheaties with salty sauce, yum! Disabling Commands ------------------ ``cmd2`` supports disabling commands during runtime. This is useful if certain commands should only be available when the application is in a specific state. When a command is disabled, it will not show up in the help menu or tab complete. If a user tries to run the command, a command-specific message supplied by the developer will be printed. The following functions support this feature. enable_command() Enable an individual command enable_category() Enable an entire category of commands disable_command() Disable an individual command and set the message that will print when this command is run or help is called on it while disabled disable_category() Disable an entire category of commands and set the message that will print when anything in this category is run or help is called on it while disabled See the definitions of these functions for descriptions of their arguments. See the ``do_enable_commands()`` and ``do_disable_commands()`` functions in the HelpCategories_ example for a demonstration. .. _HelpCategories: https://github.com/python-cmd2/cmd2/blob/master/examples/help_categories.py Default to shell ---------------- Every ``cmd2`` application can execute operating-system level (shell) commands with ``shell`` or a ``!`` shortcut:: (Cmd) shell which python /usr/bin/python (Cmd) !which python /usr/bin/python However, if the parameter ``default_to_shell`` is ``True``, then *every* command will be attempted on the operating system. Only if that attempt fails (i.e., produces a nonzero return value) will the application's own ``default`` method be called. :: (Cmd) which python /usr/bin/python (Cmd) my dog has fleas sh: my: not found *** Unknown syntax: my dog has fleas cmd2-2.3.3/docs/features/modular_commands.rst000066400000000000000000000325301416142110700211700ustar00rootroot00000000000000Modular Commands ================ Overview -------- Cmd2 also enables developers to modularize their command definitions into ``CommandSet`` objects. CommandSets represent a logical grouping of commands within an cmd2 application. By default, all CommandSets will be discovered and loaded automatically when the cmd2.Cmd class is instantiated with this mixin. This also enables the developer to dynamically add/remove commands from the cmd2 application. This could be useful for loadable plugins that add additional capabilities. Additionally, it allows for object-oriented encapsulation and garbage collection of state that is specific to a CommandSet. Features ~~~~~~~~ * Modular Command Sets - Commands can be broken into separate modules rather than in one god class holding all commands. * Automatic Command Discovery - In your application, merely defining and importing a CommandSet is sufficient for cmd2 to discover and load your command. No manual registration is necessary. * Dynamically Loadable/Unloadable Commands - Command functions and CommandSets can both be loaded and unloaded dynamically during application execution. This can enable features such as dynamically loaded modules that add additional commands. * Events handlers - Four event handlers are provided in ``CommandSet`` class for custom initialization and cleanup steps. See :ref:`features/modular_commands:Event Handlers`. * Subcommand Injection - Subcommands can be defined separately from the base command. This allows for a more action-centric instead of object-centric command system while still organizing your code and handlers around the objects being managed. See API documentation for :attr:`cmd2.command_definition.CommandSet` See [the examples](https://github.com/python-cmd2/cmd2/tree/master/examples/modular_commands) for more details. Defining Commands ----------------- Command Sets ~~~~~~~~~~~~~ CommandSets group multiple commands together. The plugin will inspect functions within a ``CommandSet`` using the same rules as when they're defined in ``cmd2.Cmd``. Commands must be prefixed with ``do_``, help functions with ``help_``, and completer functions with ``complete_``. A new decorator ``with_default_category`` is provided to categorize all commands within a CommandSet in the same command category. Individual commands in a CommandSet may be override the default category by specifying a specific category with ``cmd2.with_category``. CommandSet command methods will always expect the same parameters as when defined in a ``cmd2.Cmd`` sub-class, except that ``self`` will now refer to the ``CommandSet`` instead of the cmd2 instance. The cmd2 instance can be accessed through ``self._cmd`` that is populated when the ``CommandSet`` is registered. CommandSets will only be auto-loaded if the constructor takes no arguments. If you need to provide constructor arguments, see :ref:`features/modular_commands:Manual CommandSet Construction` .. code-block:: python import cmd2 from cmd2 import CommandSet, with_default_category @with_default_category('My Category') class AutoLoadCommandSet(CommandSet): def __init__(self): super().__init__() def do_hello(self, _: cmd2.Statement): self._cmd.poutput('Hello') def do_world(self, _: cmd2.Statement): self._cmd.poutput('World') class ExampleApp(cmd2.Cmd): """ CommandSets are automatically loaded. Nothing needs to be done. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def do_something(self, arg): self.poutput('this is the something command') Manual CommandSet Construction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a CommandSet class requires parameters to be provided to the constructor, you man manually construct CommandSets and pass in the constructor to Cmd2. .. code-block:: python import cmd2 from cmd2 import CommandSet, with_default_category @with_default_category('My Category') class CustomInitCommandSet(CommandSet): def __init__(self, arg1, arg2): super().__init__() self._arg1 = arg1 self._arg2 = arg2 def do_show_arg1(self, _: cmd2.Statement): self._cmd.poutput('Arg1: ' + self._arg1) def do_show_arg2(self, _: cmd2.Statement): self._cmd.poutput('Arg2: ' + self._arg2) class ExampleApp(cmd2.Cmd): """ CommandSets with constructor parameters are provided in the constructor """ def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, **kwargs) def do_something(self, arg): self.last_result = 5 self.poutput('this is the something command') def main(): my_commands = CustomInitCommandSet(1, 2) app = ExampleApp(command_sets=[my_commands]) app.cmdloop() Dynamic Commands ~~~~~~~~~~~~~~~~ You can also dynamically load and unload commands by installing and removing CommandSets at runtime. For example, if you could support runtime loadable plugins or add/remove commands based on your state. You may need to disable command auto-loading if you need dynamically load commands at runtime. .. code-block:: python import argparse import cmd2 from cmd2 import CommandSet, with_argparser, with_category, with_default_category @with_default_category('Fruits') class LoadableFruits(CommandSet): def __init__(self): super().__init__() def do_apple(self, _: cmd2.Statement): self._cmd.poutput('Apple') def do_banana(self, _: cmd2.Statement): self._cmd.poutput('Banana') @with_default_category('Vegetables') class LoadableVegetables(CommandSet): def __init__(self): super().__init__() def do_arugula(self, _: cmd2.Statement): self._cmd.poutput('Arugula') def do_bokchoy(self, _: cmd2.Statement): self._cmd.poutput('Bok Choy') class ExampleApp(cmd2.Cmd): """ CommandSets are loaded via the `load` and `unload` commands """ def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, auto_load_commands=False, **kwargs) self._fruits = LoadableFruits() self._vegetables = LoadableVegetables() load_parser = cmd2.Cmd2ArgumentParser() load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) @with_argparser(load_parser) @with_category('Command Loading') def do_load(self, ns: argparse.Namespace): if ns.cmds == 'fruits': try: self.register_command_set(self._fruits) self.poutput('Fruits loaded') except ValueError: self.poutput('Fruits already loaded') if ns.cmds == 'vegetables': try: self.register_command_set(self._vegetables) self.poutput('Vegetables loaded') except ValueError: self.poutput('Vegetables already loaded') @with_argparser(load_parser) def do_unload(self, ns: argparse.Namespace): if ns.cmds == 'fruits': self.unregister_command_set(self._fruits) self.poutput('Fruits unloaded') if ns.cmds == 'vegetables': self.unregister_command_set(self._vegetables) self.poutput('Vegetables unloaded') if __name__ == '__main__': app = ExampleApp() app.cmdloop() Event Handlers -------------- The following functions are called at different points in the ``CommandSet`` life cycle. ``on_register(self, cmd) -> None`` - Called by cmd2.Cmd as the first step to registering a CommandSet. The commands defined in this class have not be added to the CLI object at this point. Subclasses can override this to perform any initialization requiring access to the Cmd object (e.g. configure commands and their parsers based on CLI state data). ``on_registered(self) -> None`` - Called by cmd2.Cmd after a CommandSet is registered and all its commands have been added to the CLI. Subclasses can override this to perform custom steps related to the newly added commands (e.g. setting them to a disabled state). ``on_unregister(self) -> None`` - Called by ``cmd2.Cmd`` as the first step to unregistering a CommandSet. Subclasses can override this to perform any cleanup steps which require their commands being registered in the CLI. ``on_unregistered(self) -> None`` - Called by ``cmd2.Cmd`` after a CommandSet has been unregistered and all its commands removed from the CLI. Subclasses can override this to perform remaining cleanup steps. Injecting Subcommands ---------------------- Description ~~~~~~~~~~~ Using the `with_argparse` decorator, it is possible to define subcommands for your command. This has a tendency to either drive your interface into an object-centric interface. For example, imagine you have a tool that manages your media collection and you want to manage movies or shows. An object-centric approach would push you to have base commands such as `movies` and `shows` which each have subcommands `add`, `edit`, `list`, `delete`. If you wanted to present an action-centric command set, so that `add`, `edit`, `list`, and `delete` are the base commands, you'd have to organize your code around these similar actions rather than organizing your code around similar objects being managed. Subcommand injection allows you to inject subcommands into a base command to present an interface that is sensible to a user while still organizing your code in whatever structure make more logical sense to the developer. Example ~~~~~~~ This example is a variation on the Dynamic Commands example above. A `cut` command is introduced as a base command and each CommandSet .. code-block:: python import argparse import cmd2 from cmd2 import CommandSet, with_argparser, with_category, with_default_category @with_default_category('Fruits') class LoadableFruits(CommandSet): def __init__(self): super().__init__() def do_apple(self, _: cmd2.Statement): self._cmd.poutput('Apple') banana_parser = cmd2.Cmd2ArgumentParser() banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) @cmd2.as_subcommand_to('cut', 'banana', banana_parser) def cut_banana(self, ns: argparse.Namespace): """Cut banana""" self._cmd.poutput('cutting banana: ' + ns.direction) @with_default_category('Vegetables') class LoadableVegetables(CommandSet): def __init__(self): super().__init__() def do_arugula(self, _: cmd2.Statement): self._cmd.poutput('Arugula') bokchoy_parser = cmd2.Cmd2ArgumentParser() bokchoy_parser.add_argument('style', choices=['quartered', 'diced']) @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser) def cut_bokchoy(self, _: argparse.Namespace): self._cmd.poutput('Bok Choy') class ExampleApp(cmd2.Cmd): """ CommandSets are automatically loaded. Nothing needs to be done. """ def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, auto_load_commands=False, **kwargs) self._fruits = LoadableFruits() self._vegetables = LoadableVegetables() load_parser = cmd2.Cmd2ArgumentParser() load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) @with_argparser(load_parser) @with_category('Command Loading') def do_load(self, ns: argparse.Namespace): if ns.cmds == 'fruits': try: self.register_command_set(self._fruits) self.poutput('Fruits loaded') except ValueError: self.poutput('Fruits already loaded') if ns.cmds == 'vegetables': try: self.register_command_set(self._vegetables) self.poutput('Vegetables loaded') except ValueError: self.poutput('Vegetables already loaded') @with_argparser(load_parser) def do_unload(self, ns: argparse.Namespace): if ns.cmds == 'fruits': self.unregister_command_set(self._fruits) self.poutput('Fruits unloaded') if ns.cmds == 'vegetables': self.unregister_command_set(self._vegetables) self.poutput('Vegetables unloaded') cut_parser = cmd2.Cmd2ArgumentParser() cut_subparsers = cut_parser.add_subparsers(title='item', help='item to cut') @with_argparser(cut_parser) def do_cut(self, ns: argparse.Namespace): handler = ns.cmd2_handler.get() if handler is not None: # Call whatever subcommand function was selected handler(ns) else: # No subcommand was provided, so call help self.poutput('This command does nothing without sub-parsers registered') self.do_help('cut') if __name__ == '__main__': app = ExampleApp() app.cmdloop() cmd2-2.3.3/docs/features/multiline_commands.rst000066400000000000000000000032171416142110700215270ustar00rootroot00000000000000Multiline Commands ================== Command input may span multiple lines for the commands whose names are listed in the ``multiline_commands`` argument to ``cmd2.Cmd.__init__()``. These commands will be executed only after the user has entered a *terminator*. By default, the command terminator is ``;``; specifying the ``terminators`` optional argument to ``cmd2.Cmd.__init__()`` allows different terminators. A blank line is *always* considered a command terminator (cannot be overridden). In multiline commands, output redirection characters like ``>`` and ``|`` are part of the command arguments unless they appear after the terminator. Continuation prompt ------------------- When a user types a :ref:`Multiline Command ` it may span more than one line of input. The prompt for the first line of input is specified by the :attr:`cmd2.Cmd.prompt` instance attribute - see :ref:`features/prompt:Customizing the Prompt`. The prompt for subsequent lines of input is defined by the :attr:`cmd2.Cmd.continuation_prompt` attribute. Use cases --------- Multiline commands should probably be used sparingly in order to preserve a good user experience for your ``cmd2``-based line-oriented command interpreter application. However, some use cases benefit significantly from the ability to have commands that span more than one line. For example, you might want the ability for your user to type in a SQL command, which can often span lines and which are terminated with a semicolon. We estimate that less than 5 percent of ``cmd2`` applications use this feature. But it is here for those uses cases where it provides value. cmd2-2.3.3/docs/features/os.rst000066400000000000000000000143061416142110700162660ustar00rootroot00000000000000Integrating with the OS ======================= How to redirect output ---------------------- See :ref:`features/redirection:Output Redirection and Pipes` Executing OS commands from within ``cmd2`` ------------------------------------------ ``cmd2`` includes a ``shell`` command which executes it's arguments in the operating system shell:: (Cmd) shell ls -al If you use the default :ref:`features/shortcuts_aliases_macros:Shortcuts` defined in ``cmd2`` you'll get a ``!`` shortcut for ``shell``, which allows you to type:: (Cmd) !ls -al NOTE: ``cmd2`` provides user-friendly tab completion throughout the process of running a shell command - first for the shell command name itself, and then for file paths in the argument section. Editors ------- ``cmd2`` includes the built-in ``edit`` command which runs a text editor and optionally opens a file with it:: (Cmd) edit foo.txt The editor used is determined by the ``editor`` settable parameter and can be either a text editor such as **vim** or a graphical editor such as **VSCode**. To set it:: set editor If you have the ``EDITOR`` environment variable set, then this will be the default value for ``editor``. If not, then ``cmd2`` will attempt to search for any in a list of common editors for your operating system. Terminal pagers --------------- Output of any command can be displayed one page at a time using the :meth:`~.cmd2.Cmd.ppaged` method. Alternatively, a terminal pager can be invoked directly using the ability to run shell commands with the ``!`` shortcut like so:: (Cmd) !less foo.txt NOTE: Once you are in a terminal pager, that program temporarily has control of your terminal, **NOT** ``cmd2``. Typically you can use either the arrow keys or / keys to scroll around or type ``q`` to quit the pager and return control to your ``cmd2`` application. Exit codes ---------- The ``self.exit_code`` attribute of your ``cmd2`` application controls what exit code is returned from ``cmdloop()`` when it completes. It is your job to make sure that this exit code gets sent to the shell when your application exits by calling ``sys.exit(app.cmdloop())``. Invoking With Arguments ----------------------- Typically you would invoke a ``cmd2`` program by typing:: $ python mycmd2program.py or:: $ mycmd2program.py Either of these methods will launch your program and enter the ``cmd2`` command loop, which allows the user to enter commands, which are then executed by your program. You may want to execute commands in your program without prompting the user for any input. There are several ways you might accomplish this task. The easiest one is to pipe commands and their arguments into your program via standard input. You don't need to do anything to your program in order to use this technique. Here's a demonstration using the ``examples/example.py`` included in the source code of ``cmd2``:: $ echo "speak -p some words" | python examples/example.py omesay ordsway Using this same approach you could create a text file containing the commands you would like to run, one command per line in the file. Say your file was called ``somecmds.txt``. To run the commands in the text file using your ``cmd2`` program (from a Windows command prompt):: c:\cmd2> type somecmds.txt | python.exe examples/example.py omesay ordsway By default, ``cmd2`` programs also look for commands pass as arguments from the operating system shell, and execute those commands before entering the command loop:: $ python examples/example.py help Documented commands (use 'help -v' for verbose/'help ' for details): =========================================================================== alias help macro orate quit run_script set shortcuts edit history mumble py run_pyscript say shell speak (Cmd) You may need more control over command line arguments passed from the operating system shell. For example, you might have a command inside your ``cmd2`` program which itself accepts arguments, and maybe even option strings. Say you wanted to run the ``speak`` command from the operating system shell, but have it say it in pig latin:: $ python example/example.py speak -p hello there python example.py speak -p hello there usage: speak [-h] [-p] [-s] [-r REPEAT] words [words ...] speak: error: the following arguments are required: words *** Unknown syntax: -p *** Unknown syntax: hello *** Unknown syntax: there (Cmd) Uh-oh, that's not what we wanted. ``cmd2`` treated ``-p``, ``hello``, and ``there`` as commands, which don't exist in that program, thus the syntax errors. There is an easy way around this, which is demonstrated in ``examples/cmd_as_argument.py``. By setting ``allow_cli_args=False`` you can so your own argument parsing of the command line:: $ python examples/cmd_as_argument.py speak -p hello there ellohay heretay Check the source code of this example, especially the ``main()`` function, to see the technique. Alternatively you can simply wrap the command plus arguments in quotes (either single or double quotes):: $ python example/example.py "speak -p hello there" ellohay heretay (Cmd) Automating cmd2 apps from other CLI/CLU tools ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ While ``cmd2`` is designed to create **interactive** command-line applications which enter a Read-Evaluate-Print-Loop (REPL), there are a great many times when it would be useful to use a ``cmd2`` application as a run-and-done command-line utility for purposes of automation and scripting. This is easily achieved by combining the following capabilities of ``cmd2``: #. Ability to invoke a ``cmd2`` application with arguments #. Ability to set an exit code when leaving a ``cmd2`` application #. Ability to exit a ``cmd2`` application with the ``quit`` command Here is a simple example which doesn't require the quit command since the custom ``exit`` command quits while returning an exit code:: $ python examples/exit_code.py "exit 23" 'examples/exit_code.py' exiting with code: 23 $ echo $? 23 Here is another example using ``quit``:: $ python example/example.py "speak -p hello there" quit ellohay heretay $ cmd2-2.3.3/docs/features/packaging.rst000066400000000000000000000035601416142110700175710ustar00rootroot00000000000000 Packaging a cmd2 application for distribution ================================================= As a general-purpose tool for building interactive command-line applications, ``cmd2`` is designed to be used in many ways. How you distribute your ``cmd2`` application to customers or end users is up to you. See the `Overview of Packaging for Python`_ from the Python Packaging Authority for a thorough discussion of the extensive options within the Python ecosystem. For developers wishing to package a ``cmd2`` application into a single binary image or compressed file, we can recommend all of the following based on personal and professional experience: * Deploy your ``cmd2`` Python app using Docker_ * Powerful and flexible - allows you to control entire user space and setup other applications like databases * As long as it isn't problematic for your customers to have Docker installed, then this is probably the best option * PyInstaller_ * Quick and easy - it "just works" and everything you need is installable via ``pip`` * Packages up all of the dependencies into a single directory which you can then zip up * Nuitka_ * Converts your Python to C and compiles it to a native binary file * This can be particularly convenient if you wish to obfuscate the Python source code behind your application * Recommend invoking with ``--follow-imports`` flag like: ``python3 -m nuitka --follow-imports your_app.py`` * `Conda Constructor`_ * Allows you to create a custom Python distro based on Miniconda_ .. _`Overview of Packaging for Python`: https://packaging.python.org/overview/ .. _Docker: https://djangostars.com/blog/what-is-docker-and-how-to-use-it-with-python/ .. _PyInstaller: https://www.pyinstaller.org .. _Nuitka: https://nuitka.net .. _`Conda Constructor`: https://github.com/conda/constructor .. _Miniconda: https://docs.conda.io/en/latest/miniconda.html cmd2-2.3.3/docs/features/plugins.rst000066400000000000000000000127251416142110700173310ustar00rootroot00000000000000Plugins ======= ``cmd2`` has a built-in plugin framework which allows developers to create a a ``cmd2`` plugin which can extend basic ``cmd2`` functionality and can be used by multiple applications. There are many ways to add functionality to ``cmd2`` using a plugin. Most plugins will be implemented as a mixin. A mixin is a class that encapsulates and injects code into another class. Developers who use a plugin in their ``cmd2`` project will inject the plugin's code into their subclass of :class:`cmd2.Cmd`. Mixin and Initialization ------------------------ The following short example shows how to mix in a plugin and how the plugin gets initialized. Here's the plugin:: class MyPlugin: def __init__(self, *args, **kwargs): # code placed here runs before cmd2.Cmd initializes super().__init__(*args, **kwargs) # code placed here runs after cmd2.Cmd initializes and an example app which uses the plugin:: import cmd2 import cmd2_myplugin class Example(cmd2_myplugin.MyPlugin, cmd2.Cmd): """An class to show how to use a plugin""" def __init__(self, *args, **kwargs): # code placed here runs before cmd2.Cmd or # any plugins initialize super().__init__(*args, **kwargs) # code placed here runs after cmd2.Cmd and # all plugins have initialized Note how the plugin must be inherited (or mixed in) before :class:`cmd2.Cmd`. This is required for two reasons: - The ``cmd.Cmd.__init__`` method in the python standard library does not call ``super().__init__()``. Because of this oversight, if you don't inherit from ``MyPlugin`` first, the ``MyPlugin.__init__()`` method will never be called. - You may want your plugin to be able to override methods from :class:`cmd2.Cmd`. If you mixin the plugin after ``cmd2.Cmd``, the python method resolution order will call :class:`cmd2.Cmd` methods before it calls those in your plugin. Add commands ------------ Your plugin can add user visible commands. You do it the same way in a plugin that you would in a :class:`cmd2.Cmd` app:: class MyPlugin: def do_say(self, statement): """Simple say command""" self.poutput(statement) You have all the same capabilities within the plugin that you do inside a :class:`cmd2.Cmd` app, including argument parsing via decorators and custom help methods. Add (or hide) settings ---------------------- A plugin may add user controllable settings to the application. Here's an example:: class MyPlugin: def __init__(self, *args, **kwargs): # code placed here runs before cmd2.Cmd initializes super().__init__(*args, **kwargs) # code placed here runs after cmd2.Cmd initializes self.mysetting = 'somevalue' self.add_settable(cmd2.Settable('mysetting', str, 'short help message for mysetting', self)) You can hide settings from the user by calling :meth:`~cmd2.Cmd.remove_settable`. See :ref:`features/settings:Settings` for more information. Decorators ---------- Your plugin can provide a decorator which users of your plugin can use to wrap functionality around their own commands. Override methods ---------------- Your plugin can override core :class:`cmd2.Cmd` methods, changing their behavior. This approach should be used sparingly, because it is very brittle. If a developer chooses to use multiple plugins in their application, and several of the plugins override the same method, only the first plugin to be mixed in will have the overridden method called. Hooks are a much better approach. Hooks ----- Plugins can register hook methods, which are called by :class:`cmd2.Cmd` during various points in the application and command processing lifecycle. Plugins should not override any of the deprecated hook methods, instead they should register their hooks as described in the :ref:`features/hooks:Hooks` section. You should name your hooks so that they begin with the name of your plugin. Hook methods get mixed into the ``cmd2`` application and this naming convention helps avoid unintentional method overriding. Here's a simple example:: class MyPlugin: def __init__(self, *args, **kwargs): # code placed here runs before cmd2 initializes super().__init__(*args, **kwargs) # code placed here runs after cmd2 initializes # this is where you register any hook functions self.register_postparsing_hook(self.cmd2_myplugin_postparsing_hook) def cmd2_myplugin_postparsing_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """Method to be called after parsing user input, but before running the command""" self.poutput('in postparsing_hook') return data Registration allows multiple plugins (or even the application itself) to each inject code to be called during the application or command processing lifecycle. See the :ref:`features/hooks:Hooks` documentation for full details of the application and command lifecycle, including all available hooks and the ways hooks can influence the lifecycle. Classes and Functions --------------------- Your plugin can also provide classes and functions which can be used by developers of ``cmd2`` based applications. Describe these classes and functions in your documentation so users of your plugin will know what's available. Examples -------- See ``_ for more info. cmd2-2.3.3/docs/features/prompt.rst000066400000000000000000000047451416142110700171740ustar00rootroot00000000000000Prompt ====== ``cmd2`` issues a configurable prompt before soliciting user input. Customizing the Prompt ---------------------- This prompt can be configured by setting the :attr:`cmd2.Cmd.prompt` instance attribute. This contains the string which should be printed as a prompt for user input. See the Pirate_ example for the simple use case of statically setting the prompt. .. _Pirate: https://github.com/python-cmd2/cmd2/blob/master/examples/pirate.py#L33 Continuation Prompt ------------------- When a user types a :ref:`Multiline Command ` it may span more than one line of input. The prompt for the first line of input is specified by the :attr:`cmd2.Cmd.prompt` instance attribute. The prompt for subsequent lines of input is defined by the :attr:`cmd2.Cmd.continuation_prompt` attribute.See the Initialization_ example for a demonstration of customizing the continuation prompt. .. _Initialization: https://github.com/python-cmd2/cmd2/blob/master/examples/initialization.py#L33 Updating the prompt ------------------- If you wish to update the prompt between commands, you can do so using one of the :ref:`Application Lifecycle Hooks ` such as a :ref:`Postcommand hook `. See PythonScripting_ for an example of dynamically updating the prompt. .. _PythonScripting: https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py#L34-L48 Asynchronous Feedback --------------------- ``cmd2`` provides two functions to provide asynchronous feedback to the user without interfering with the command line. This means the feedback is provided to the user when they are still entering text at the prompt. To use this functionality, the application must be running in a terminal that supports VT100 control characters and readline. Linux, Mac, and Windows 10 and greater all support these. .. automethod:: cmd2.Cmd.async_alert :noindex: .. automethod:: cmd2.Cmd.async_update_prompt :noindex: ``cmd2`` also provides a function to change the title of the terminal window. This feature requires the application be running in a terminal that supports VT100 control characters. Linux, Mac, and Windows 10 and greater all support these. .. automethod:: cmd2.Cmd.set_window_title :noindex: The easiest way to understand these functions is to see the AsyncPrinting_ example for a demonstration. .. _AsyncPrinting: https://github.com/python-cmd2/cmd2/blob/master/examples/async_printing.py cmd2-2.3.3/docs/features/redirection.rst000066400000000000000000000055221416142110700201540ustar00rootroot00000000000000Output Redirection and Pipes ============================ As in POSIX shells, output of a command can be redirected and/or piped. This feature is fully cross-platform and works identically on Windows, macOS, and Linux. Output Redirection ------------------ Redirect to a file ~~~~~~~~~~~~~~~~~~ Redirecting the output of a ``cmd2`` command to a file works just like in POSIX shells: - send to a file with ``>``, as in ``mycommand args > filename.txt`` - append to a file with ``>>``, as in ``mycommand args >> filename.txt`` If you need to include any of these redirection characters in your command, you can enclose them in quotation marks, ``mycommand 'with > in the argument'``. Redirect to the clipboard ~~~~~~~~~~~~~~~~~~~~~~~~~ ``cmd2`` output redirection supports an additional feature not found in most shells - if the file name following the ``>`` or ``>>`` is left blank, then the output is redirected to the operating system clipboard so that it can then be pasted into another program. - overwrite the clipboard with ``mycommand args >`` - append to the clipboard with ``mycommand args >>`` Pipes ----- Piping the output of a ``cmd2`` command to a shell command works just like in POSIX shells: - pipe as input to a shell command with ``|``, as in ``mycommand args | wc`` Multiple Pipes and Redirection ------------------------------ Multiple pipes, optionally followed by a redirect, are supported. Thus, it is possible to do something like the following:: (Cmd) help | grep py | wc > output.txt The above runs the **help** command, pipes its output to **grep** searching for any lines containing *py*, then pipes the output of grep to the **wc** "word count" command, and finally writes redirects the output of that to a file called *output.txt*. Disabling Redirection --------------------- .. note:: If you wish to disable cmd2's output redirection and pipes features, you can do so by setting the ``allow_redirection`` attribute of your ``cmd2.Cmd`` class instance to ``False``. This would be useful, for example, if you want to restrict the ability for an end user to write to disk or interact with shell commands for security reasons:: from cmd2 import Cmd class App(Cmd): def __init__(self): super().__init__(allow_redirection=False) cmd2's parser will still treat the ``>``, ``>>``, and `|` symbols as output redirection and pipe symbols and will strip arguments after them from the command line arguments accordingly. But output from a command will not be redirected to a file or piped to a shell command. Limitations of Redirection -------------------------- Some limitations apply to redirection and piping within ``cmd2`` applications: - Can only pipe to shell commands, not other ``cmd2`` application commands - **stdout** gets redirected/piped, **stderr** does not cmd2-2.3.3/docs/features/scripting.rst000066400000000000000000000110771416142110700176510ustar00rootroot00000000000000Scripting ========= Operating system shells have long had the ability to execute a sequence of commands saved in a text file. These script files make long sequences of commands easier to repeatedly execute. ``cmd2`` supports two similar mechanisms: command scripts and python scripts. Command Scripts --------------- A command script contains a sequence of commands typed at the the prompt of a ``cmd2`` based application. Unlike operating system shell scripts, command scripts can't contain logic or loops. Creating Command Scripts ~~~~~~~~~~~~~~~~~~~~~~~~ Command scripts can be created in several ways: - creating a text file using any method of your choice - using the built-in :ref:`features/builtin_commands:edit` command to create or edit an existing text file - saving previously entered commands to a script file using :ref:`history -s ` If you create create a text file from scratch, just include one command per line, exactly as you would type it inside a ``cmd2`` application. Running Command Scripts ~~~~~~~~~~~~~~~~~~~~~~~ Command script files can be executed using the built-in :ref:`features/builtin_commands:run_script` command or the ``@`` shortcut (if your application is using the default shortcuts). Both ASCII and UTF-8 encoded unicode text files are supported. The :ref:`features/builtin_commands:run_script` command supports tab completion of file system paths. There is a variant :ref:`features/builtin_commands:_relative_run_script` command or ``@@`` shortcut (if using the default shortcuts) for use within a script which uses paths relative to the first script. Comments ~~~~~~~~ Any command line input where the first non-whitespace character is a `#` will be treated as a comment. This means any `#` character appearing later in the command will be treated as a literal. The same applies to a `#` in the middle of a multiline command, even if it is the first character on a line. Comments are useful in scripts, but would be pointless within an interactive session. :: (Cmd) # this is a comment (Cmd) command # this is not a comment .. _scripting-python-scripts: Python Scripts -------------- .. _arg_printer: https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/arg_printer.py If you require logic flow, loops, branching, or other advanced features, you can write a python script which executes in the context of your ``cmd2`` app. This script is run using the :ref:`features/builtin_commands:run_pyscript` command. Here's a simple example that uses the arg_printer_ script:: (Cmd) run_pyscript examples/scripts/arg_printer.py foo bar 'baz 23' Running Python script 'arg_printer.py' which was called with 3 arguments arg 1: 'foo' arg 2: 'bar' arg 3: 'baz 23' :ref:`features/builtin_commands:run_pyscript` supports tab completion of file system paths, and as shown above it has the ability to pass command-line arguments to the scripts invoked. Python scripts executed with :ref:`features/builtin_commands:run_pyscript` can run ``cmd2`` application commands by using the syntax:: app(‘command args’) where: * ``app`` is a configurable name which can be changed by setting the :data:`cmd2.Cmd.py_bridge_name` attribute * ``command`` and ``args`` are entered exactly like they would be entered by a user of your application. See python_scripting_ example and associated conditional_ script for more information. Advanced Support ~~~~~~~~~~~~~~~~ When implementing a command, setting ``self.last_result`` allows for application-specific data to be returned to a python script from the command. This can allow python scripts to make decisions based on the result of previous application commands. The application command (default: ``app``) returns a ``cmd2.CommandResult`` for each command. The ``cmd2.CommandResult`` object provides the captured output to ``stdout`` and ``stderr`` while a command is executing. Additionally, it provides the value that command stored in ``self.last_result``. Additionally, an external test Mixin plugin has been provided to allow for python based external testing of the application. For example, for system integration tests scenarios where the python application is a component of a larger suite of tools and components. This interface allows python based tests to call commands and validate results as part of a larger test suite. See :ref:`plugins/external_test:External Test Plugin` .. _python_scripting: https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py .. _conditional: https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/conditional.py cmd2-2.3.3/docs/features/settings.rst000066400000000000000000000131571416142110700175100ustar00rootroot00000000000000Settings ======== Settings provide a mechanism for a user to control the behavior of a ``cmd2`` based application. A setting is stored in an instance attribute on your subclass of :class:`cmd2.Cmd` and must also appear in the :attr:`cmd2.Cmd.settable` dictionary. Developers may set default values for these settings and users can modify them at runtime using the :ref:`features/builtin_commands:set` command. Developers can :ref:`features/settings:Create New Settings` and can also :ref:`features/settings:Hide Builtin Settings` from the user. Builtin Settings ----------------- ``cmd2`` has a number of builtin settings. These settings control the behavior of certain application features and :ref:`features/builtin_commands:Builtin Commands`. Users can use the :ref:`features/builtin_commands:set` command to show all settings and to modify the value of any setting. allow_style ~~~~~~~~~~~ Output generated by ``cmd2`` programs may contain ANSI escape seqences which instruct the terminal to apply colors or text styling (i.e. bold) to the output. The ``allow_style`` setting controls the behavior of these escape sequences in output generated with any of the following methods: - :meth:`cmd2.Cmd.poutput` - :meth:`cmd2.Cmd.perror` - :meth:`cmd2.Cmd.pwarning` - :meth:`cmd2.Cmd.pexcept` - :meth:`cmd2.Cmd.pfeedback` - :meth:`cmd2.Cmd.ppaged` This setting can be one of three values: - ``Never`` - all ANSI escape sequences which instruct the terminal to style output are stripped from the output. - ``Terminal`` - (the default value) pass through ANSI escape sequences when the output is being sent to the terminal, but if the output is redirected to a pipe or a file the escape sequences are stripped. - ``Always`` - ANSI escape sequences are always passed through to the output debug ~~~~~ The default value of this setting is ``False``, which causes the :meth:`~cmd2.Cmd.pexcept` method to only display the message from an exception. However, if the debug setting is ``True``, then the entire stack trace will be printed. echo ~~~~ If ``True``, each command the user issues will be repeated to the screen before it is executed. This is particularly useful when running scripts. This behavior does not occur when running a command at the prompt. editor ~~~~~~ Similar to the ``EDITOR`` shell variable, this setting contains the name of the program which should be run by the :ref:`features/builtin_commands:edit` command. feedback_to_output ~~~~~~~~~~~~~~~~~~ Controls whether feedback generated with the :meth:`~cmd2.Cmd.pfeedback` method is sent to ``sys.stdout`` or ``sys.stderr``. If ``False`` the output will be sent to ``sys.stderr`` If ``True`` the output is sent to ``stdout`` (which is often the screen but may be :ref:`redirected `). The feedback output will be mixed in with and indistinguishable from output generated with :meth:`~cmd2.Cmd.poutput`. max_completion_items ~~~~~~~~~~~~~~~~~~~~ Maximum number of CompletionItems to display during tab completion. A CompletionItem is a special kind of tab completion hint which displays both a value and description and uses one line for each hint. Tab complete the ``set`` command for an example. If the number of tab completion hints exceeds ``max_completion_items``, then they will be displayed in the typical columnized format and will not include the description text of the CompletionItem. quiet ~~~~~ If ``True``, output generated by calling :meth:`~cmd2.Cmd.pfeedback` is suppressed. If ``False``, the :ref:`features/settings:feedback_to_output` setting controls where the output is sent. timing ~~~~~~ If ``True``, the elapsed time is reported for each command executed. Create New Settings ------------------- Your application can define user-settable parameters which your code can reference. In your initialization code: 1. Create an instance attribute with a default value. 2. Create a :class:`.Settable` object which describes your setting. 3. Pass the :class:`.Settable` object to :meth:`cmd2.Cmd.add_settable`. Here's an example, from ``examples/environment.py``: .. literalinclude:: ../../examples/environment.py If you want to be notified when a setting changes (as we do above), then be sure to supply a method to the ``onchange_cb`` parameter of the `.cmd2.utils.Settable`. This method will be called after the user changes a setting, and will receive both the old value and the new value. .. code-block:: text (Cmd) set | grep sunny sunny False Is it sunny outside? (Cmd) set | grep degrees degrees_c 22 Temperature in Celsius (Cmd) sunbathe Too dim. (Cmd) set degrees_c 41 degrees_c - was: 22 now: 41 (Cmd) set sunny sunny: True (Cmd) sunbathe UV is bad for your skin. (Cmd) set degrees_c 13 degrees_c - was: 41 now: 13 (Cmd) sunbathe It's 13 C - are you a penguin? Hide Builtin Settings --------------------- You may want to prevent a user from modifying a builtin setting. A setting must appear in the :attr:`cmd2.Cmd.settable` dictionary in order for it to be available to the :ref:`features/builtin_commands:set` command. Let's say that you never want end users of your program to be able to enable full debug tracebacks to print out if an error occurs. You might want to hide the :ref:`features/settings:debug` setting. To do so, remove it from the :attr:`cmd2.Cmd.settable` dictionary after you initialize your object. The :meth:`cmd2.Cmd.remove_settable` convenience method makes this easy:: class MyApp(cmd2.Cmd): def __init__(self): super().__init__() self.remove_settable('debug') cmd2-2.3.3/docs/features/shortcuts_aliases_macros.rst000066400000000000000000000072761416142110700227600ustar00rootroot00000000000000Shortcuts, Aliases, and Macros ============================== Shortcuts --------- Command shortcuts for long command names and common commands can make life more convenient for your users. Shortcuts are used without a space separating them from their arguments, like ``!ls``. By default, the following shortcuts are defined: ``?`` help ``!`` shell: run as OS-level command ``@`` run script file ``@@`` run script file; filename is relative to current script location To define more shortcuts, update the dict ``App.shortcuts`` with the {'shortcut': 'command_name'} (omit ``do_``):: class App(Cmd): def __init__(self): shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'*': 'sneeze', '~': 'squirm'}) cmd2.Cmd.__init__(self, shortcuts=shortcuts) .. warning:: Shortcuts need to be created by updating the ``shortcuts`` dictionary attribute prior to calling the ``cmd2.Cmd`` super class ``__init__()`` method. Moreover, that super class init method needs to be called after updating the ``shortcuts`` attribute This warning applies in general to many other attributes which are not settable at runtime. Note: Command, alias, and macro names cannot start with a shortcut Aliases ------- In addition to shortcuts, ``cmd2`` provides a full alias feature via the ``alias`` command. Aliases work in a similar fashion to aliases in the Bash shell. The syntax to create an alias is: ``alias create name command [args]``. Ex: ``alias create ls !ls -lF`` Redirectors and pipes should be quoted in alias definition to prevent the ``alias create`` command from being redirected:: alias create save_results print_results ">" out.txt Tab completion recognizes an alias, and completes as if its actual value was on the command line. For more details run: ``help alias create`` Use ``alias list`` to see all or some of your aliases. The output of this command displays your aliases using the same command that was used to create them. Therefore you can place this output in a ``cmd2`` startup script to recreate your aliases each time you start the application Ex: ``alias list`` For more details run: ``help alias list`` Use ``alias delete`` to remove aliases For more details run: ``help alias delete`` Note: Aliases cannot have the same name as a command or macro Macros ------ ``cmd2`` provides a feature that is similar to aliases called macros. The major difference between macros and aliases is that macros can contain argument placeholders. Arguments are expressed when creating a macro using {#} notation where {1} means the first argument. The following creates a macro called my_macro that expects two arguments: macro create my_macro make_dinner -meat {1} -veggie {2} When the macro is called, the provided arguments are resolved and the assembled command is run. For example: my_macro beef broccoli ---> make_dinner -meat beef -veggie broccoli Similar to aliases, pipes and redirectors need to be quoted in the definition of a macro:: macro create lc !cat "{1}" "|" less To use the literal string ``{1}`` in your command, escape it this way: ``{{1}}``. Because macros do not resolve until after hitting ````, tab completion will only complete paths while typing a macro. For more details run: ``help macro create`` The macro command has ``list`` and ``delete`` subcommands that function identically to the alias subcommands of the same name. Like aliases, macros can be created via a ``cmd2`` startup script to preserve them across application sessions. For more details on listing macros run: ``help macro list`` For more details on deleting macros run: ``help macro delete`` Note: Macros cannot have the same name as a command or alias cmd2-2.3.3/docs/features/startup_commands.rst000066400000000000000000000050021416142110700212210ustar00rootroot00000000000000Startup Commands ================ ``cmd2`` provides a couple different ways for running commands immediately after your application starts up: 1. Commands at Invocation 2. Startup Script Commands run as part of a startup script are always run immediately after the application finishes initializing so they are guaranteed to run before any *Commands At Invocation*. Commands At Invocation ---------------------- .. _Argparse: https://docs.python.org/3/library/argparse.html You can send commands to your app as you invoke it by including them as extra arguments to the program. ``cmd2`` interprets each argument as a separate command, so you should enclose each command in quotation marks if it is more than a one-word command. You can use either single or double quotes for this purpose. .. code-block:: shell $ python examples/example.py "say hello" "say Gracie" quit hello Gracie You can end your commands with a **quit** command so that your ``cmd2`` application runs like a non-interactive command-line utility (CLU). This means that it can then be scripted from an external application and easily used in automation. .. note:: If you wish to disable cmd2's consumption of command-line arguments, you can do so by setting the ``allow_cli_args`` argument of your ``cmd2.Cmd`` class instance to ``False``. This would be useful, for example, if you wish to use something like Argparse_ to parse the overall command line arguments for your application:: from cmd2 import Cmd class App(Cmd): def __init__(self): super().__init__(allow_cli_args=False) Startup Script -------------- .. _AliasStartup: https://github.com/python-cmd2/cmd2/blob/master/examples/alias_startup.py You can execute commands from an initialization script by passing a file path to the ``startup_script`` argument to the ``cmd2.Cmd.__init__()`` method like so:: class StartupApp(cmd2.Cmd): def __init__(self): cmd2.Cmd.__init__(self, startup_script='.cmd2rc') This text file should contain a :ref:`Command Script `. See the AliasStartup_ example for a demonstration. You can silence a startup script's output by setting ``silence_startup_script`` to True:: cmd2.Cmd.__init__(self, startup_script='.cmd2rc', silence_startup_script=True) Anything written to stderr will still print. Additionally, a startup script cannot be silenced if ``allow_redirection`` is False since silencing works by redirecting a script's output to ``os.devnull``. cmd2-2.3.3/docs/features/table_creation.rst000066400000000000000000000037111416142110700206160ustar00rootroot00000000000000Table Creation ============== ``cmd2`` provides a table creation class called :attr:`cmd2.table_creator.TableCreator`. This class handles ANSI style sequences and characters with display widths greater than 1 when performing width calculations. It was designed with the ability to build tables one row at a time. This helps when you have large data sets that you don't want to hold in memory or when you receive portions of the data set incrementally. ``TableCreator`` has one public method: :attr:`cmd2.table_creator.TableCreator.generate_row()` This function and the :attr:`cmd2.table_creator.Column` class provide all features needed to build tables with headers, borders, colors, horizontal and vertical alignment, and wrapped text. However, it's generally easier to inherit from this class and implement a more granular API rather than use ``TableCreator`` directly. The following table classes build upon ``TableCreator`` and are provided in the :ref:`api/table_creator:cmd2.table_creator` module. They can be used as is or as examples for how to build your own table classes. :attr:`cmd2.table_creator.SimpleTable` - Implementation of TableCreator which generates a borderless table with an optional divider row after the header. This class can be used to create the whole table at once or one row at a time. :attr:`cmd2.table_creator.BorderedTable` - Implementation of TableCreator which generates a table with borders around the table and between rows. Borders between columns can also be toggled. This class can be used to create the whole table at once or one row at a time. :attr:`cmd2.table_creator.AlternatingTable` - Implementation of BorderedTable which uses background colors to distinguish between rows instead of row border lines. This class can be used to create the whole table at once or one row at a time. See the table_creation_ example to see these classes in use .. _table_creation: https://github.com/python-cmd2/cmd2/blob/master/examples/table_creation.py cmd2-2.3.3/docs/features/transcripts.rst000066400000000000000000000166021416142110700202220ustar00rootroot00000000000000Transcripts =========== A transcript is both the input and output of a successful session of a ``cmd2``-based app which is saved to a text file. With no extra work on your part, your app can play back these transcripts as a unit test. Transcripts can contain regular expressions, which provide the flexibility to match responses from commands that produce dynamic or variable output. .. highlight:: none Creating From History --------------------- A transcript can automatically generated based upon commands previously executed in the *history* using ``history -t``:: (Cmd) help ... (Cmd) help history ... (Cmd) history 1:2 -t transcript.txt 2 commands and outputs saved to transcript file 'transcript.txt' This is by far the easiest way to generate a transcript. .. warning:: Make sure you use the **poutput()** method in your ``cmd2`` application for generating command output. This method of the ``cmd2.Cmd`` class ensure that output is properly redirected when redirecting to a file, piping to a shell command, and when generating a transcript. Creating From A Script File --------------------------- A transcript can also be automatically generated from a script file using ``run_script -t``:: (Cmd) run_script scripts/script.txt -t transcript.txt 2 commands and their outputs saved to transcript file 'transcript.txt' (Cmd) This is a particularly attractive option for automatically regenerating transcripts for regression testing as your ``cmd2`` application changes. Creating Manually ----------------- Here's a transcript created from ``python examples/example.py``:: (Cmd) say -r 3 Goodnight, Gracie Goodnight, Gracie Goodnight, Gracie Goodnight, Gracie (Cmd) mumble maybe we could go to lunch like maybe we ... could go to hmmm lunch (Cmd) mumble maybe we could go to lunch well maybe we could like go to er lunch right? This transcript has three commands: they are on the lines that begin with the prompt. The first command looks like this:: (Cmd) say -r 3 Goodnight, Gracie Following each command is the output generated by that command. The transcript ignores all lines in the file until it reaches the first line that begins with the prompt. You can take advantage of this by using the first lines of the transcript as comments:: # Lines at the beginning of the transcript that do not ; start with the prompt i.e. '(Cmd) ' are ignored. /* You can use them for comments. */ All six of these lines before the first prompt are treated as comments. (Cmd) say -r 3 Goodnight, Gracie Goodnight, Gracie Goodnight, Gracie Goodnight, Gracie (Cmd) mumble maybe we could go to lunch like maybe we ... could go to hmmm lunch (Cmd) mumble maybe we could go to lunch maybe we could like go to er lunch right? In this example I've used several different commenting styles, and even bare text. It doesn't matter what you put on those beginning lines. Everything before:: (Cmd) say -r 3 Goodnight, Gracie will be ignored. Regular Expressions ------------------- If we used the above transcript as-is, it would likely fail. As you can see, the ``mumble`` command doesn't always return the same thing: it inserts random words into the input. Regular expressions can be included in the response portion of a transcript, and are surrounded by slashes:: (Cmd) mumble maybe we could go to lunch /.*\bmaybe\b.*\bcould\b.*\blunch\b.*/ (Cmd) mumble maybe we could go to lunch /.*\bmaybe\b.*\bcould\b.*\blunch\b.*/ Without creating a tutorial on regular expressions, this one matches anything that has the words ``maybe``, ``could``, and ``lunch`` in that order. It doesn't ensure that ``we`` or ``go`` or ``to`` appear in the output, but it does work if mumble happens to add words to the beginning or the end of the output. Since the output could be multiple lines long, ``cmd2`` uses multiline regular expression matching, and also uses the ``DOTALL`` flag. These two flags subtly change the behavior of commonly used special characters like ``.``, ``^`` and ``$``, so you may want to double check the `Python regular expression documentation `_. If your output has slashes in it, you will need to escape those slashes so the stuff between them is not interpred as a regular expression. In this transcript:: (Cmd) say cd /usr/local/lib/python3.6/site-packages /usr/local/lib/python3.6/site-packages the output contains slashes. The text between the first slash and the second slash, will be interpreted as a regular expression, and those two slashes will not be included in the comparison. When replayed, this transcript would therefore fail. To fix it, we could either write a regular expression to match the path instead of specifying it verbatim, or we can escape the slashes:: (Cmd) say cd /usr/local/lib/python3.6/site-packages \/usr\/local\/lib\/python3.6\/site-packages .. warning:: Be aware of trailing spaces and newlines. Your commands might output trailing spaces which are impossible to see. Instead of leaving them invisible, you can add a regular expression to match them, so that you can see where they are when you look at the transcript:: (Cmd) set editor editor: vim/ / Some terminal emulators strip trailing space when you copy text from them. This could make the actual data generated by your app different than the text you pasted into the transcript, and it might not be readily obvious why the transcript is not passing. Consider using :ref:`features/redirection:Output Redirection and Pipes` to the clipboard or to a file to ensure you accurately capture the output of your command. If you aren't using regular expressions, make sure the newlines at the end of your transcript exactly match the output of your commands. A common cause of a failing transcript is an extra or missing newline. If you are using regular expressions, be aware that depending on how you write your regex, the newlines after the regex may or may not matter. ``\Z`` matches *after* the newline at the end of the string, whereas ``$`` matches the end of the string *or* just before a newline. Running A Transcript -------------------- Once you have created a transcript, it's easy to have your application play it back and check the output. From within the ``examples/`` directory:: $ python example.py --test transcript_regex.txt . ---------------------------------------------------------------------- Ran 1 test in 0.013s OK The output will look familiar if you use ``unittest``, because that's exactly what happens. Each command in the transcript is run, and we ``assert`` the output matches the expected result from the transcript. .. note:: If you have passed an ``allow_cli_args`` parameter containing `False` to :meth:`cmd2.Cmd.__init__` in order to disable parsing of command line arguments at invocation, then the use of ``-t`` or ``--test`` to run transcript testing is automatically disabled. In this case, you can alternatively provide a value for the optional ``transcript_files`` when constructing the instance of your ``cmd2.Cmd`` derived class in order to cause a transcript test to run:: from cmd2 import Cmd class App(Cmd): # customized attributes and methods here if __name__ == '__main__': app = App(transcript_files=['exampleSession.txt']) app.cmdloop() cmd2-2.3.3/docs/index.rst000066400000000000000000000025621416142110700151370ustar00rootroot00000000000000==== cmd2 ==== .. default-domain:: py .. _cmd: https://docs.python.org/3/library/cmd.html A python package for building powerful command-line interpreter (CLI) programs. Extends the Python Standard Library's cmd_ package. The basic use of ``cmd2`` is identical to that of cmd_. 1. Create a subclass of ``cmd2.Cmd``. Define attributes and ``do_*`` methods to control its behavior. Throughout this documentation, we will assume that you are naming your subclass ``App``:: from cmd2 import Cmd class App(Cmd): # customized attributes and methods here 2. Instantiate ``App`` and start the command loop:: app = App() app.cmdloop() Getting Started =============== .. include:: overview/summary.rst .. toctree:: :maxdepth: 1 :hidden: overview/index Migrating from cmd ================== .. include:: migrating/summary.rst .. toctree:: :maxdepth: 2 :hidden: migrating/index Features ======== .. toctree:: :maxdepth: 2 features/index Examples ======== .. toctree:: :maxdepth: 2 examples/index Plugins ======= .. toctree:: :maxdepth: 2 plugins/index Testing ======= .. toctree:: :maxdepth: 2 testing API Reference ============= .. toctree:: :maxdepth: 2 api/index Meta ==== :doc:`doc_conventions` .. toctree:: :maxdepth: 2 :hidden: :caption: Meta doc_conventions cmd2-2.3.3/docs/make.bat000077500000000000000000000057731416142110700147150ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation set SPHINXBUILD=sphinx-build set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\cmd2.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\cmd2.ghc goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end cmd2-2.3.3/docs/migrating/000077500000000000000000000000001416142110700152525ustar00rootroot00000000000000cmd2-2.3.3/docs/migrating/incompatibilities.rst000066400000000000000000000052541416142110700215220ustar00rootroot00000000000000Incompatibilities ================= .. _cmd: https://docs.python.org/3/library/cmd.html ``cmd2`` strives to be drop-in compatible with cmd_, however there are a few incompatibilities. Cmd.emptyline() --------------- The `Cmd.emptyline() `_ function is called when an empty line is entered in response to the prompt. By default, in cmd_ if this method is not overridden, it repeats and executes the last nonempty command entered. However, no end user we have encountered views this as expected or desirable default behavior. ``cmd2`` completely ignores empty lines and the base class ``cmd.emptyline()`` method never gets called and thus the empty line behavior cannot be overridden. Cmd.identchars -------------- In cmd_, the `Cmd.identchars `_ attribute contains the string of characters accepted for command names. cmd_ uses those characters to split the first "word" of the input, without requiring the user to type a space. For example, if ``identchars`` contained a string of all alphabetic characters, the user could enter a command like ``L20`` and it would be interpreted as the command ``L`` with the first argument of ``20``. Since version 0.9.0, ``cmd2`` has ignored ``identchars``; the parsing logic in ``cmd2`` splits the command and arguments on whitespace. We opted for this breaking change because while cmd_ supports unicode, using non-ascii unicode characters in command names while simultaneously using ``identchars`` functionality can be somewhat painful. Requiring white space to delimit arguments also ensures reliable operation of many other useful ``cmd2`` features, including :ref:`features/completion:Completion` and :ref:`features/shortcuts_aliases_macros:Shortcuts, Aliases, and Macros`. If you really need this functionality in your app, you can add it back in by writing a :ref:`Postparsing Hook `. Cmd.cmdqueue ------------ In cmd_, the `Cmd.cmdqueue `_ attribute contains a list of queued input lines. The cmdqueue list is checked in ``cmdloop()`` when new input is needed; if it is nonempty, its elements will be processed in order, as if entered at the prompt. Since version 0.9.13 ``cmd2`` has removed support for ``Cmd.cmdqueue``. Because ``cmd2`` supports running commands via the main ``cmdloop()``, text scripts, Python scripts, transcripts, and history replays, the only way to preserve consistent behavior across these methods was to eliminate the command queue. Additionally, reasoning about application behavior is much easier without this queue present. cmd2-2.3.3/docs/migrating/index.rst000066400000000000000000000002411416142110700171100ustar00rootroot00000000000000Migrating From cmd ================== .. toctree:: :maxdepth: 1 :hidden: why incompatibilities minimum next_steps .. include:: summary.rst cmd2-2.3.3/docs/migrating/minimum.rst000066400000000000000000000023271416142110700174630ustar00rootroot00000000000000Minimum Required Changes ======================== ``cmd2.Cmd`` subclasses ``Cmd.cmd`` from the standard library, and overrides most of the methods. Most apps based on the standard library can be migrated to ``cmd2`` in just a couple of minutes. Import and Inheritance ---------------------- You need to change your import from this:: import cmd to this:: import cmd2 Then you need to change your class definition from:: class CmdLineApp(cmd.Cmd): to:: class CmdLineApp(cmd2.Cmd): Exiting ------- Have a look at the commands you created to exit your application. You probably have one called ``exit`` and maybe a similar one called ``quit``. You also might have implemented a ``do_EOF()`` method so your program exits like many operating system shells. If all these commands do is quit the application, you may be able to remove them. See :ref:`features/misc:Exiting`. Distribution ------------ If you are distributing your application, you'll also need to ensure that ``cmd2`` is properly installed. You will need to add this to your ``setup()`` method in ``setup.py``:: install_requires=[ 'cmd2>=1,<2` ] See :ref:`overview/integrating:Integrate cmd2 Into Your Project` for more details. cmd2-2.3.3/docs/migrating/next_steps.rst000066400000000000000000000047141416142110700202060ustar00rootroot00000000000000Next Steps ========== Once your current application is using ``cmd2``, you can start to expand the functionality by levering other ``cmd2`` features. The three ideas here will get you started. Browse the rest of the :ref:`features/index:Features` to see what else ``cmd2`` can help you do. Argument Parsing ---------------- For all but the simplest of commands, it's probably easier to use `argparse `_ to parse user input. ``cmd2`` provides a ``@with_argparser()`` decorator which associates an ``ArgumentParser`` object with one of your commands. Using this method will: 1. Pass your command a `Namespace `_ containing the arguments instead of a string of text. 2. Properly handle quoted string input from your users. 3. Create a help message for you based on the ``ArgumentParser``. 4. Give you a big headstart adding :ref:`features/completion:Completion` to your application. 5. Make it much easier to implement subcommands (i.e. ``git`` has a bunch of subcommands such as ``git pull``, ``git diff``, etc). There's a lot more about :ref:`features/argument_processing:Argument Processing` if you want to dig in further. Help ---- If you have lot of commands in your application, ``cmd2`` can categorize those commands using a one line decorator ``@with_category()``. When a user types ``help`` the available commands will be organized by the category you specified. If you were already using ``argparse`` or decided to switch to it, you can easily :ref:`standardize all of your help messages ` to be generated by your argument parsers and displayed by ``cmd2``. No more help messages that don't match what the code actually does. Generating Output ----------------- If your program generates output by printing directly to ``sys.stdout``, you should consider switching to :meth:`~cmd2.Cmd.poutput`, :meth:`~cmd2.Cmd.perror`, and :meth:`~cmd2.Cmd.pfeedback`. These methods work with several of the built in :ref:`features/settings:Settings` to allow the user to view or suppress feedback (i.e. progress or status output). They also properly handle ansi colored output according to user preference. Speaking of colored output, you can use any color library you want, or use the included :meth:`cmd2.ansi.style` function. These and other related topics are covered in :ref:`features/generating_output:Generating Output`. cmd2-2.3.3/docs/migrating/summary.rst000066400000000000000000000012671416142110700175070ustar00rootroot00000000000000 .. _cmd: https://docs.python.org/3/library/cmd.html If you're thinking of migrating your cmd_ app to ``cmd2``, this section will help you decide if it's right for your app, and show you how to do it. * :ref:`migrating/why:Why cmd2` - we try and convince you to use ``cmd2`` instead of cmd_ * :ref:`migrating/incompatibilities:Incompatibilities` - ``cmd2`` is not quite 100% compatible with cmd_. * :ref:`migrating/minimum:Minimum Required Changes` - the minimum changes required to move from cmd_ to ``cmd2``. Start your migration here. * :ref:`migrating/next_steps:Next Steps` - Once you've migrated, here a list of things you can do next to add even more functionality to your app. cmd2-2.3.3/docs/migrating/why.rst000066400000000000000000000063731416142110700166240ustar00rootroot00000000000000Why cmd2 ======== .. _cmd: https://docs.python.org/3/library/cmd.html cmd --- cmd_ is the Python Standard Library's module for creating simple interactive command-line applications. cmd_ is an extremely bare-bones framework which leaves a lot to be desired. It doesn't even include a built-in way to exit from an application! Since the API provided by cmd_ provides the foundation on which ``cmd2`` is based, understanding the use of cmd_ is the first step in learning the use of ``cmd2``. Once you have read the cmd_ docs, return here to learn the ways that ``cmd2`` differs from cmd_. cmd2 ---- ``cmd2`` is a batteries-included extension of cmd_, which provides a wealth of functionality to make it quicker and easier for developers to create feature-rich interactive command-line applications which delight customers. ``cmd2`` can be used as a drop-in replacement for cmd_ with a few minor discrepancies as discussed in the :ref:`migrating/incompatibilities:Incompatibilities` section. Simply importing ``cmd2`` in place of cmd_ will add many features to an application without any further modifications. Migrating to ``cmd2`` will also open many additional doors for making it possible for developers to provide a top-notch interactive command-line experience for their users. Free Features ------------- After switching from cmd_ to ``cmd2``, your application will have the following new features and capabilities, without you having to do anything: - More robust :ref:`features/history:History`. Both cmd_ and ``cmd2`` have readline history, but ``cmd2`` also has a robust ``history`` command which allows you to edit prior commands in a text editor of your choosing, re-run multiple commands at a time, and save prior commands as a script to be executed later. - Users can redirect output to a file or pipe it to some other operating system command. You did remember to use ``self.stdout`` instead of ``sys.stdout`` in all of your print functions, right? If you did, then this will work out of the box. If you didn't, you'll have to go back and fix them. Before you do, you might consider the various ways ``cmd2`` has of :ref:`features/generating_output:Generating Output`. - Users can load script files, which contain a series of commands to be executed. - Users can create :ref:`features/shortcuts_aliases_macros:Shortcuts, Aliases, and Macros` to reduce the typing required for repetitive commands. - Embedded python shell allows a user to execute python code from within your ``cmd2`` app. How meta. - :ref:`features/clipboard:Clipboard Integration` allows you to save command output to the operating system clipboard. - A built-in :ref:`features/misc:Timer` can show how long it takes a command to execute - A :ref:`Transcript ` is a file which contains both the input and output of a successful session of a ``cmd2``-based app. The transcript can be played back into the app as a unit test. Next Steps ---------- In addition to the features you get with no additional work, ``cmd2`` offers a broad range of additional capabilties which can be easily added to your application. :ref:`migrating/next_steps:Next Steps` has some ideas of where you can start, or you can dig in to all the :ref:`features/index:Features`. cmd2-2.3.3/docs/overview/000077500000000000000000000000001416142110700151375ustar00rootroot00000000000000cmd2-2.3.3/docs/overview/alternatives.rst000066400000000000000000000051251416142110700203750ustar00rootroot00000000000000Alternatives ============ For programs that do not interact with the user in a continuous loop - programs that simply accept a set of arguments from the command line, return results, and do not keep the user within the program's environment - all you need are sys_\ .argv (the command-line arguments) and argparse_ (for parsing UNIX-style options and flags). Though some people may prefer docopt_ or click_ to argparse_. .. _sys: https://docs.python.org/3/library/sys.html .. _argparse: https://docs.python.org/3/library/argparse.html .. _docopt: https://pypi.org/project/docopt .. _click: https://click.palletsprojects.com The curses_ module produces applications that interact via a plaintext terminal window, but are not limited to simple text input and output; they can paint the screen with options that are selected from using the cursor keys. However, programming a curses_-based application is not as straightforward as using cmd_. .. _curses: https://docs.python.org/3/library/curses.html .. _cmd: https://docs.python.org/3/library/cmd.html Several Python packages exist for building interactive command-line applications approximately similar in concept to cmd_ applications. None of them share ``cmd2``'s close ties to cmd_, but they may be worth investigating nonetheless. Two of the most mature and full featured are: * `Python Prompt Toolkit`_ * Click_ .. _`Python Prompt Toolkit`: https://github.com/prompt-toolkit/python-prompt-toolkit `Python Prompt Toolkit`_ is a library for building powerful interactive command lines and terminal applications in Python. It provides a lot of advanced visual features like syntax highlighting, bottom bars, and the ability to create fullscreen apps. Click_ is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It is more geared towards command line utilities instead of command line interpreters, but it can be used for either. Getting a working command-interpreter application based on either `Python Prompt Toolkit`_ or Click_ requires a good deal more effort and boilerplate code than ``cmd2``. ``cmd2`` focuses on providing an excellent out-of-the-box experience with as many useful features as possible built in for free with as little work required on the developer's part as possible. We believe that ``cmd2`` provides developers the easiest way to write a command-line interpreter, while allowing a good experience for end users. If you are seeking a visually richer end-user experience and don't mind investing more development time, we would recommend checking out `Python Prompt Toolkit`_. cmd2-2.3.3/docs/overview/index.rst000066400000000000000000000002731416142110700170020ustar00rootroot00000000000000Getting Started =============== .. toctree:: :maxdepth: 1 :hidden: installation ../examples/first_app integrating alternatives resources .. include:: summary.rst cmd2-2.3.3/docs/overview/installation.rst000066400000000000000000000074551416142110700204050ustar00rootroot00000000000000 Installation Instructions ========================= .. _pip: https://pypi.org/project/pip .. _setuptools: https://pypi.org/project/setuptools .. _PyPI: https://pypi.org ``cmd2`` works on Linux, macOS, and Windows. It requires Python 3.6 or higher, pip_, and setuptools_. If you've got all that, then you can just: .. code-block:: shell $ pip install cmd2 .. note:: Depending on how and where you have installed Python on your system and on what OS you are using, you may need to have administrator or root privileges to install Python packages. If this is the case, take the necessary steps required to run the commands in this section as root/admin, e.g.: on most Linux or Mac systems, you can precede them with ``sudo``: .. code-block:: shell $ sudo pip install Prerequisites ------------- If you have Python 3 >=3.6 installed from `python.org `_, you will already have pip_ and setuptools_, but may need to upgrade to the latest versions: On Linux or OS X: .. code-block:: shell $ pip install -U pip setuptools On Windows: .. code-block:: shell > python -m pip install -U pip setuptools .. _`pip_install`: Install from PyPI ----------------- pip_ is the recommended installer. Installing packages from PyPI_ with pip is easy: .. code-block:: shell $ pip install cmd2 This will install the required 3rd-party dependencies, if necessary. .. _github: Install from GitHub ------------------- The latest version of ``cmd2`` can be installed directly from the master branch on GitHub using pip_: .. code-block:: shell $ pip install -U git+git://github.com/python-cmd2/cmd2.git Install from Debian or Ubuntu repos ----------------------------------- We recommend installing from pip_, but if you wish to install from Debian or Ubuntu repos this can be done with apt-get. For Python 3:: $ sudo apt-get install python3-cmd2 This will also install the required 3rd-party dependencies. .. warning:: Versions of ``cmd2`` before 0.8.9 should be considered to be of unstable "beta" quality and should not be relied upon for production use. If you cannot get a version >= 0.8.9 from your OS repository, then we recommend installing from either pip or GitHub - see :ref:`pip_install` or :ref:`github`. Upgrading cmd2 -------------- Upgrade an already installed ``cmd2`` to the latest version from PyPI_:: pip install -U cmd2 This will upgrade to the newest stable version of ``cmd2`` and will also upgrade any dependencies if necessary. Uninstalling cmd2 ----------------- If you wish to permanently uninstall ``cmd2``, this can also easily be done with pip_:: $ pip uninstall cmd2 macOS Considerations -------------------- macOS comes with the `libedit `_ library which is similar, but not identical, to GNU Readline. Tab completion for ``cmd2`` applications is only tested against GNU Readline. There are several ways GNU Readline can be installed within a Python environment on a Mac, detailed in the following subsections. gnureadline Python module ~~~~~~~~~~~~~~~~~~~~~~~~~ Install the `gnureadline `_ Python module which is statically linked against a specific compatible version of GNU Readline: .. code-block:: shell $ pip install -U gnureadline readline via conda ~~~~~~~~~~~~~~~~~~ Install the **readline** package using the ``conda`` package manager included with the Anaconda Python distribution: .. code-block:: shell $ conda install readline readline via brew ~~~~~~~~~~~~~~~~~ Install the **readline** package using the Homebrew package manager (compiles from source): .. code-block:: shell $ brew install openssl $ brew install pyenv $ brew install readline Then use pyenv to compile Python and link against the installed readline cmd2-2.3.3/docs/overview/integrating.rst000066400000000000000000000025651416142110700202140ustar00rootroot00000000000000Integrate cmd2 Into Your Project ==================================== Once installed, you will want to ensure that your project's dependencies include ``cmd2``. Make sure your ``setup.py`` includes the following:: install_requires=[ 'cmd2>=1,<2', ] The ``cmd2`` project uses `Semantic Versioning `_, which means that any incompatible API changes will be release with a new major version number. The public API is documented in the :ref:`api/index:API Reference`. We recommend that you follow the advice given by the Python Packaging User Guide related to `install_requires `_. By setting an upper bound on the allowed version, you can ensure that your project does not inadvertently get installed with an incompatible future version of ``cmd2``. Windows Considerations ---------------------- If you would like to use :ref:`features/completion:Completion`, and you want your application to run on Windows, you will need to ensure you install the ``pyreadline3`` or ``pyreadline`` package. Make sure to include the following in your ``setup.py``:: install_requires=[ 'cmd2>=1,<2', ":sys_platform=='win32'": [ "pyreadline ; python_version<'3.8'", "pyreadline3 ; python_version>='3.8'", # pyreadline3 is a drop-in replacement for Python 3.8 and above ], ] cmd2-2.3.3/docs/overview/resources.rst000066400000000000000000000010521416142110700177010ustar00rootroot00000000000000Resources ========= .. _cmd: https://docs.python.org/3/library/cmd.html .. _`cmd2 project page`: https://github.com/python-cmd2/cmd2 .. _`project bug tracker`: https://github.com/python-cmd2/cmd2/issues Project related links and other resources: * cmd_ * `cmd2 project page`_ * `project bug tracker`_ * PyOhio 2019: `slides `_, `video `_, `examples `_ cmd2-2.3.3/docs/overview/summary.rst000066400000000000000000000017661416142110700174000ustar00rootroot00000000000000 .. _cmd: https://docs.python.org/3/library/cmd.html Building a new `REPL `_ or `Command Line Interface `_ application? Already built an application that uses cmd_ from the python standard library and want to add more functionality with very little work? ``cmd2`` is a powerful python library for building command line applications. Start here to find out if this library is a good fit for your needs. * :ref:`overview/installation:Installation Instructions` - how to install ``cmd2`` and associated optional dependencies * :ref:`examples/first_app:First Application` - a sample application showing 8 key features of ``cmd2`` * :ref:`overview/integrating:Integrate cmd2 Into Your Project` - adding ``cmd2`` to your project * :ref:`overview/alternatives:Alternatives` - other python packages that might meet your needs * :ref:`overview/resources:Resources` - related links and other materials cmd2-2.3.3/docs/plugins/000077500000000000000000000000001416142110700147525ustar00rootroot00000000000000cmd2-2.3.3/docs/plugins/external_test.rst000066400000000000000000000054001416142110700203640ustar00rootroot00000000000000External Test Plugin ==================== Overview ~~~~~~~~ .. _cmd2_external_test_plugin: https://github.com/python-cmd2/cmd2/tree/cmdset_settables/plugins/ext_test The `External Test Plugin `_ supports testing of a cmd2 application by exposing access cmd2 commands with the same context as from within a cmd2 :ref:`Python Scripts `. This interface captures ``stdout``, ``stderr``, as well as any application-specific data returned by the command. This also allows for verification of an application's support for :ref:`Python Scripts ` and enables the cmd2 application to be tested as part of a larger system integration test. Example cmd2 Application ~~~~~~~~~~~~~~~~~~~~~~~~ The following short example shows how to mix in the external test plugin to create a fixture for testing your cmd2 application. Define your cmd2 application .. code-block:: python import cmd2 class ExampleApp(cmd2.Cmd): """An class to show how to use a plugin""" def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, **kwargs) def do_something(self, arg): self.last_result = 5 self.poutput('this is the something command') Defining the test fixture ~~~~~~~~~~~~~~~~~~~~~~~~~ In your test, define a fixture for your cmd2 application .. code-block:: python import cmd2_ext_test import pytest class ExampleAppTester(cmd2_ext_test.ExternalTestMixin, ExampleApp): def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, **kwargs) @pytest.fixture def example_app(): app = ExampleAppTester() app.fixture_setup() yield app app.fixture_teardown() Writing Tests ~~~~~~~~~~~~~ Now write your tests that validate your application using the :meth:`~cmd2_ext_test.ExternalTestMixin.app_cmd()` function to access the cmd2 application's commands. This allows invocation of the application's commands in the same format as a user would type. The results from calling a command matches what is returned from running an python script with cmd2's :ref:`feature-builtin-commands-run-pyscript` command, which provides ``stdout``, ``stderr``, and the command's result data. .. code-block:: python from cmd2 import CommandResult def test_something(example_app): # execute a command out = example_app.app_cmd("something") # validate the command output and result data assert isinstance(out, CommandResult) assert str(out.stdout).strip() == 'this is the something command' assert out.data == 5 cmd2-2.3.3/docs/plugins/index.rst000066400000000000000000000001011416142110700166030ustar00rootroot00000000000000Plugins ======== .. toctree:: :maxdepth: 1 external_test cmd2-2.3.3/docs/testing.rst000066400000000000000000000045521416142110700155060ustar00rootroot00000000000000Testing ======= .. toctree:: :maxdepth: 1 Overview ~~~~~~~~ This covers special considerations when writing unit tests for a cmd2 application. Testing Commands ~~~~~~~~~~~~~~~~ The :doc:`External Test Plugin ` provides a mixin class with an :meth:`` function that allows external calls to application commands. The :meth:`~cmd2_ext_test.ExternalTestMixin.app_cmd()` function captures and returns stdout, stderr, and the command-specific result data. Mocking ~~~~~~~ .. _python_mock_autospeccing: https://docs.python.org/3/library/unittest.mock.html#autospeccing .. _python_mock_patch: https://docs.python.org/3/library/unittest.mock.html#patch If you need to mock anything in your cmd2 application, and most specifically in sub-classes of :class:`~cmd2.Cmd` or :class:`~cmd2.command_definition.CommandSet`, you must use `Autospeccing `_, `spec=True `_, or whatever equivalant is provided in the mocking library you're using. In order to automatically load functions as commands cmd2 performs a number of reflection calls to look up attributes of classes defined in your cmd2 application. Many mocking libraries will automatically create mock objects to match any attribute being requested, regardless of whether they're present in the object being mocked. This behavior can incorrectly instruct cmd2 to treat a function or attribute as something it needs to recognize and process. To prevent this, you should always mock with `Autospeccing `_ or `spec=True `_ enabled. If you don't have autospeccing on, your unit tests will failing with an error message like:: cmd2.exceptions.CommandSetRegistrationError: Subcommand is not valid: must be a string. Received instead Examples ~~~~~~~~ .. code-block:: python def test_mocked_methods(): with mock.patch.object(MockMethodApp, 'foo', spec=True): cli = MockMethodApp() Another one using `pytest-mock `_ to provide a ``mocker`` fixture: .. code-block:: python def test_mocked_methods2(mocker): mock_cmdloop = mocker.patch("cmd2.Cmd.cmdloop", autospec=True) cli = cmd2.Cmd() cli.cmdloop() assert mock_cmdloop.call_count == 1 cmd2-2.3.3/examples/000077500000000000000000000000001416142110700141575ustar00rootroot00000000000000cmd2-2.3.3/examples/.cmd2rc000066400000000000000000000000571416142110700153340ustar00rootroot00000000000000alias create ls !ls -hal alias create pwd !pwd cmd2-2.3.3/examples/alias_startup.py000077500000000000000000000013471416142110700174140ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A simple example demonstrating the following: 1) How to add custom command aliases using the alias command 2) How to run an initialization script at startup """ import os import cmd2 class AliasAndStartup(cmd2.Cmd): """Example cmd2 application where we create commands that just print the arguments they are called with.""" def __init__(self): alias_script = os.path.join(os.path.dirname(__file__), '.cmd2rc') super().__init__(startup_script=alias_script) def do_nothing(self, args): """This command does nothing and produces no output.""" pass if __name__ == '__main__': import sys app = AliasAndStartup() sys.exit(app.cmdloop()) cmd2-2.3.3/examples/arg_decorators.py000077500000000000000000000037271416142110700175430ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """An example demonstrating how use one of cmd2's argument parsing decorators""" import argparse import os import cmd2 class ArgparsingApp(cmd2.Cmd): def __init__(self): super().__init__(include_ipy=True) self.intro = 'cmd2 has awesome decorators to make it easy to use Argparse to parse command arguments' # do_fsize parser fsize_parser = cmd2.Cmd2ArgumentParser(description='Obtain the size of a file') fsize_parser.add_argument('-c', '--comma', action='store_true', help='add comma for thousands separator') fsize_parser.add_argument('-u', '--unit', choices=['MB', 'KB'], help='unit to display size in') fsize_parser.add_argument('file_path', help='path of file', completer=cmd2.Cmd.path_complete) @cmd2.with_argparser(fsize_parser) def do_fsize(self, args: argparse.Namespace) -> None: """Obtain the size of a file""" expanded_path = os.path.expanduser(args.file_path) try: size = os.path.getsize(expanded_path) except OSError as ex: self.perror("Error retrieving size: {}".format(ex)) return if args.unit == 'KB': size /= 1024 elif args.unit == 'MB': size /= 1024 * 1024 else: args.unit = 'bytes' size = round(size, 2) if args.comma: size = '{:,}'.format(size) self.poutput('{} {}'.format(size, args.unit)) # do_pow parser pow_parser = cmd2.Cmd2ArgumentParser() pow_parser.add_argument('base', type=int) pow_parser.add_argument('exponent', type=int, choices=range(-5, 6)) @cmd2.with_argparser(pow_parser) def do_pow(self, args: argparse.Namespace) -> None: """Raise an integer to a small integer exponent, either positive or negative""" self.poutput('{} ** {} == {}'.format(args.base, args.exponent, args.base ** args.exponent)) if __name__ == '__main__': app = ArgparsingApp() app.cmdloop() cmd2-2.3.3/examples/arg_print.py000077500000000000000000000062221416142110700165230ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A simple example demonstrating the following: 1) How arguments and options get parsed and passed to commands 2) How to change what syntax gets parsed as a comment and stripped from the arguments This is intended to serve as a live demonstration so that developers can experiment with and understand how command and argument parsing work. It also serves as an example of how to create shortcuts. """ import cmd2 class ArgumentAndOptionPrinter(cmd2.Cmd): """Example cmd2 application where we create commands that just print the arguments they are called with.""" def __init__(self): # Create command shortcuts which are typically 1 character abbreviations which can be used in place of a command shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'$': 'aprint', '%': 'oprint'}) super().__init__(shortcuts=shortcuts) def do_aprint(self, statement): """Print the argument string this basic command is called with.""" self.poutput('aprint was called with argument: {!r}'.format(statement)) self.poutput('statement.raw = {!r}'.format(statement.raw)) self.poutput('statement.argv = {!r}'.format(statement.argv)) self.poutput('statement.command = {!r}'.format(statement.command)) @cmd2.with_argument_list def do_lprint(self, arglist): """Print the argument list this basic command is called with.""" self.poutput('lprint was called with the following list of arguments: {!r}'.format(arglist)) @cmd2.with_argument_list(preserve_quotes=True) def do_rprint(self, arglist): """Print the argument list this basic command is called with (with quotes preserved).""" self.poutput('rprint was called with the following list of arguments: {!r}'.format(arglist)) oprint_parser = cmd2.Cmd2ArgumentParser() oprint_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') oprint_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') oprint_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') oprint_parser.add_argument('words', nargs='+', help='words to print') @cmd2.with_argparser(oprint_parser) def do_oprint(self, args): """Print the options and argument list this options command was called with.""" self.poutput('oprint was called with the following\n\toptions: {!r}'.format(args)) pprint_parser = cmd2.Cmd2ArgumentParser() pprint_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') pprint_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') pprint_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') @cmd2.with_argparser(pprint_parser, with_unknown_args=True) def do_pprint(self, args, unknown): """Print the options and argument list this options command was called with.""" self.poutput('oprint was called with the following\n\toptions: {!r}\n\targuments: {}'.format(args, unknown)) if __name__ == '__main__': import sys app = ArgumentAndOptionPrinter() sys.exit(app.cmdloop()) cmd2-2.3.3/examples/argparse_completion.py000066400000000000000000000106651416142110700205760ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A simple example demonstrating how to integrate tab completion with argparse-based commands. """ import argparse from typing import ( Dict, List, ) from cmd2 import ( Cmd, Cmd2ArgumentParser, CompletionError, CompletionItem, ansi, with_argparser, ) # Data source for argparse.choices food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] class ArgparseCompletion(Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] def choices_provider(self) -> List[str]: """A choices provider is useful when the choice list is based on instance data of your application""" return self.sport_item_strs def choices_completion_error(self) -> List[str]: """ CompletionErrors can be raised if an error occurs while tab completing. Example use cases - Reading a database to retrieve a tab completion data set failed - A previous command line argument that determines the data set being completed is invalid """ if self.debug: return self.sport_item_strs raise CompletionError("debug must be true") # noinspection PyMethodMayBeStatic def choices_completion_item(self) -> List[CompletionItem]: """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" fancy_item = "These things can\ncontain newlines and\n" fancy_item += ansi.style("styled text!!", fg=ansi.Fg.LIGHT_YELLOW, underline=True) items = {1: "My item", 2: "Another item", 3: "Yet another item", 4: fancy_item} return [CompletionItem(item_id, description) for item_id, description in items.items()] # noinspection PyMethodMayBeStatic def choices_arg_tokens(self, arg_tokens: Dict[str, List[str]]) -> List[str]: """ If a choices or completer function/method takes a value called arg_tokens, then it will be passed a dictionary that maps the command line tokens up through the one being completed to their argparse argument name. All values of the arg_tokens dictionary are lists, even if a particular argument expects only 1 token. """ # Check if choices_provider flag has appeared values = ['choices_provider', 'flag'] if 'choices_provider' in arg_tokens: values.append('is {}'.format(arg_tokens['choices_provider'][0])) else: values.append('not supplied') return values # Parser for example command example_parser = Cmd2ArgumentParser( description="Command demonstrating tab completion with argparse\n" "Notice even the flags of this command tab complete" ) # Tab complete from a list using argparse choices. Set metavar if you don't # want the entire choices list showing in the usage text for this command. example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", help="tab complete using choices") # Tab complete from choices provided by a choices_provider example_parser.add_argument( '--choices_provider', choices_provider=choices_provider, help="tab complete using a choices_provider" ) # Tab complete using a completer example_parser.add_argument('--completer', completer=Cmd.path_complete, help="tab complete using a completer") # Demonstrate raising a CompletionError while tab completing example_parser.add_argument( '--completion_error', choices_provider=choices_completion_error, help="raise a CompletionError while tab completing if debug is False", ) # Demonstrate returning CompletionItems instead of strings example_parser.add_argument( '--completion_item', choices_provider=choices_completion_item, metavar="ITEM_ID", descriptive_header="Description", help="demonstrate use of CompletionItems", ) # Demonstrate use of arg_tokens dictionary example_parser.add_argument( '--arg_tokens', choices_provider=choices_arg_tokens, help="demonstrate use of arg_tokens dictionary" ) @with_argparser(example_parser) def do_example(self, _: argparse.Namespace) -> None: """The example command""" self.poutput("I do nothing") if __name__ == '__main__': import sys app = ArgparseCompletion() sys.exit(app.cmdloop()) cmd2-2.3.3/examples/async_printing.py000077500000000000000000000155271416142110700175750ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A simple example demonstrating an application that asynchronously prints alerts, updates the prompt and changes the window title """ import random import threading import time from typing import ( List, ) import cmd2 from cmd2 import ( Fg, style, ) ALERTS = [ "Watch as this application prints alerts and updates the prompt", "This will only happen when the prompt is present", "Notice how it doesn't interfere with your typing or cursor location", "Go ahead and type some stuff and move the cursor throughout the line", "Keep typing...", "Move that cursor...", "Pretty seamless, eh?", "Feedback can also be given in the window title. Notice the alert count up there?", "You can stop and start the alerts by typing stop_alerts and start_alerts", "This demo will now continue to print alerts at random intervals", ] class AlerterApp(cmd2.Cmd): """An app that shows off async_alert() and async_update_prompt()""" def __init__(self, *args, **kwargs) -> None: """Initializer""" super().__init__(*args, **kwargs) self.prompt = "(APR)> " # The thread that will asynchronously alert the user of events self._stop_event = threading.Event() self._alerter_thread = threading.Thread() self._alert_count = 0 self._next_alert_time = 0 # Create some hooks to handle the starting and stopping of our thread self.register_preloop_hook(self._preloop_hook) self.register_postloop_hook(self._postloop_hook) def _preloop_hook(self) -> None: """Start the alerter thread""" # This runs after cmdloop() acquires self.terminal_lock, which will be locked until the prompt appears. # Therefore this is the best place to start the alerter thread since there is no risk of it alerting # before the prompt is displayed. You can also start it via a command if its not something that should # be running during the entire application. See do_start_alerts(). self._stop_event.clear() self._alerter_thread = threading.Thread(name='alerter', target=self._alerter_thread_func) self._alerter_thread.start() def _postloop_hook(self) -> None: """Stops the alerter thread""" # After this function returns, cmdloop() releases self.terminal_lock which could make the alerter # thread think the prompt is on screen. Therefore this is the best place to stop the alerter thread. # You can also stop it via a command. See do_stop_alerts(). self._stop_event.set() if self._alerter_thread.is_alive(): self._alerter_thread.join() def do_start_alerts(self, _): """Starts the alerter thread""" if self._alerter_thread.is_alive(): print("The alert thread is already started") else: self._stop_event.clear() self._alerter_thread = threading.Thread(name='alerter', target=self._alerter_thread_func) self._alerter_thread.start() def do_stop_alerts(self, _): """Stops the alerter thread""" self._stop_event.set() if self._alerter_thread.is_alive(): self._alerter_thread.join() else: print("The alert thread is already stopped") def _get_alerts(self) -> List[str]: """ Reports alerts :return: the list of alerts """ global ALERTS cur_time = time.monotonic() if cur_time < self._next_alert_time: return [] alerts = [] if self._alert_count < len(ALERTS): alerts.append(ALERTS[self._alert_count]) self._alert_count += 1 self._next_alert_time = cur_time + 4 else: rand_num = random.randint(1, 20) if rand_num > 2: return [] for i in range(0, rand_num): self._alert_count += 1 alerts.append("Alert {}".format(self._alert_count)) self._next_alert_time = 0 return alerts def _generate_alert_str(self) -> str: """ Combines alerts into one string that can be printed to the terminal :return: the alert string """ global ALERTS alert_str = '' alerts = self._get_alerts() longest_alert = max(ALERTS, key=len) num_asterisks = len(longest_alert) + 8 for i, cur_alert in enumerate(alerts): # Use padding to center the alert padding = ' ' * int((num_asterisks - len(cur_alert)) / 2) if i > 0: alert_str += '\n' alert_str += '*' * num_asterisks + '\n' alert_str += padding + cur_alert + padding + '\n' alert_str += '*' * num_asterisks + '\n' return alert_str def _generate_colored_prompt(self) -> str: """ Randomly generates a colored prompt :return: the new prompt """ rand_num = random.randint(1, 20) status_color = Fg.RESET if rand_num == 1: status_color = Fg.LIGHT_RED elif rand_num == 2: status_color = Fg.LIGHT_YELLOW elif rand_num == 3: status_color = Fg.CYAN elif rand_num == 4: status_color = Fg.LIGHT_GREEN elif rand_num == 5: status_color = Fg.LIGHT_BLUE return style(self.visible_prompt, fg=status_color) def _alerter_thread_func(self) -> None: """Prints alerts and updates the prompt any time the prompt is showing""" self._alert_count = 0 self._next_alert_time = 0 while not self._stop_event.is_set(): # Always acquire terminal_lock before printing alerts or updating the prompt # To keep the app responsive, do not block on this call if self.terminal_lock.acquire(blocking=False): # Get any alerts that need to be printed alert_str = self._generate_alert_str() # Generate a new prompt new_prompt = self._generate_colored_prompt() # Check if we have alerts to print if alert_str: # new_prompt is an optional parameter to async_alert() self.async_alert(alert_str, new_prompt) new_title = "Alerts Printed: {}".format(self._alert_count) self.set_window_title(new_title) # No alerts needed to be printed, check if the prompt changed elif new_prompt != self.prompt: self.async_update_prompt(new_prompt) # Don't forget to release the lock self.terminal_lock.release() self._stop_event.wait(0.5) if __name__ == '__main__': import sys app = AlerterApp() app.set_window_title("Asynchronous Printer Test") sys.exit(app.cmdloop()) cmd2-2.3.3/examples/basic.py000077500000000000000000000025171416142110700156220ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """A simple example demonstrating the following: 1) How to add a command 2) How to add help for that command 3) Persistent history 4) How to run an initialization script at startup 5) How to add custom command aliases using the alias command 6) Shell-like capabilities """ import cmd2 from cmd2 import ( Bg, Fg, style, ) class BasicApp(cmd2.Cmd): CUSTOM_CATEGORY = 'My Custom Commands' def __init__(self): super().__init__( multiline_commands=['echo'], persistent_history_file='cmd2_history.dat', startup_script='scripts/startup.txt', include_ipy=True, ) self.intro = style('Welcome to PyOhio 2019 and cmd2!', fg=Fg.RED, bg=Bg.WHITE, bold=True) + ' 😀' # Allow access to your application in py and ipy via self self.self_in_py = True # Set the default category name self.default_category = 'cmd2 Built-in Commands' @cmd2.with_category(CUSTOM_CATEGORY) def do_intro(self, _): """Display the intro banner""" self.poutput(self.intro) @cmd2.with_category(CUSTOM_CATEGORY) def do_echo(self, arg): """Example of a multiline command""" self.poutput(arg) if __name__ == '__main__': app = BasicApp() app.cmdloop() cmd2-2.3.3/examples/basic_completion.py000077500000000000000000000102121416142110700200420ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A simple example demonstrating how to enable tab completion by assigning a completer function to do_* commands. This also demonstrates capabilities of the following completer features included with cmd2: - CompletionError exceptions - delimiter_complete() - flag_based_complete() (see note below) - index_based_complete() (see note below) flag_based_complete() and index_based_complete() are basic methods and should only be used if you are not familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use argparse-based completion. For an example integrating tab completion with argparse, see argparse_completion.py """ import functools from typing import ( List, ) import cmd2 # List of strings used with completion functions food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] # This data is used to demonstrate delimiter_complete file_strs = [ '/home/user/file.db', '/home/user/file space.db', '/home/user/another.db', '/home/other user/maps.db', '/home/other user/tests.db', ] class BasicCompletion(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def do_flag_based(self, statement: cmd2.Statement): """Tab completes arguments based on a preceding flag using flag_based_complete -f, --food [completes food items] -s, --sport [completes sports] -p, --path [completes local file system paths] """ self.poutput("Args: {}".format(statement.args)) def complete_flag_based(self, text, line, begidx, endidx) -> List[str]: """Completion function for do_flag_based""" flag_dict = { # Tab complete food items after -f and --food flags in command line '-f': food_item_strs, '--food': food_item_strs, # Tab complete sport items after -s and --sport flags in command line '-s': sport_item_strs, '--sport': sport_item_strs, # Tab complete using path_complete function after -p and --path flags in command line '-p': self.path_complete, '--path': self.path_complete, } return self.flag_based_complete(text, line, begidx, endidx, flag_dict=flag_dict) def do_index_based(self, statement: cmd2.Statement): """Tab completes first 3 arguments using index_based_complete""" self.poutput("Args: {}".format(statement.args)) def complete_index_based(self, text, line, begidx, endidx) -> List[str]: """Completion function for do_index_based""" index_dict = { 1: food_item_strs, # Tab complete food items at index 1 in command line 2: sport_item_strs, # Tab complete sport items at index 2 in command line 3: self.path_complete, # Tab complete using path_complete function at index 3 in command line } return self.index_based_complete(text, line, begidx, endidx, index_dict=index_dict) def do_delimiter_complete(self, statement: cmd2.Statement): """Tab completes files from a list using delimiter_complete""" self.poutput("Args: {}".format(statement.args)) # Use a partialmethod to set arguments to delimiter_complete complete_delimiter_complete = functools.partialmethod(cmd2.Cmd.delimiter_complete, match_against=file_strs, delimiter='/') def do_raise_error(self, statement: cmd2.Statement): """Demonstrates effect of raising CompletionError""" self.poutput("Args: {}".format(statement.args)) def complete_raise_error(self, text, line, begidx, endidx) -> List[str]: """ CompletionErrors can be raised if an error occurs while tab completing. Example use cases - Reading a database to retrieve a tab completion data set failed - A previous command line argument that determines the data set being completed is invalid """ raise cmd2.CompletionError("This is how a CompletionError behaves") if __name__ == '__main__': import sys app = BasicCompletion() sys.exit(app.cmdloop()) cmd2-2.3.3/examples/cmd2_history.dat000066400000000000000000000001601416142110700172540ustar00rootroot00000000000000ý7zXZæÖ´F!t/å£à61]=‚€+ŽIh>t{Ë +š®»ý·:™Â4“Üh†ù¬`”Ùºpc ÒÜÛµëC¨ì%ši<M7l!ìݶó}YZcmd2-2.3.3/examples/cmd_as_argument.py000077500000000000000000000076631416142110700177000ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2. This example is very similar to example.py, but had additional code in main() that shows how to accept a command from the command line at invocation: $ python cmd_as_argument.py speak -p hello there """ import argparse import random import cmd2 class CmdLineApp(cmd2.Cmd): """Example cmd2 application.""" # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh'] MUMBLE_FIRST = ['so', 'like', 'well'] MUMBLE_LAST = ['right?'] def __init__(self): shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'&': 'speak'}) # Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell super().__init__(allow_cli_args=False, include_ipy=True, multiline_commands=['orate'], shortcuts=shortcuts) self.self_in_py = True self.maxrepeats = 3 # Make maxrepeats settable at runtime self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input mumble_parser = cmd2.Cmd2ArgumentParser() mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat') mumble_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(mumble_parser) def do_mumble(self, args): """Mumbles what you tell me to.""" repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): output = [] if random.random() < 0.33: output.append(random.choice(self.MUMBLE_FIRST)) for word in args.words: if random.random() < 0.40: output.append(random.choice(self.MUMBLES)) output.append(word) if random.random() < 0.25: output.append(random.choice(self.MUMBLE_LAST)) self.poutput(' '.join(output)) def main(argv=None): """Run when invoked from the operating system shell""" parser = cmd2.Cmd2ArgumentParser(description='Commands as arguments') command_help = 'optional command to run, if no command given, enter an interactive shell' parser.add_argument('command', nargs='?', help=command_help) arg_help = 'optional arguments for command' parser.add_argument('command_args', nargs=argparse.REMAINDER, help=arg_help) args = parser.parse_args(argv) c = CmdLineApp() sys_exit_code = 0 if args.command: # we have a command, run it and then exit c.onecmd_plus_hooks('{} {}'.format(args.command, ' '.join(args.command_args))) else: # we have no command, drop into interactive mode sys_exit_code = c.cmdloop() return sys_exit_code if __name__ == '__main__': import sys sys.exit(main()) cmd2-2.3.3/examples/colors.py000077500000000000000000000065341416142110700160450ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2. Demonstrating colorized output. Experiment with the command line options on the `speak` command to see how different output colors ca The allow_style setting has three possible values: Never poutput(), pfeedback(), and ppaged() strip all ANSI style sequences which instruct the terminal to colorize output Terminal (the default value) poutput(), pfeedback(), and ppaged() do not strip any ANSI style sequences when the output is a terminal, but if the output is a pipe or a file the style sequences are stripped. If you want colorized output, add ANSI style sequences using cmd2's internal ansi module. Always poutput(), pfeedback(), and ppaged() never strip ANSI style sequences, regardless of the output destination """ import cmd2 from cmd2 import ( Bg, Fg, ansi, ) fg_choices = [c.name.lower() for c in Fg] bg_choices = [c.name.lower() for c in Bg] class CmdLineApp(cmd2.Cmd): """Example cmd2 application demonstrating colorized output.""" def __init__(self): # Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell super().__init__(include_ipy=True) self.maxrepeats = 3 # Make maxrepeats settable at runtime self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) # Should ANSI color output be allowed self.allow_style = ansi.AllowStyle.TERMINAL speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('-f', '--fg', choices=fg_choices, help='foreground color to apply to output') speak_parser.add_argument('-b', '--bg', choices=bg_choices, help='background color to apply to output') speak_parser.add_argument('-l', '--bold', action='store_true', help='bold the output') speak_parser.add_argument('-u', '--underline', action='store_true', help='underline the output') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 fg_color = Fg[args.fg.upper()] if args.fg else None bg_color = Bg[args.bg.upper()] if args.bg else None output_str = ansi.style(' '.join(words), fg=fg_color, bg=bg_color, bold=args.bold, underline=args.underline) for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(output_str) def do_timetravel(self, _): """A command which always generates an error message, to demonstrate custom error colors""" self.perror('Mr. Fusion failed to start. Could not energize flux capacitor.') if __name__ == '__main__': import sys c = CmdLineApp() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/custom_parser.py000066400000000000000000000021111416142110700174120ustar00rootroot00000000000000# coding=utf-8 """ Defines the CustomParser used with override_parser.py example """ import sys from cmd2 import ( Cmd2ArgumentParser, ansi, set_default_argument_parser_type, ) # First define the parser class CustomParser(Cmd2ArgumentParser): """Overrides error class""" def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) def error(self, message: str) -> None: """Custom override that applies custom formatting to the error message""" lines = message.split('\n') linum = 0 formatted_message = '' for line in lines: if linum == 0: formatted_message = 'Error: ' + line else: formatted_message += '\n ' + line linum += 1 self.print_usage(sys.stderr) # Format errors with style_warning() formatted_message = ansi.style_warning(formatted_message) self.exit(2, '{}\n\n'.format(formatted_message)) # Now set the default parser for a cmd2 app set_default_argument_parser_type(CustomParser) cmd2-2.3.3/examples/decorator_example.py000077500000000000000000000104271416142110700202350ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A sample application showing how to use cmd2's argparse decorators to process command line arguments for your application. Thanks to cmd2's built-in transcript testing capability, it also serves as a test suite when used with the exampleSession.txt transcript. Running `python decorator_example.py -t exampleSession.txt` will run all the commands in the transcript against decorator_example.py, verifying that the output produced matches the transcript. """ import argparse from typing import ( List, ) import cmd2 class CmdLineApp(cmd2.Cmd): """Example cmd2 application.""" def __init__(self, ip_addr=None, port=None, transcript_files=None): shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'&': 'speak'}) super().__init__(transcript_files=transcript_files, multiline_commands=['orate'], shortcuts=shortcuts) self.maxrepeats = 3 # Make maxrepeats settable at runtime self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) # Example of args set from the command-line (but they aren't being used here) self._ip = ip_addr self._port = port # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # self.default_to_shell = True speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args: argparse.Namespace): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): self.poutput(' '.join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input tag_parser = cmd2.Cmd2ArgumentParser() tag_parser.add_argument('tag', help='tag') tag_parser.add_argument('content', nargs='+', help='content to surround with tag') @cmd2.with_argparser(tag_parser) def do_tag(self, args: argparse.Namespace): """create an html tag""" # The Namespace always includes the Statement object created when parsing the command line statement = args.cmd2_statement.get() self.poutput("The command line you ran was: {}".format(statement.command_and_args)) self.poutput("It generated this tag:") self.poutput('<{0}>{1}'.format(args.tag, ' '.join(args.content))) @cmd2.with_argument_list def do_tagg(self, arglist: List[str]): """version of creating an html tag using arglist instead of argparser""" if len(arglist) >= 2: tag = arglist[0] content = arglist[1:] self.poutput('<{0}>{1}'.format(tag, ' '.join(content))) else: self.perror("tagg requires at least 2 arguments") if __name__ == '__main__': import sys # You can do your custom Argparse parsing here to meet your application's needs parser = cmd2.Cmd2ArgumentParser(description='Process the arguments however you like.') # Add a few arguments which aren't really used, but just to get the gist parser.add_argument('-p', '--port', type=int, help='TCP port') parser.add_argument('-i', '--ip', type=str, help='IPv4 address') # Add an argument which enables transcript testing args, unknown_args = parser.parse_known_args() port = None if args.port: port = args.port ip_addr = None if args.ip: ip_addr = args.ip # Perform surgery on sys.argv to remove the arguments which have already been processed by argparse sys.argv = sys.argv[:1] + unknown_args # Instantiate your cmd2 application c = CmdLineApp() # And run your cmd2 application sys.exit(c.cmdloop()) cmd2-2.3.3/examples/default_categories.py000066400000000000000000000046521416142110700203710ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """ Simple example demonstrating basic CommandSet usage. """ import cmd2 from cmd2 import ( CommandSet, with_default_category, ) @with_default_category('Default Category') class MyBaseCommandSet(CommandSet): """Defines a default category for all sub-class CommandSets""" pass class ChildInheritsParentCategories(MyBaseCommandSet): """ This subclass doesn't declare any categories so all commands here are also categorized under 'Default Category' """ def do_hello(self, _: cmd2.Statement): self._cmd.poutput('Hello') def do_world(self, _: cmd2.Statement): self._cmd.poutput('World') @with_default_category('Non-Heritable Category', heritable=False) class ChildOverridesParentCategoriesNonHeritable(MyBaseCommandSet): """ This subclass overrides the 'Default Category' from the parent, but in a non-heritable fashion. Sub-classes of this CommandSet will not inherit this category and will, instead, inherit 'Default Category' """ def do_goodbye(self, _: cmd2.Statement): self._cmd.poutput('Goodbye') class GrandchildInheritsGrandparentCategory(ChildOverridesParentCategoriesNonHeritable): """ This subclass's parent class declared its default category non-heritable. Instead, it inherits the category defined by the grandparent class. """ def do_aloha(self, _: cmd2.Statement): self._cmd.poutput('Aloha') @with_default_category('Heritable Category') class ChildOverridesParentCategories(MyBaseCommandSet): """ This subclass is decorated with a default category that is heritable. This overrides the parent class's default category declaration. """ def do_bonjour(self, _: cmd2.Statement): self._cmd.poutput('Bonjour') class GrandchildInheritsHeritable(ChildOverridesParentCategories): """ This subclass's parent declares a default category that overrides its parent. As a result, commands in this CommandSet will be categorized under 'Heritable Category' """ def do_monde(self, _: cmd2.Statement): self._cmd.poutput('Monde') class ExampleApp(cmd2.Cmd): """ Example to demonstrate heritable default categories """ def __init__(self): super(ExampleApp, self).__init__() def do_something(self, arg): self.poutput('this is the something command') if __name__ == '__main__': app = ExampleApp() app.cmdloop() cmd2-2.3.3/examples/dynamic_commands.py000077500000000000000000000031151416142110700200410ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """A simple example demonstrating how do_* commands can be created in a loop. """ import functools import cmd2 from cmd2.constants import ( COMMAND_FUNC_PREFIX, HELP_FUNC_PREFIX, ) COMMAND_LIST = ['foo', 'bar'] CATEGORY = 'Dynamic Commands' class CommandsInLoop(cmd2.Cmd): """Example of dynamically adding do_* commands.""" def __init__(self): # Add dynamic commands before calling cmd2.Cmd's init since it validates command names for command in COMMAND_LIST: # Create command function and add help category to it cmd_func = functools.partial(self.send_text, text=command) cmd2.categorize(cmd_func, CATEGORY) # Add command function to CLI object cmd_func_name = COMMAND_FUNC_PREFIX + command setattr(self, cmd_func_name, cmd_func) # Add help function to CLI object help_func = functools.partial(self.text_help, text=command) help_func_name = HELP_FUNC_PREFIX + command setattr(self, help_func_name, help_func) super().__init__(include_ipy=True) def send_text(self, args: cmd2.Statement, *, text: str): """Simulate sending text to a server and printing the response.""" self.poutput(text.capitalize()) def text_help(self, *, text: str): """Deal with printing help for the dynamically added commands.""" self.poutput("Simulate sending {!r} to a server and printing the response".format(text)) if __name__ == '__main__': app = CommandsInLoop() app.cmdloop() cmd2-2.3.3/examples/environment.py000077500000000000000000000021631416142110700171020ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2 demonstrating customized environment parameters """ import cmd2 class EnvironmentApp(cmd2.Cmd): """Example cmd2 application.""" def __init__(self): super().__init__() self.degrees_c = 22 self.sunny = False self.add_settable( cmd2.Settable('degrees_c', int, 'Temperature in Celsius', self, onchange_cb=self._onchange_degrees_c) ) self.add_settable(cmd2.Settable('sunny', bool, 'Is it sunny outside?', self)) def do_sunbathe(self, arg): """Attempt to sunbathe.""" if self.degrees_c < 20: result = "It's {} C - are you a penguin?".format(self.degrees_c) elif not self.sunny: result = 'Too dim.' else: result = 'UV is bad for your skin.' self.poutput(result) def _onchange_degrees_c(self, param_name, old, new): # if it's over 40C, it's gotta be sunny, right? if new > 40: self.sunny = True if __name__ == '__main__': import sys c = EnvironmentApp() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/event_loops.py000077500000000000000000000015651416142110700171000ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A sample application for integrating cmd2 with external event loops. This is an example of how to use cmd2 in a way so that cmd2 doesn't own the inner event loop of your application. This opens up the possibility of registering cmd2 input with event loops, like asyncio, without occupying the main loop. """ import cmd2 class Cmd2EventBased(cmd2.Cmd): """Basic example of how to run cmd2 without it controlling the main loop.""" def __init__(self): super().__init__() # ... your class code here ... if __name__ == '__main__': app = Cmd2EventBased() app.preloop() # Do this within whatever event loop mechanism you wish to run a single command. # In this case, no prompt is generated, so you need to provide one and read the user's input. app.onecmd_plus_hooks("help history") app.postloop() cmd2-2.3.3/examples/example.py000077500000000000000000000061351416142110700161740ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2. Thanks to cmd2's built-in transcript testing capability, it also serves as a test suite for example.py when used with the transcript_regex.txt transcript. Running `python example.py -t transcript_regex.txt` will run all the commands in the transcript against example.py, verifying that the output produced matches the transcript. """ import random import cmd2 class CmdLineApp(cmd2.Cmd): """Example cmd2 application.""" # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh'] MUMBLE_FIRST = ['so', 'like', 'well'] MUMBLE_LAST = ['right?'] def __init__(self): shortcuts = cmd2.DEFAULT_SHORTCUTS shortcuts.update({'&': 'speak'}) super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) # Make maxrepeats settable at runtime self.maxrepeats = 3 self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input mumble_parser = cmd2.Cmd2ArgumentParser() mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat') mumble_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(mumble_parser) def do_mumble(self, args): """Mumbles what you tell me to.""" repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): output = [] if random.random() < 0.33: output.append(random.choice(self.MUMBLE_FIRST)) for word in args.words: if random.random() < 0.40: output.append(random.choice(self.MUMBLES)) output.append(word) if random.random() < 0.25: output.append(random.choice(self.MUMBLE_LAST)) self.poutput(' '.join(output)) if __name__ == '__main__': import sys c = CmdLineApp() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/exit_code.py000077500000000000000000000021771416142110700165060ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A simple example demonstrating the following how to emit a non-zero exit code in your cmd2 application. """ from typing import ( List, ) import cmd2 class ReplWithExitCode(cmd2.Cmd): """Example cmd2 application where we can specify an exit code when existing.""" def __init__(self): super().__init__() @cmd2.with_argument_list def do_exit(self, arg_list: List[str]) -> bool: """Exit the application with an optional exit code. Usage: exit [exit_code] Where: * exit_code - integer exit code to return to the shell""" # If an argument was provided if arg_list: try: self.exit_code = int(arg_list[0]) except ValueError: self.perror("{} isn't a valid integer exit code".format(arg_list[0])) self.exit_code = 1 return True if __name__ == '__main__': import sys app = ReplWithExitCode() sys_exit_code = app.cmdloop() app.poutput('{!r} exiting with code: {}'.format(sys.argv[0], sys_exit_code)) sys.exit(sys_exit_code) cmd2-2.3.3/examples/first_app.py000077500000000000000000000034351416142110700165300ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A simple application using cmd2 which demonstrates 8 key features: * Settings * Commands * Argument Parsing * Generating Output * Help * Shortcuts * Multiline Commands * History """ import cmd2 class FirstApp(cmd2.Cmd): """A simple cmd2 application.""" def __init__(self): shortcuts = cmd2.DEFAULT_SHORTCUTS shortcuts.update({'&': 'speak'}) super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) # Make maxrepeats settable at runtime self.maxrepeats = 3 self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) speak_parser = cmd2.Cmd2ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) def do_speak(self, args): """Repeats what you tell me to.""" words = [] for word in args.words: if args.piglatin: word = '%s%say' % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too self.poutput(' '.join(words)) # orate is a synonym for speak which takes multiline input do_orate = do_speak if __name__ == '__main__': import sys c = FirstApp() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/hello_cmd2.py000077500000000000000000000012511416142110700165430ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ This is intended to be a completely bare-bones cmd2 application suitable for rapid testing and debugging. """ from cmd2 import ( cmd2, ) if __name__ == '__main__': import sys # If run as the main application, simply start a bare-bones cmd2 application with only built-in functionality. # Enable commands to support interactive Python and IPython shells. app = cmd2.Cmd(include_py=True, include_ipy=True, persistent_history_file='cmd2_history.dat') app.self_in_py = True # Enable access to "self" within the py command app.debug = True # Show traceback if/when an exception occurs sys.exit(app.cmdloop()) cmd2-2.3.3/examples/help_categories.py000077500000000000000000000124511416142110700176740ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for tagging categories on commands. It also demonstrates the effects of decorator order when it comes to argparse errors occurring. """ import functools import cmd2 from cmd2 import ( COMMAND_NAME, argparse_custom, ) def my_decorator(f): @functools.wraps(f) def wrapper(*args, **kwds): print('Calling decorated function') return f(*args, **kwds) return wrapper class HelpCategories(cmd2.Cmd): """Example cmd2 application.""" START_TIMES = ['now', 'later', 'sometime', 'whenever'] # Command categories CMD_CAT_CONNECTING = 'Connecting' CMD_CAT_APP_MGMT = 'Application Management' CMD_CAT_SERVER_INFO = 'Server Information' def __init__(self): super().__init__() def do_connect(self, _): """Connect command""" self.poutput('Connect') # Tag the above command functions under the category Connecting cmd2.categorize(do_connect, CMD_CAT_CONNECTING) @cmd2.with_category(CMD_CAT_CONNECTING) def do_which(self, _): """Which command""" self.poutput('Which') def do_list(self, _): """List command""" self.poutput('List') def do_deploy(self, _): """Deploy command""" self.poutput('Deploy') start_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description='Start', epilog='my_decorator runs even with argparse errors' ) start_parser.add_argument('when', choices=START_TIMES, help='Specify when to start') @my_decorator @cmd2.with_argparser(start_parser) def do_start(self, _): """Start command""" self.poutput('Start') def do_sessions(self, _): """Sessions command""" self.poutput('Sessions') def do_redeploy(self, _): """Redeploy command""" self.poutput('Redeploy') restart_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER( description='Restart', epilog='my_decorator does not run when argparse errors' ) restart_parser.add_argument('when', choices=START_TIMES, help='Specify when to restart') @cmd2.with_argparser(restart_parser) @cmd2.with_category(CMD_CAT_APP_MGMT) @my_decorator def do_restart(self, _): """Restart command""" self.poutput('Restart') def do_expire(self, _): """Expire command""" self.poutput('Expire') def do_undeploy(self, _): """Undeploy command""" self.poutput('Undeploy') def do_stop(self, _): """Stop command""" self.poutput('Stop') def do_findleakers(self, _): """Find Leakers command""" self.poutput('Find Leakers') # Tag the above command functions under the category Application Management cmd2.categorize( (do_list, do_deploy, do_start, do_sessions, do_redeploy, do_expire, do_undeploy, do_stop, do_findleakers), CMD_CAT_APP_MGMT, ) def do_resources(self, _): """Resources command""" self.poutput('Resources') def do_status(self, _): """Status command""" self.poutput('Status') def do_serverinfo(self, _): """Server Info command""" self.poutput('Server Info') def do_thread_dump(self, _): """Thread Dump command""" self.poutput('Thread Dump') def do_sslconnectorciphers(self, _): """ SSL Connector Ciphers command is an example of a command that contains multiple lines of help information for the user. Each line of help in a contiguous set of lines will be printed and aligned in the verbose output provided with 'help --verbose' This is after a blank line and won't de displayed in the verbose help """ self.poutput('SSL Connector Ciphers') def do_vminfo(self, _): """VM Info command""" self.poutput('VM Info') # Tag the above command functions under the category Server Information cmd2.categorize(do_resources, CMD_CAT_SERVER_INFO) cmd2.categorize(do_status, CMD_CAT_SERVER_INFO) cmd2.categorize(do_serverinfo, CMD_CAT_SERVER_INFO) cmd2.categorize(do_thread_dump, CMD_CAT_SERVER_INFO) cmd2.categorize(do_sslconnectorciphers, CMD_CAT_SERVER_INFO) cmd2.categorize(do_vminfo, CMD_CAT_SERVER_INFO) # The following command functions don't have the HELP_CATEGORY attribute set # and show up in the 'Other' group def do_config(self, _): """Config command""" self.poutput('Config') def do_version(self, _): """Version command""" self.poutput(cmd2.__version__) @cmd2.with_category("Command Management") def do_disable_commands(self, _): """Disable the Application Management commands""" message_to_print = "{} is not available while {} commands are disabled".format(COMMAND_NAME, self.CMD_CAT_APP_MGMT) self.disable_category(self.CMD_CAT_APP_MGMT, message_to_print) self.poutput("The Application Management commands have been disabled") @cmd2.with_category("Command Management") def do_enable_commands(self, _): """Enable the Application Management commands""" self.enable_category(self.CMD_CAT_APP_MGMT) self.poutput("The Application Management commands have been enabled") if __name__ == '__main__': import sys c = HelpCategories() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/hooks.py000077500000000000000000000072701416142110700156650ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for cmd2 demonstrating how to use hooks. This application shows how to use postparsing hooks to allow case insensitive command names, abbreviated commands, as well as allowing numeric arguments to follow a command without any intervening whitespace. """ import re from typing import ( List, ) import cmd2 class CmdLineApp(cmd2.Cmd): """Example cmd2 application demonstrating the use of hooks. This simple application has one command, `list` which generates a list of 10 numbers. This command takes one optional argument, which is the number to start on. We have three postparsing hooks, which allow the user to enter: (Cmd) list 5 (Cmd) L 5 (Cmd) l 5 (Cmd) L5 (Cmd) LI5 and have them all treated as valid input which prints a list of 10 numbers starting with the number 5. """ # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # register three hooks self.register_postparsing_hook(self.add_whitespace_hook) self.register_postparsing_hook(self.downcase_hook) self.register_postparsing_hook(self.abbrev_hook) def add_whitespace_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """A hook to split alphabetic command names immediately followed by a number. l24 -> l 24 list24 -> list 24 list 24 -> list 24 """ command = data.statement.command # regular expression with looks for: # ^ - the beginning of the string # ([^\s\d]+) - one or more non-whitespace non-digit characters, set as capture group 1 # (\d+) - one or more digit characters, set as capture group 2 command_pattern = re.compile(r'^([^\s\d]+)(\d+)') match = command_pattern.search(command) if match: data.statement = self.statement_parser.parse( "{} {} {}".format(match.group(1), match.group(2), '' if data.statement.args is None else data.statement.args) ) return data def downcase_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """A hook to make uppercase commands lowercase.""" command = data.statement.command.lower() data.statement = self.statement_parser.parse( "{} {}".format(command, '' if data.statement.args is None else data.statement.args) ) return data def abbrev_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """Accept unique abbreviated commands""" func = self.cmd_func(data.statement.command) if func is None: # check if the entered command might be an abbreviation possible_cmds = [cmd for cmd in self.get_all_commands() if cmd.startswith(data.statement.command)] if len(possible_cmds) == 1: raw = data.statement.raw.replace(data.statement.command, possible_cmds[0], 1) data.statement = self.statement_parser.parse(raw) return data @cmd2.with_argument_list def do_list(self, arglist: List[str]) -> None: """Generate a list of 10 numbers.""" if arglist: first = arglist[0] try: first = int(first) except ValueError: first = 1 else: first = 1 last = first + 10 for x in range(first, last): self.poutput(str(x)) if __name__ == '__main__': import sys c = CmdLineApp() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/initialization.py000077500000000000000000000044001416142110700175610ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """A simple example cmd2 application demonstrating the following: 1) Colorizing/stylizing output 2) Using multiline commands 3) Persistent history 4) How to run an initialization script at startup 5) How to group and categorize commands when displaying them in help 6) Opting-in to using the ipy command to run an IPython shell 7) Allowing access to your application in py and ipy 8) Displaying an intro banner upon starting your application 9) Using a custom prompt 10) How to make custom attributes settable at runtime """ import cmd2 from cmd2 import ( Bg, Fg, style, ) class BasicApp(cmd2.Cmd): CUSTOM_CATEGORY = 'My Custom Commands' def __init__(self): super().__init__( multiline_commands=['echo'], persistent_history_file='cmd2_history.dat', startup_script='scripts/startup.txt', include_ipy=True, ) # Prints an intro banner once upon application startup self.intro = style('Welcome to cmd2!', fg=Fg.RED, bg=Bg.WHITE, bold=True) # Show this as the prompt when asking for input self.prompt = 'myapp> ' # Used as prompt for multiline commands after the first line self.continuation_prompt = '... ' # Allow access to your application in py and ipy via self self.self_in_py = True # Set the default category name self.default_category = 'cmd2 Built-in Commands' # Color to output text in with echo command self.foreground_color = Fg.CYAN.name.lower() # Make echo_fg settable at runtime fg_colors = [c.name.lower() for c in Fg] self.add_settable( cmd2.Settable('foreground_color', str, 'Foreground color to use with echo command', self, choices=fg_colors) ) @cmd2.with_category(CUSTOM_CATEGORY) def do_intro(self, _): """Display the intro banner""" self.poutput(self.intro) @cmd2.with_category(CUSTOM_CATEGORY) def do_echo(self, arg): """Example of a multiline command""" fg_color = Fg[self.foreground_color.upper()] self.poutput(style(arg, fg=fg_color)) if __name__ == '__main__': app = BasicApp() app.cmdloop() cmd2-2.3.3/examples/migrating.py000077500000000000000000000023051416142110700165150ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A sample application for cmd which can be used to show how to migrate to cmd2. """ import cmd import random class CmdLineApp(cmd.Cmd): """Example cmd application.""" MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh'] MUMBLE_FIRST = ['so', 'like', 'well'] MUMBLE_LAST = ['right?'] def do_exit(self, line): """Exit the application""" return True do_EOF = do_exit do_quit = do_exit def do_speak(self, line): """Repeats what you tell me to.""" print(line, file=self.stdout) do_say = do_speak def do_mumble(self, line): """Mumbles what you tell me to.""" words = line.split(' ') output = [] if random.random() < 0.33: output.append(random.choice(self.MUMBLE_FIRST)) for word in words: if random.random() < 0.40: output.append(random.choice(self.MUMBLES)) output.append(word) if random.random() < 0.25: output.append(random.choice(self.MUMBLE_LAST)) print(' '.join(output), file=self.stdout) if __name__ == '__main__': import sys c = CmdLineApp() sys.exit(c.cmdloop()) cmd2-2.3.3/examples/modular_commands/000077500000000000000000000000001416142110700175035ustar00rootroot00000000000000cmd2-2.3.3/examples/modular_commands/__init__.py000066400000000000000000000000001416142110700216020ustar00rootroot00000000000000cmd2-2.3.3/examples/modular_commands/commandset_basic.py000066400000000000000000000076341416142110700233620ustar00rootroot00000000000000# coding=utf-8 """ A simple example demonstrating a loadable command set """ from typing import ( List, ) from cmd2 import ( Cmd, CommandSet, CompletionError, Statement, with_category, with_default_category, ) @with_default_category('Basic Completion') class BasicCompletionCommandSet(CommandSet): # List of strings used with completion functions food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] # This data is used to demonstrate delimiter_complete file_strs = [ '/home/user/file.db', '/home/user/file space.db', '/home/user/another.db', '/home/other user/maps.db', '/home/other user/tests.db', ] def do_flag_based(self, cmd: Cmd, statement: Statement): """Tab completes arguments based on a preceding flag using flag_based_complete -f, --food [completes food items] -s, --sport [completes sports] -p, --path [completes local file system paths] """ self._cmd.poutput("Args: {}".format(statement.args)) def complete_flag_based(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: """Completion function for do_flag_based""" flag_dict = { # Tab complete food items after -f and --food flags in command line '-f': self.food_item_strs, '--food': self.food_item_strs, # Tab complete sport items after -s and --sport flags in command line '-s': self.sport_item_strs, '--sport': self.sport_item_strs, # Tab complete using path_complete function after -p and --path flags in command line '-p': cmd.path_complete, '--path': cmd.path_complete, } return cmd.flag_based_complete(text, line, begidx, endidx, flag_dict=flag_dict) def do_index_based(self, cmd: Cmd, statement: Statement): """Tab completes first 3 arguments using index_based_complete""" self._cmd.poutput("Args: {}".format(statement.args)) def complete_index_based(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: """Completion function for do_index_based""" index_dict = { 1: self.food_item_strs, # Tab complete food items at index 1 in command line 2: self.sport_item_strs, # Tab complete sport items at index 2 in command line 3: cmd.path_complete, # Tab complete using path_complete function at index 3 in command line } return cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict) def do_delimiter_complete(self, cmd: Cmd, statement: Statement): """Tab completes files from a list using delimiter_complete""" self._cmd.poutput("Args: {}".format(statement.args)) def complete_delimiter_complete(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: return cmd.delimiter_complete(text, line, begidx, endidx, match_against=self.file_strs, delimiter='/') def do_raise_error(self, cmd: Cmd, statement: Statement): """Demonstrates effect of raising CompletionError""" self._cmd.poutput("Args: {}".format(statement.args)) def complete_raise_error(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: """ CompletionErrors can be raised if an error occurs while tab completing. Example use cases - Reading a database to retrieve a tab completion data set failed - A previous command line argument that determines the data set being completed is invalid """ raise CompletionError("This is how a CompletionError behaves") @with_category('Not Basic Completion') def do_custom_category(self, cmd: Cmd, statement: Statement): self._cmd.poutput('Demonstrates a command that bypasses the default category') cmd2-2.3.3/examples/modular_commands/commandset_complex.py000066400000000000000000000034031416142110700237360ustar00rootroot00000000000000# coding=utf-8 # flake8: noqa E302 """ Test CommandSet """ import argparse from typing import ( List, ) import cmd2 @cmd2.with_default_category('Fruits') class CommandSetA(cmd2.CommandSet): def do_apple(self, statement: cmd2.Statement): self._cmd.poutput('Apple!') def do_banana(self, statement: cmd2.Statement): """Banana Command""" self._cmd.poutput('Banana!!') cranberry_parser = cmd2.Cmd2ArgumentParser() cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce']) @cmd2.with_argparser(cranberry_parser, with_unknown_args=True) def do_cranberry(self, ns: argparse.Namespace, unknown: List[str]): self._cmd.poutput('Cranberry {}!!'.format(ns.arg1)) if unknown and len(unknown): self._cmd.poutput('Unknown: ' + ', '.join(['{}'] * len(unknown)).format(*unknown)) self._cmd.last_result = {'arg1': ns.arg1, 'unknown': unknown} def help_cranberry(self): self._cmd.stdout.write('This command does diddly squat...\n') @cmd2.with_argument_list @cmd2.with_category('Also Alone') def do_durian(self, args: List[str]): """Durian Command""" self._cmd.poutput('{} Arguments: '.format(len(args))) self._cmd.poutput(', '.join(['{}'] * len(args)).format(*args)) def complete_durian(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: return self._cmd.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) elderberry_parser = cmd2.Cmd2ArgumentParser() elderberry_parser.add_argument('arg1') @cmd2.with_category('Alone') @cmd2.with_argparser(elderberry_parser) def do_elderberry(self, ns: argparse.Namespace): self._cmd.poutput('Elderberry {}!!'.format(ns.arg1)) cmd2-2.3.3/examples/modular_commands/commandset_custominit.py000066400000000000000000000010651416142110700244670ustar00rootroot00000000000000# coding=utf-8 """ A simple example demonstrating a loadable command set """ from cmd2 import ( Cmd, CommandSet, Statement, with_default_category, ) @with_default_category('Custom Init') class CustomInitCommandSet(CommandSet): def __init__(self, arg1, arg2): super().__init__() self._arg1 = arg1 self._arg2 = arg2 def do_show_arg1(self, cmd: Cmd, _: Statement): self._cmd.poutput('Arg1: ' + self._arg1) def do_show_arg2(self, cmd: Cmd, _: Statement): self._cmd.poutput('Arg2: ' + self._arg2) cmd2-2.3.3/examples/modular_commands_basic.py000066400000000000000000000014431416142110700212200ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """ Simple example demonstrating basic CommandSet usage. """ import cmd2 from cmd2 import ( CommandSet, with_default_category, ) @with_default_category('My Category') class AutoLoadCommandSet(CommandSet): def __init__(self): super().__init__() def do_hello(self, _: cmd2.Statement): self._cmd.poutput('Hello') def do_world(self, _: cmd2.Statement): self._cmd.poutput('World') class ExampleApp(cmd2.Cmd): """ CommandSets are automatically loaded. Nothing needs to be done. """ def __init__(self): super(ExampleApp, self).__init__() def do_something(self, arg): self.poutput('this is the something command') if __name__ == '__main__': app = ExampleApp() app.cmdloop() cmd2-2.3.3/examples/modular_commands_dynamic.py000066400000000000000000000050431416142110700215630ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """ Simple example demonstrating dynamic CommandSet loading and unloading. There are 2 CommandSets defined. ExampleApp sets the `auto_load_commands` flag to false. The `load` and `unload` commands will load and unload the CommandSets. The available commands will change depending on which CommandSets are loaded """ import argparse import cmd2 from cmd2 import ( CommandSet, with_argparser, with_category, with_default_category, ) @with_default_category('Fruits') class LoadableFruits(CommandSet): def __init__(self): super().__init__() def do_apple(self, _: cmd2.Statement): self._cmd.poutput('Apple') def do_banana(self, _: cmd2.Statement): self._cmd.poutput('Banana') @with_default_category('Vegetables') class LoadableVegetables(CommandSet): def __init__(self): super().__init__() def do_arugula(self, _: cmd2.Statement): self._cmd.poutput('Arugula') def do_bokchoy(self, _: cmd2.Statement): self._cmd.poutput('Bok Choy') class ExampleApp(cmd2.Cmd): """ CommandSets are loaded via the `load` and `unload` commands """ def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, auto_load_commands=False, **kwargs) self._fruits = LoadableFruits() self._vegetables = LoadableVegetables() load_parser = cmd2.Cmd2ArgumentParser() load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) @with_argparser(load_parser) @with_category('Command Loading') def do_load(self, ns: argparse.Namespace): if ns.cmds == 'fruits': try: self.register_command_set(self._fruits) self.poutput('Fruits loaded') except ValueError: self.poutput('Fruits already loaded') if ns.cmds == 'vegetables': try: self.register_command_set(self._vegetables) self.poutput('Vegetables loaded') except ValueError: self.poutput('Vegetables already loaded') @with_argparser(load_parser) def do_unload(self, ns: argparse.Namespace): if ns.cmds == 'fruits': self.unregister_command_set(self._fruits) self.poutput('Fruits unloaded') if ns.cmds == 'vegetables': self.unregister_command_set(self._vegetables) self.poutput('Vegetables unloaded') if __name__ == '__main__': app = ExampleApp() app.cmdloop() cmd2-2.3.3/examples/modular_commands_main.py000066400000000000000000000045131416142110700210640ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ A complex example demonstrating a variety of methods to load CommandSets using a mix of command decorators with examples of how to integrate tab completion with argparse-based commands. """ import argparse from typing import ( Iterable, List, Optional, ) from modular_commands.commandset_basic import ( # noqa: F401 BasicCompletionCommandSet, ) from modular_commands.commandset_complex import ( # noqa: F401 CommandSetA, ) from modular_commands.commandset_custominit import ( # noqa: F401 CustomInitCommandSet, ) from cmd2 import ( Cmd, Cmd2ArgumentParser, CommandSet, with_argparser, ) class WithCommandSets(Cmd): def __init__(self, command_sets: Optional[Iterable[CommandSet]] = None): super().__init__(command_sets=command_sets) self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] def choices_provider(self) -> List[str]: """A choices provider is useful when the choice list is based on instance data of your application""" return self.sport_item_strs # Parser for example command example_parser = Cmd2ArgumentParser( description="Command demonstrating tab completion with argparse\n" "Notice even the flags of this command tab complete" ) # Tab complete from a list using argparse choices. Set metavar if you don't # want the entire choices list showing in the usage text for this command. example_parser.add_argument( '--choices', choices=['some', 'choices', 'here'], metavar="CHOICE", help="tab complete using choices" ) # Tab complete from choices provided by a choices provider example_parser.add_argument( '--choices_provider', choices_provider=choices_provider, help="tab complete using a choices_provider" ) # Tab complete using a completer example_parser.add_argument('--completer', completer=Cmd.path_complete, help="tab complete using a completer") @with_argparser(example_parser) def do_example(self, _: argparse.Namespace) -> None: """The example command""" self.poutput("I do nothing") if __name__ == '__main__': import sys print("Starting") my_sets = [CustomInitCommandSet('First argument', 'Second argument')] app = WithCommandSets(command_sets=my_sets) sys.exit(app.cmdloop()) cmd2-2.3.3/examples/modular_subcommands.py000066400000000000000000000076401416142110700205760ustar00rootroot00000000000000#!/usr/bin/env python3 # coding=utf-8 """A simple example demonstrating modular subcommand loading through CommandSets In this example, there are loadable CommandSets defined. Each CommandSet has 1 subcommand defined that will be attached to the 'cut' command. The cut command is implemented with the `do_cut` function that has been tagged as an argparse command. The `load` and `unload` command will load and unload the CommandSets. The available top level commands as well as subcommands to the `cut` command will change depending on which CommandSets are loaded. """ import argparse import cmd2 from cmd2 import ( CommandSet, with_argparser, with_category, with_default_category, ) @with_default_category('Fruits') class LoadableFruits(CommandSet): def __init__(self): super().__init__() def do_apple(self, _: cmd2.Statement): self._cmd.poutput('Apple') banana_description = "Cut a banana" banana_parser = cmd2.Cmd2ArgumentParser(description=banana_description) banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) @cmd2.as_subcommand_to('cut', 'banana', banana_parser, help=banana_description.lower()) def cut_banana(self, ns: argparse.Namespace): """Cut banana""" self._cmd.poutput('cutting banana: ' + ns.direction) @with_default_category('Vegetables') class LoadableVegetables(CommandSet): def __init__(self): super().__init__() def do_arugula(self, _: cmd2.Statement): self._cmd.poutput('Arugula') bokchoy_description = "Cut some bokchoy" bokchoy_parser = cmd2.Cmd2ArgumentParser(description=bokchoy_description) bokchoy_parser.add_argument('style', choices=['quartered', 'diced']) @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser, help=bokchoy_description.lower()) def cut_bokchoy(self, _: argparse.Namespace): self._cmd.poutput('Bok Choy') class ExampleApp(cmd2.Cmd): """ CommandSets are automatically loaded. Nothing needs to be done. """ def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, auto_load_commands=False, **kwargs) self._fruits = LoadableFruits() self._vegetables = LoadableVegetables() load_parser = cmd2.Cmd2ArgumentParser() load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) @with_argparser(load_parser) @with_category('Command Loading') def do_load(self, ns: argparse.Namespace): if ns.cmds == 'fruits': try: self.register_command_set(self._fruits) self.poutput('Fruits loaded') except ValueError: self.poutput('Fruits already loaded') if ns.cmds == 'vegetables': try: self.register_command_set(self._vegetables) self.poutput('Vegetables loaded') except ValueError: self.poutput('Vegetables already loaded') @with_argparser(load_parser) def do_unload(self, ns: argparse.Namespace): if ns.cmds == 'fruits': self.unregister_command_set(self._fruits) self.poutput('Fruits unloaded') if ns.cmds == 'vegetables': self.unregister_command_set(self._vegetables) self.poutput('Vegetables unloaded') cut_parser = cmd2.Cmd2ArgumentParser() cut_subparsers = cut_parser.add_subparsers(title='item', help='item to cut') @with_argparser(cut_parser) def do_cut(self, ns: argparse.Namespace): # Call handler for whatever subcommand was selected handler = ns.cmd2_handler.get() if handler is not None: handler(ns) else: # No subcommand was provided, so call help self.poutput('This command does nothing without sub-parsers registered') self.do_help('cut') if __name__ == '__main__': app = ExampleApp() app.cmdloop() cmd2-2.3.3/examples/override_parser.py000077500000000000000000000020041416142110700177230ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 # flake8: noqa F402 """ The standard parser used by cmd2 built-in commands is Cmd2ArgumentParser. The following code shows how to override it with your own parser class. """ # First set a value called argparse.cmd2_parser_module with the module that defines the custom parser. # See the code for custom_parser.py. It simply defines a parser and calls cmd2.set_default_argument_parser_type() # with the custom parser's type. import argparse argparse.cmd2_parser_module = 'examples.custom_parser' # Next import from cmd2. It will import your module just before the cmd2.Cmd class file is imported # and therefore override the parser class it uses on its commands. from cmd2 import ( cmd2, ) if __name__ == '__main__': import sys app = cmd2.Cmd(include_ipy=True, persistent_history_file='cmd2_history.dat') app.self_in_py = True # Enable access to "self" within the py command app.debug = True # Show traceback if/when an exception occurs sys.exit(app.cmdloop()) cmd2-2.3.3/examples/paged_output.py000077500000000000000000000034671416142110700172460ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A simple example demonstrating the using paged output via the ppaged() method. """ import os from typing import ( List, ) import cmd2 class PagedOutput(cmd2.Cmd): """Example cmd2 application which shows how to display output using a pager.""" def __init__(self): super().__init__() def page_file(self, file_path: str, chop: bool = False): """Helper method to prevent having too much duplicated code.""" filename = os.path.expanduser(file_path) try: with open(filename, 'r') as f: text = f.read() self.ppaged(text, chop=chop) except OSError as ex: self.pexcept('Error reading {!r}: {}'.format(filename, ex)) @cmd2.with_argument_list def do_page_wrap(self, args: List[str]): """Read in a text file and display its output in a pager, wrapping long lines if they don't fit. Usage: page_wrap """ if not args: self.perror('page_wrap requires a path to a file as an argument') return self.page_file(args[0], chop=False) complete_page_wrap = cmd2.Cmd.path_complete @cmd2.with_argument_list def do_page_truncate(self, args: List[str]): """Read in a text file and display its output in a pager, truncating long lines if they don't fit. Truncated lines can still be accessed by scrolling to the right using the arrow keys. Usage: page_chop """ if not args: self.perror('page_truncate requires a path to a file as an argument') return self.page_file(args[0], chop=True) complete_page_truncate = cmd2.Cmd.path_complete if __name__ == '__main__': import sys app = PagedOutput() sys.exit(app.cmdloop()) cmd2-2.3.3/examples/persistent_history.py000077500000000000000000000022321416142110700205140ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """This example demonstrates how to enable persistent readline history in your cmd2 application. This will allow end users of your cmd2-based application to use the arrow keys and Ctrl+r in a manner which persists across invocations of your cmd2 application. This can make it much easier for them to use your application. """ import cmd2 class Cmd2PersistentHistory(cmd2.Cmd): """Basic example of how to enable persistent readline history within your cmd2 app.""" def __init__(self, hist_file): """Configure the app to load persistent history from a file (both readline and cmd2 history command affected). :param hist_file: file to load history from at start and write it to at end """ super().__init__(persistent_history_file=hist_file, persistent_history_length=500, allow_cli_args=False) self.prompt = 'ph> ' # ... your class code here ... if __name__ == '__main__': import sys history_file = '~/.persistent_history.cmd2' if len(sys.argv) > 1: history_file = sys.argv[1] app = Cmd2PersistentHistory(hist_file=history_file) sys.exit(app.cmdloop()) cmd2-2.3.3/examples/pirate.py000077500000000000000000000063351416142110700160270ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """ This example is adapted from the pirate8.py example created by Catherine Devlin and presented as part of her PyCon 2010 talk. It demonstrates many features of cmd2. """ import cmd2 from cmd2 import ( Fg, ) from cmd2.constants import ( MULTILINE_TERMINATOR, ) color_choices = [c.name.lower() for c in Fg] class Pirate(cmd2.Cmd): """A piratical example cmd2 application involving looting and drinking.""" def __init__(self): """Initialize the base class as well as this one""" shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) shortcuts.update({'~': 'sing'}) super().__init__(multiline_commands=['sing'], terminators=[MULTILINE_TERMINATOR, '...'], shortcuts=shortcuts) self.default_to_shell = True self.songcolor = 'blue' # Make songcolor settable at runtime self.add_settable(cmd2.Settable('songcolor', str, 'Color to ``sing``', self, choices=color_choices)) # prompts and defaults self.gold = 0 self.initial_gold = self.gold self.prompt = 'arrr> ' def precmd(self, line): """Runs just before a command line is parsed, but after the prompt is presented.""" self.initial_gold = self.gold return line def postcmd(self, stop, line): """Runs right before a command is about to return.""" if self.gold != self.initial_gold: self.poutput('Now we gots {0} doubloons'.format(self.gold)) if self.gold < 0: self.poutput("Off to debtorrr's prison.") self.exit_code = 1 stop = True return stop # noinspection PyUnusedLocal def do_loot(self, arg): """Seize booty from a passing ship.""" self.gold += 1 def do_drink(self, arg): """Drown your sorrrows in rrrum. drink [n] - drink [n] barrel[s] o' rum.""" try: self.gold -= int(arg) except ValueError: if arg: self.poutput('''What's "{0}"? I'll take rrrum.'''.format(arg)) self.gold -= 1 def do_quit(self, arg): """Quit the application gracefully.""" self.poutput("Quiterrr!") return True def do_sing(self, arg): """Sing a colorful song.""" self.poutput(cmd2.ansi.style(arg, fg=Fg[self.songcolor.upper()])) yo_parser = cmd2.Cmd2ArgumentParser() yo_parser.add_argument('--ho', type=int, default=2, help="How often to chant 'ho'") yo_parser.add_argument('-c', '--commas', action='store_true', help='Intersperse commas') yo_parser.add_argument('beverage', help='beverage to drink with the chant') @cmd2.with_argparser(yo_parser) def do_yo(self, args): """Compose a yo-ho-ho type chant with flexible options.""" chant = ['yo'] + ['ho'] * args.ho separator = ', ' if args.commas else ' ' chant = separator.join(chant) self.poutput('{0} and a bottle of {1}'.format(chant, args.beverage)) if __name__ == '__main__': import sys # Create an instance of the Pirate derived class and enter the REPL with cmdloop(). pirate = Pirate() sys_exit_code = pirate.cmdloop() print('Exiting with code: {!r}'.format(sys_exit_code)) sys.exit(sys_exit_code) cmd2-2.3.3/examples/python_scripting.py000077500000000000000000000106151416142110700201420ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 """A sample application for how Python scripting can provide conditional control flow of a cmd2 application. cmd2's built-in scripting capability, which can be invoked via the "@" shortcut or "run_script" command, uses basic ASCII/UTF-8 text scripts and is very easy to use. Moreover, the trivial syntax of the script files, where there is one command per line and the line is exactly what the user would type inside the application, makes it so non-technical that end users can quickly learn to create scripts. However, there comes a time when technical end users want more capability and power. In particular it is common that users will want to create a script with conditional control flow - where the next command run will depend on the results from the previous command. This is where the ability to run Python scripts inside a cmd2 application via the run_pyscript command and the "run_pyscript