structlog-24.4.0/.git_archival.txt0000644000000000000000000000017514645734712014050 0ustar00node: $Format:%H$ node-date: $Format:%cI$ describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ ref-names: $Format:%D$ structlog-24.4.0/.gitattributes0000644000000000000000000000021714645734712013465 0ustar00# Force LF line endings for text files * text=auto eol=lf # Needed for hatch-vcs / setuptools-scm-git-archive .git_archival.txt export-subst structlog-24.4.0/.pre-commit-config.yaml0000644000000000000000000000136414645734712015057 0ustar00--- ci: autoupdate_schedule: monthly repos: - repo: https://github.com/psf/black rev: 24.4.2 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.5.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/econchick/interrogate rev: 1.7.0 hooks: - id: interrogate args: [tests] - repo: https://github.com/codespell-project/codespell rev: v2.3.0 hooks: - id: codespell args: [-L, alog] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer exclude: docs/_static - id: check-toml - id: check-yaml structlog-24.4.0/.python-version-default0000644000000000000000000000000514645734712015214 0ustar003.12 structlog-24.4.0/.readthedocs.yaml0000644000000000000000000000063314645734712014023 0ustar00--- version: 2 build: os: ubuntu-lts-latest tools: # Keep version in sync with tox.ini/docs and ci.yml/docs python: "3.12" jobs: # Need the tags to calculate the version post_checkout: - git fetch --tags # Replace versions in sponsor URLs pre_build: - cog -rP docs/index.md python: install: - method: pip path: . extra_requirements: - docs structlog-24.4.0/CHANGELOG.md0000644000000000000000000012560214645734712012411 0ustar00# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/) and this project adheres to [Calendar Versioning](https://calver.org/). The **first number** of the version is the year. The **second number** is incremented with each release, starting at 1 for each year. The **third number** is for emergencies when we need to start branches for older releases. You can find our backwards-compatibility policy [here](https://github.com/hynek/structlog/blob/main/.github/SECURITY.md). ## [24.4.0](https://github.com/hynek/structlog/compare/24.3.0...24.4.0) - 2024-07-17 ### Changed No code changes since 24.3.0 ## [24.3.0](https://github.com/hynek/structlog/compare/24.2.0...24.3.0) - 2024-07-17 ### Added - Restore feature parity between `structlog.traceback.ExceptionDictTransformer` and Rich's traceback extractor: - When displaying locals, use Rich for formatting if it is available. - When displaying locals, call `repr()` on strings, too (improves handling of `SecretStr` implementations). - Add `locals_max_length` config option - Add `locals_hide_sunder` config option - Add `locals_hide_dunder` config option - Add `suppress` config option [#627](https://github.com/hynek/structlog/pull/627) ### Changed - `structlog.testing.capture_logs()` now maps the `exception` log level to `error` (as it's elsewhere). [#628](https://github.com/hynek/structlog/pull/628) ## [24.2.0](https://github.com/hynek/structlog/compare/24.1.0...24.2.0) - 2024-05-27 ### Added - It is now possible to disable log level-padding in `structlog.dev.LogLevelColumnFormatter` and `structlog.dev.ConsoleRenderer`. [#599](https://github.com/hynek/structlog/pull/599) - The `structlog.processors.CallsiteParameterAdder` can now be pickled. [#603](https://github.com/hynek/structlog/pull/603) - `structlog.processors.CallsiteParameterAdder` now also works with `structlog.stdlib.BoundLogger`'s non-standard async methods (`ainfo()`, and so forth) [#618](https://github.com/hynek/structlog/pull/618) ### Changed - `structlog.processors.LogfmtRenderer` now escapes newlines. [#592](https://github.com/hynek/structlog/pull/592) - `structlog.processors.LogfmtRenderer` now escapes backslashes and double quotes. [#594](https://github.com/hynek/structlog/pull/594) - `structlog.processors.CallsiteParameterAdder` has been optimized to be about 2x faster. [#606](https://github.com/hynek/structlog/pull/606) ### Fixed - `structlog.stdlib.render_to_log_kwargs` now correctly passes stacklevel as a kwarg to stdlib logging. [#619](https://github.com/hynek/structlog/pull/620) ## [24.1.0](https://github.com/hynek/structlog/compare/23.3.0...24.1.0) - 2024-01-08 ### Fixed - The lazy logger proxy returned by `structlog.get_logger()` now returns its initial values when asked for context. When asked for context before binding for the first time, it returned an empty dictionary in 23.3.0. - The displayed level name when using `structlog.stdlib.BoundLogger.exception()` is `"error"` instead of `"exception"`. Fixes regression in 23.3.0. [#584](https://github.com/hynek/structlog/issues/584) - Don't ignore the `width` argument of `RichTracebackFormatter`. [#587](https://github.com/hynek/structlog/issues/587) ## [23.3.0](https://github.com/hynek/structlog/compare/23.2.0...23.3.0) - 2023-12-29 ### Added - The colorful development logger is now even more configurable! Choose freely your colors and the order of the key-value pairs! Implement your own formatters for certain keys! Implementing the output on top of the new columns API has changed the default very slightly, but shouldn't be noticeable. [#577](https://github.com/hynek/structlog/issues/577) - Async log methods (those starting with an `a`) now also support the collection of callsite information using `structlog.processors.CallsiteParameterAdder`. [#565](https://github.com/hynek/structlog/issues/565) ### Changed - `structlog.stdlib.recreate_defaults()` now also adds `structlog.stdlib.add_logger_name` to the processors. Check out the [updated screenshot](https://raw.githubusercontent.com/hynek/structlog/main/docs/_static/console_renderer.png)! ### Fixed - The return value from `get_logger()` (a `BoundLoggerLazyProxy`) now passes `isinstance`-checks against `structlog.typing.BindableLogger` on Python 3.12. [#561](https://github.com/hynek/structlog/issues/561) - `structlog.threadlocal.tmp_bind()` now also works with `BoundLoggerLazyProxy` (in other words: before anything is bound to a bound logger). - stdlib: `ProcessorFormatter` can now be told to not render the log record message using `getMessage` and just `str(record.msg)` instead. [#550](https://github.com/hynek/structlog/issues/550) - stdlib: `structlog.stdlib.BoundLogger.exception()`'s handling of`LogRecord.exc_info` is now set consistent with `logging`. [#571](https://github.com/hynek/structlog/issues/571) [#572](https://github.com/hynek/structlog/issues/572) ## [23.2.0](https://github.com/hynek/structlog/compare/23.1.0...23.2.0) - 2023-10-09 ### Removed - Support for Python 3.7. ### Added - Official support for Python 3.12. [#515](https://github.com/hynek/structlog/issues/515) - `structlog.processors.MaybeTimeStamper` that only adds a timestamp if there isn't one already. [#81](https://github.com/hynek/structlog/issues/81) - `structlog.dev.ConsoleRenderer` now supports renamed timestamp keys using the *timestamp_key* parameter. [#541](https://github.com/hynek/structlog/issues/541) - `structlog.dev.RichTracebackFormatter` that allows to configure the traceback formatting. [#542](https://github.com/hynek/structlog/issues/542) ### Fixed - `FilteringBoundLogger.exception()` and `FilteringBoundLogger.aexception()` now support positional argument formatting like the rest of the methods. [#531](https://github.com/hynek/structlog/issues/531) - `structlog.processors.format_exc_info()` and `structlog.dev.ConsoleRenderer` do not crash anymore when told to format a non-existent exception. [#533](https://github.com/hynek/structlog/issues/533) ## [23.1.0](https://github.com/hynek/structlog/compare/22.3.0...23.1.0) - 2023-04-06 ### Added - `structlog.stdlib.BoundLogger` now has, analogously to our native logger, a full set of async log methods prefixed with an `a`: `await log.ainfo("event!")` [#502](https://github.com/hynek/structlog/issues/502) - The default configuration now respects the presence of `FORCE_COLOR` (regardless of its value, unless an empty string). This disables all heuristics whether it makes sense to use colors. [#503](https://github.com/hynek/structlog/issues/503) - The default configuration now respects the presence of [`NO_COLOR`](https://no-color.org) (regardless of its value, unless an empty string). This disables all heuristics whether it makes sense to use colors and overrides `FORCE_COLOR`. [#504](https://github.com/hynek/structlog/issues/504) ### Fixed - ConsoleRenderer now reuses the `_figure_out_exc_info` to process the `exc_info` argument like `ExceptionRenderer` does. This prevents crashes if the actual Exception is passed for the *exc_info* argument instead of a tuple or `True`. [#482](https://github.com/hynek/structlog/issues/482) - `FilteringBoundLogger.aexception()` now extracts the exception info using `sys.exc_info()` before passing control to the asyncio executor (where original exception info is no longer available). [#488](https://github.com/hynek/structlog/issues/488) ## [22.3.0](https://github.com/hynek/structlog/compare/22.2.0...22.3.0) - 2022-11-24 ### Changed - String interpolation in `FilteringBoundLogger` (used by default) is now only attempted if positional arguments are passed. This prevents crashes if something different than a string is passed for the *event* argument. [#475](https://github.com/hynek/structlog/pull/475) ### Fixed - String interpolation doesn't cause crashes in filtered log call anymore. [#478](https://github.com/hynek/structlog/pull/478) ## [22.2.0](https://github.com/hynek/structlog/compare/22.1.0...22.2.0) - 2022-11-19 ### Deprecated - Accessing package metadata as attributes on the *structlog* module is deprecated (for example, `structlog.__version__`). Please use [`importlib.metadata`](https://docs.python.org/3.10/library/importlib.metadata.html) instead (for Python 3.7: the [*importlib-metadata*](https://pypi.org/project/importlib-metadata/) PyPI package). - The `structlog.types` module is now deprecated in favor of the `structlog.typing` module. It seems like the Python typing community is settling on this name. ### Added - `FilteringBoundLogger` (used by default) now allows for string interpolation using positional arguments: ```pycon >>> log.info("Hello %s! The answer is %d.", "World", 42, x=1) 2022-10-07 10:04.31 [info ] Hello World! The answer is 42. x=1 ``` [#454](https://github.com/hynek/structlog/pull/454) - `FilteringBoundLogger` now also has support for *asyncio*-based logging. Instead of a wrapper class like `structlog.stdlib.AsyncBoundLogger`, async equivalents have been added for all logging methods. So instead of `log.info("hello")` you can also write `await log.ainfo("hello")` in async functions and methods. This seems like the better approach and if it's liked by the community, `structlog.stdlib.BoundLogger` will get those methods too. [#457](https://github.com/hynek/structlog/pull/457) ### Changed - The documentation has been **heavily** overhauled. Have a look if you haven't lately! Especially the graphs in the [standard library chapter](https://www.structlog.org/en/latest/standard-library.html) have proven valuable to many. - The build backend has been switched to [*Hatch*](https://hatch.pypa.io/). ### Fixed - The timestamps in the default configuration now use the correct separator (`:`) for seconds. ## [22.1.0](https://github.com/hynek/structlog/compare/21.5.0...22.1.0) - 2022-07-20 ### Removed - Python 3.6 is not supported anymore. - Pickling is now only possible with protocol version 3 and newer. ### Deprecated - The entire `structlog.threadlocal` module is deprecated. Please use the primitives from `structlog.contextvars` instead. If you're using the modern APIs (`bind_threadlocal()` / `merge_threadlocal()`) it's enough to replace them 1:1 with their `contextvars` counterparts. The old approach around `wrap_dict()` has been discouraged for a while. Currently there are no concrete plans to remove the module, but no patches against it will be accepted from now on. [#409](https://github.com/hynek/structlog/pull/409) ### Added - `structlog.processors.StackInfoRenderer` now has an *additional_ignores* parameter that allows you to filter out your own logging layer. [#396](https://github.com/hynek/structlog/issues/396) - Added `structlog.WriteLogger`, a faster – but more low-level – alternative to `structlog.PrintLogger`. It works the way `PrintLogger` used to work in previous versions. [#403](https://github.com/hynek/structlog/pull/403) [#404](https://github.com/hynek/structlog/pull/404) - `structlog.make_filtering_bound_logger()`-returned loggers now also have a `log()` method to match the `structlog.stdlib.BoundLogger` signature closer. [#413](https://github.com/hynek/structlog/pull/413) - Added structured logging of tracebacks via the `structlog.tracebacks` module, and most notably the `structlog.tracebacks.ExceptionDictTransformer` which can be used with the new `structlog.processors.ExceptionRenderer` to render JSON tracebacks. [#407](https://github.com/hynek/structlog/pull/407) - `structlog.stdlib.recreate_defaults(log_level=logging.NOTSET)` that recreates *structlog*'s defaults on top of standard library's `logging`. It optionally also configures `logging` to log to standard out at the passed log level. [#428](https://github.com/hynek/structlog/pull/428) - `structlog.processors.EventRenamer` allows you to rename the hitherto hard-coded event dict key `event` to something else. Optionally, you can rename another key to `event` at the same time, too. So adding `EventRenamer(to="msg", replace_by="_event")` to your processor pipeline will rename the standard `event` key to `msg` and then rename the `_event` key to `event`. This allows you to use the `event` key in your own log files and to have consistent log message keys across languages. - `structlog.dev.ConsoleRenderer(event_key="event")` now allows to customize the name of the key that is used for the log message. ### Changed - `structlog.make_filtering_bound_logger()` now returns a method with the same signature for all log levels, whether they are active or not. This ensures that invalid calls to inactive log levels are caught immediately and don't explode once the log level changes. [#401](https://github.com/hynek/structlog/pull/401) - `structlog.PrintLogger` – that is used by default – now uses `print()` for printing, making it a better citizen for interactive terminal applications. [#399](https://github.com/hynek/structlog/pull/399) - `structlog.testing.capture_logs` now works for already initialized bound loggers. [#408](https://github.com/hynek/structlog/pull/412) - `structlog.processors.format_exc_info()` is no longer a function, but an instance of `structlog.processors.ExceptionRenderer`. Its behavior has not changed. [#407](https://github.com/hynek/structlog/pull/407) - The default configuration now includes the `structlog.contextvars.merge_contextvars` processor. That means you can use [`structlog.contextvars`](https://www.structlog.org/en/stable/contextvars.html) features without configuring *structlog*. ### Fixed - Overloaded the `bind`, `unbind`, `try_unbind` and `new` methods in the `FilteringBoundLogger` [Protocol](https://docs.python.org/3/library/typing.html#typing.Protocol). This makes it easier to use objects of type `FilteringBoundLogger` in a typed context. [#392](https://github.com/hynek/structlog/pull/392) - Monkeypatched `sys.stdout`s are now handled more gracefully by `ConsoleRenderer` (that's used by default). [#404](https://github.com/hynek/structlog/pull/404) - `structlog.stdlib.render_to_log_kwargs()` now correctly handles the presence of `exc_info`, `stack_info`, and `stackLevel` in the event dictionary. They are transformed into proper keyword arguments instead of putting them into the `extra` dictionary. [#424](https://github.com/hynek/structlog/issues/424), [#427](https://github.com/hynek/structlog/issues/427) ## [21.5.0](https://github.com/hynek/structlog/compare/21.4.0...21.5.0) - 2021-12-16 ### Added - Added the `structlog.processors.LogfmtRenderer` processor to render log lines using the [*logfmt*](https://brandur.org/logfmt) format. [#376](https://github.com/hynek/structlog/pull/376) - Added the `structlog.stdlib.ExtraAdder` processor that adds extra attributes of `logging.LogRecord` objects to the event dictionary. This processor can be used for adding data passed in the `extra` parameter of the `logging` module's log methods to the event dictionary. [#209](https://github.com/hynek/structlog/pull/209), [#377](https://github.com/hynek/structlog/pull/377) - Added the `structlog.processor.CallsiteParameterAdder` processor that adds parameters of the callsite that an event dictionary originated from to the event dictionary. This processor can be used to enrich events dictionaries with information such as the function name, line number and filename that an event dictionary originated from. [#380](https://github.com/hynek/structlog/pull/380) ## [21.4.0](https://github.com/hynek/structlog/compare/21.3.0...21.4.0) - 2021-11-25 ### Added - Added the `structlog.threadlocal.bound_threadlocal` and `structlog.contextvars.bound_contextvars` decorator/context managers to temporarily bind key-value pairs to a thread-local and context-local context. [#371](https://github.com/hynek/structlog/pull/371) ### Fixed - Fixed import when running in optimized mode (`PYTHONOPTIMIZE=2` or `python -OO`) . [#373](https://github.com/hynek/structlog/pull/373) ## [21.3.0](https://github.com/hynek/structlog/compare/21.2.0...21.3.0) - 2021-11-20 ### Added - `structlog.dev.ConsoleRenderer` now has `sort_keys` boolean parameter that allows to disable the sorting of keys on output. [#358](https://github.com/hynek/structlog/pull/358) ### Changed - *structlog* switched its packaging to [*flit*](https://flit.pypa.io/). Users shouldn't notice a difference, but (re-)packagers might. - `structlog.stdlib.AsyncBoundLogger` now determines the running loop when logging, not on instantiation. That has a minor performance impact, but makes it more robust when loops change (for example, `aiohttp.web.run_app()`), or you want to use `sync_bl` *before* a loop has started. ### Fixed - `structlog.processors.TimeStamper` now works well with [*FreezeGun*](https://github.com/spulec/freezegun) even when it gets applied before the loggers are configured. [#364](https://github.com/hynek/structlog/pull/364) - `structlog.stdlib.ProcessorFormatter` now has a *processors* argument that allows to define a processor chain to run over *all* log entries. Before running the chain, two additional keys are added to the event dictionary: `_record` and `_from_structlog`. With them it's possible to extract information from `logging.LogRecord`s and differentiate between *structlog* and `logging` log entries while processing them. The old *processor* (singular) parameter is now deprecated, but no plans exist to remove it. [#365](https://github.com/hynek/structlog/pull/365) ## [21.2.0](https://github.com/hynek/structlog/compare/21.1.0...21.2.0) - 2021-10-12 ### Added - `structlog.threadlocal.get_threadlocal()` and `structlog.contextvars.get_contextvars()` can now be used to get a copy of the current thread-local/context-local context that has been bound using `structlog.threadlocal.bind_threadlocal()` and `structlog.contextvars.bind_contextvars()`. [#331](https://github.com/hynek/structlog/pull/331), [#337](https://github.com/hynek/structlog/pull/337) - `structlog.threadlocal.get_merged_threadlocal(bl)` and `structlog.contextvars.get_merged_contextvars(bl)` do the same, but also merge the context from a bound logger *bl*. Same pull requests as previous change. - `structlog.contextvars.bind_contextvars()` now returns a mapping of keys to `contextvars.Token`s, allowing you to reset values using the new `structlog.contextvars.reset_contextvars()`. [#339](https://github.com/hynek/structlog/pull/339) - Exception rendering in `structlog.dev.ConsoleLogger` is now configurable using the `exception_formatter` setting. If either the [Rich](https://github.com/Textualize/rich) or the [*better-exceptions*](https://github.com/qix-/better-exceptions) package is present, *structlog* will use them for pretty-printing tracebacks. Rich takes precedence over *better-exceptions* if both are present. This only works if `format_exc_info` is **absent** in the processor chain. [#330](https://github.com/hynek/structlog/pull/330), [#349](https://github.com/hynek/structlog/pull/349) - The final processor can now return a `bytearray` (additionally to `str` and `bytes`). [#344](https://github.com/hynek/structlog/issues/344) ### Changed - To implement pretty exceptions (see Changes below), `structlog.dev.ConsoleRenderer` now formats exceptions itself. Make sure to remove `format_exc_info` from your processor chain if you configure *structlog* manually. This change is not really breaking, because the old use-case will keep working as before. However if you pass `pretty_exceptions=True` (which is the default if either `rich` or `better-exceptions` is installed), a warning will be raised and the exception will be rendered without prettification. - All use of [Colorama](https://github.com/tartley/colorama) on non-Windows systems has been excised. Thus, colors are now enabled by default in `structlog.dev.ConsoleRenderer` on non-Windows systems. You can keep using Colorama to customize colors, of course. [#345](https://github.com/hynek/structlog/pull/345) ### Fixed - *structlog* is now importable if `sys.stdout` is `None` (for example, when running using `pythonw`). [#313](https://github.com/hynek/structlog/issues/313) ## [21.1.0](https://github.com/hynek/structlog/compare/20.2.0...21.1.0) - 2021-02-18 ### Changed - `structlog.dev.ConsoleRenderer` will now look for a `logger_name` key if no `logger` key is set. [#295](https://github.com/hynek/structlog/pull/295) ### Fixed - `structlog.threadlocal.wrap_dict()` now has a correct type annotation. [#290](https://github.com/hynek/structlog/pull/290) - Fix isolation in `structlog.contextvars`. [#302](https://github.com/hynek/structlog/pull/302) - The default configuration and loggers are pickleable again. [#301](https://github.com/hynek/structlog/pull/301) ## [20.2.0](https://github.com/hynek/structlog/compare/20.1.0...20.2.0) - 2020-12-31 ### Removed - Python 2.7 and 3.5 aren't supported anymore. The package meta data should ensure that you keep getting 20.1.0 on those versions. [#244](https://github.com/hynek/structlog/pull/244) ### Deprecated - Accessing the `_context` attribute of a bound logger is now deprecated. Please use the new `structlog.get_context()`. ### Added - *structlog* has now type hints for all of its APIs! Since *structlog* is highly dynamic and configurable, this led to a few concessions like a specialized `structlog.stdlib.get_logger()` whose only difference to `structlog.get_logger()` is that it has the correct type hints. We consider them provisional for the time being – that means the backwards-compatibility does not apply to them in its full strength until we feel we got it right. Please feel free to provide feedback! [#223](https://github.com/hynek/structlog/issues/223), [#282](https://github.com/hynek/structlog/issues/282) - Added `structlog.make_filtering_logger` that can be used like `configure(wrapper_class=make_filtering_bound_logger(logging.INFO))`. It creates a highly optimized bound logger whose inactive methods only consist of a `return None`. This is now also the default logger. - As a complement, `structlog.stdlib.add_log_level()` can now additionally be imported as `structlog.processors.add_log_level` since it just adds the method name to the event dict. - Added `structlog.BytesLogger` to avoid unnecessary encoding round trips. Concretely this is useful with *orjson* which returns bytes. [#271](https://github.com/hynek/structlog/issues/271) - The final processor now also may return bytes that are passed untouched to the wrapped logger. - `structlog.get_context()` allows you to retrieve the original context of a bound logger. [#266](https://github.com/hynek/structlog/issues/266), - Added `structlog.testing.CapturingLogger` for more unit testing goodness. - Added `structlog.stdlib.AsyncBoundLogger` that executes logging calls in a thread executor and therefore doesn't block. [#245](https://github.com/hynek/structlog/pull/245) ### Changed - The default bound logger (`wrapper_class`) if you don't configure *structlog* has changed. It's mostly compatible with the old one but a few uncommon methods like `log`, `failure`, or `err` don't exist anymore. You can regain the old behavior by using `structlog.configure(wrapper_class=structlog.BoundLogger)`. Please note that due to the various interactions between settings, it's possible that you encounter even more errors. We **strongly** urge you to always configure all possible settings since the default configuration is *not* covered by our backwards-compatibility policy. - `structlog.processors.add_log_level()` is now part of the default configuration. - `structlog.stdlib.ProcessorFormatter` no longer uses exceptions for control flow, allowing `foreign_pre_chain` processors to use `sys.exc_info()` to access the real exception. ### Fixed - `structlog.PrintLogger` now supports `copy.deepcopy()`. [#268](https://github.com/hynek/structlog/issues/268) ## [20.1.0](https://github.com/hynek/structlog/compare/19.2.0...20.1.0) - 2020-01-28 ### Deprecated - This is the last version to support Python 2.7 (including PyPy) and 3.5. All following versions will only support Python 3.6 or later. ### Added - Added a new module `structlog.contextvars` that allows to have a global but context-local *structlog* context the same way as with `structlog.threadlocal` since 19.2.0. [#201](https://github.com/hynek/structlog/issues/201), [#236](https://github.com/hynek/structlog/pull/236) - Added a new module `structlog.testing` for first class testing support. The first entry is the context manager `capture_logs()` that allows to make assertions about structured log calls. [#14](https://github.com/hynek/structlog/issues/14), [#234](https://github.com/hynek/structlog/pull/234) - Added `structlog.threadlocal.unbind_threadlocal()`. [#239](https://github.com/hynek/structlog/pull/239) ### Fixed - The logger created by `structlog.get_logger()` is not detected as an abstract method anymore, when attached to an abstract base class. [#229](https://github.com/hynek/structlog/issues/229) - Colorama isn't initialized lazily on Windows anymore because it breaks rendering. [#232](https://github.com/hynek/structlog/issues/232), [#242](https://github.com/hynek/structlog/pull/242) ## [19.2.0](https://github.com/hynek/structlog/compare/19.1.0...19.2.0) - 2019-10-16 ### Removed - Python 3.4 is not supported anymore. It has been unsupported by the Python core team for a while now and its PyPI downloads are negligible. It's very unlikely that *structlog* will break under 3.4 anytime soon, but we don't test it anymore. ### Added - Full Python 3.8 support for `structlog.stdlib`. - Added more pass-through properties to `structlog.stdlib.BoundLogger`. To makes it easier to use it as a drop-in replacement for `logging.Logger`. [#198](https://github.com/hynek/structlog/issues/198) - Added new processor `structlog.dev.set_exc_info()` that will set `exc_info=True` if the method's name is `exception` and `exc_info` isn't set at all. *This is only necessary when the standard library integration is not used*. It fixes the problem that in the default configuration, `structlog.get_logger().exception("hi")` in an `except` block would not print the exception without passing `exc_info=True` to it explicitly. [#130](https://github.com/hynek/structlog/issues/130), [#173](https://github.com/hynek/structlog/issues/173), [#200](https://github.com/hynek/structlog/issues/200), [#204](https://github.com/hynek/structlog/issues/204) - Added a new thread-local API that allows binding values to a thread-local context explicitly without affecting the default behavior of `bind()`. [#222](https://github.com/hynek/structlog/issues/222), [#225](https://github.com/hynek/structlog/issues/225) - Added *pass_foreign_args* argument to `structlog.stdlib.ProcessorFormatter`. It allows to pass a foreign log record's *args* attribute to the event dictionary under the `positional_args` key. [#228](https://github.com/hynek/structlog/issues/228) ### Changed - `structlog.stdlib.ProcessorFormatter` now takes a logger object as an optional keyword argument. This makes `ProcessorFormatter` work properly with `stuctlog.stdlib.filter_by_level()`. [#219](https://github.com/hynek/structlog/issues/219) - `structlog.dev.ConsoleRenderer` now calls `str()` on the event value. [#221](https://github.com/hynek/structlog/issues/221) ### Fixed - `structlog.dev.ConsoleRenderer` now uses no colors by default, if Colorama is not available. [#215](https://github.com/hynek/structlog/issues/215) - `structlog.dev.ConsoleRenderer` now initializes Colorama lazily, to prevent accidental side-effects just by importing *structlog*. [#210](https://github.com/hynek/structlog/issues/210) - A best effort has been made to make as much of *structlog* pickleable as possible to make it friendlier with `multiprocessing` and similar libraries. Some classes can only be pickled on Python 3 or using the [dill](https://pypi.org/project/dill/) library though and that is very unlikely to change. So far, the configuration proxy, `structlog.processor.TimeStamper`, `structlog.BoundLogger`, `structlog.PrintLogger` and `structlog.dev.ConsoleRenderer` have been made pickleable. Please report if you need any another class fixed. [#126](https://github.com/hynek/structlog/issues/126) ## [19.1.0](https://github.com/hynek/structlog/compare/18.2.0...19.1.0) - 2019-02-02 ### Added - `structlog.ReturnLogger` and `structlog.PrintLogger` now have a `fatal()` log method. [#181](https://github.com/hynek/structlog/issues/181) ### Changed - As announced in 18.1.0, `pip install -e .[dev]` now installs all development dependencies. Sorry for the inconveniences this undoubtedly will cause! - *structlog* now tolerates passing through `dict`s to stdlib logging. [#187](https://github.com/hynek/structlog/issues/187), [#188](https://github.com/hynek/structlog/pull/188), [#189](https://github.com/hynek/structlog/pull/189) ### Fixed - Under certain (rather unclear) circumstances, the frame extraction could throw an `SystemError: error return without exception set`. A workaround has been added. [#174](https://github.com/hynek/structlog/issues/174) ## [18.2.0](https://github.com/hynek/structlog/compare/18.1.0...18.2.0) - 2018-09-05 ### Added - Added `structlog.stdlib.add_log_level_number()` processor that adds the level *number* to the event dictionary. Can be used to simplify log filtering. [#151](https://github.com/hynek/structlog/pull/151) - `structlog.processors.JSONRenderer` now allows for overwriting the *default* argument of its serializer. [#77](https://github.com/hynek/structlog/pull/77), [#163](https://github.com/hynek/structlog/pull/163) - Added `try_unbind()` that works like `unbind()` but doesn't raise a `KeyError` if one of the keys is missing. [#171](https://github.com/hynek/structlog/pull/171) ## [18.1.0](https://github.com/hynek/structlog/compare/17.2.0...18.1.0) - 2018-01-27 ### Deprecated - The meaning of the `structlog[dev]` installation target will change from "colorful output" to "dependencies to develop *structlog*" in 19.1.0. The main reason behind this decision is that it's impossible to have a *structlog* in your normal dependencies and additionally a `structlog[dev]` for development (`pip` will report an error). ### Added - `structlog.dev.ConsoleRenderer` now accepts a *force_colors* argument to output colored logs even if the destination is not a tty. Use this option if your logs are stored in files that are intended to be streamed to the console. - `structlog.dev.ConsoleRenderer` now accepts a *level_styles* argument for overriding the colors for individual levels, as well as to add new levels. See the docs for `ConsoleRenderer.get_default_level_styles()` for usage. [#139](https://github.com/hynek/structlog/pull/139) - Added `structlog.is_configured()` to check whether or not *structlog* has been configured. - Added `structlog.get_config()` to introspect current configuration. ### Changed - Empty strings are valid events now. [#110](https://github.com/hynek/structlog/issues/110) - `structlog.stdlib.BoundLogger.exception()` now uses the `exc_info` argument if it has been passed instead of setting it unconditionally to `True`. [#149](https://github.com/hynek/structlog/pull/149) - Default configuration now uses plain `dict`s on Python 3.6+ and PyPy since they are ordered by default. ### Fixed - Do not encapsulate Twisted failures twice with newer versions of Twisted. [#144](https://github.com/hynek/structlog/issues/144) ## [17.2.0](https://github.com/hynek/structlog/compare/17.1.0...17.2.0) - 2017-05-15 ### Added - `structlog.stdlib.ProcessorFormatter` now accepts *keep_exc_info* and *keep_stack_info* arguments to control what to do with this information on log records. Most likely you want them both to be `False` therefore it's the default. [#109](https://github.com/hynek/structlog/issues/109) ### Fixed - `structlog.stdlib.add_logger_name()` now works in `structlog.stdlib.ProcessorFormatter`'s `foreign_pre_chain`. [#112](https://github.com/hynek/structlog/issues/112) - Clear log record args in `structlog.stdlib.ProcessorFormatter` after rendering. This fix is for you if you tried to use it and got `TypeError: not all arguments converted during string formatting` exceptions. [#116](https://github.com/hynek/structlog/issues/116), [#117](https://github.com/hynek/structlog/issues/117) ## [17.1.0](https://github.com/hynek/structlog/compare/16.1.0...17.1.0) - 2017-04-24 The main features of this release are massive improvements in standard library's `logging` integration. Have a look at the updated [standard library chapter](https://www.structlog.org/en/stable/standard-library.html) on how to use them! Special thanks go to [Fabian Büchler](https://github.com/fabianbuechler), [Gilbert Gilb's](https://github.com/gilbsgilbs), [Iva Kaneva](https://github.com/if-fi), [insolite](https://github.com/insolite), and [sky-code](https://github.com/sky-code), that made them possible. ### Added - Added `structlog.stdlib.render_to_log_kwargs()`. This allows you to use `logging`-based formatters to take care of rendering your entries. [#98](https://github.com/hynek/structlog/issues/98) - Added `structlog.stdlib.ProcessorFormatter` which does the opposite: This allows you to run *structlog* processors on arbitrary `logging.LogRecords`. [#79](https://github.com/hynek/structlog/issues/79), [#105](https://github.com/hynek/structlog/issues/105) - Added *repr_native_str* to `structlog.processors.KeyValueRenderer` and `structlog.dev.ConsoleRenderer`. This allows for human-readable non-ASCII output on Python 2 (`repr()` on Python 2 behaves like `ascii()` on Python 3 in that regard). As per compatibility policy, it's on (original behavior) in `KeyValueRenderer` and off (human-friendly behavior) in `ConsoleRenderer`. [#94](https://github.com/hynek/structlog/issues/94) - Added *colors* argument to `structlog.dev.ConsoleRenderer` and made it the default renderer. [#78](https://github.com/hynek/structlog/pull/78) ### Changed - The default renderer now is `structlog.dev.ConsoleRenderer` if you don't configure *structlog*. Colors are used if available and human-friendly timestamps are prepended. This is in line with our backwards-compatibility policy that explicitly excludes default settings. - UNIX epoch timestamps from `structlog.processors.TimeStamper` are more precise now. - Positional arguments are now removed even if they are empty. [#82](https://github.com/hynek/structlog/pull/82) ## Fixed - Fixed bug with Python 3 and `structlog.stdlib.BoundLogger.log()`. Error log level was not reproducible and was logged as exception one time out of two. [#92](https://github.com/hynek/structlog/pull/92) ## [16.1.0](https://github.com/hynek/structlog/compare/16.0.0...16.1.0) - 2016-05-24 ### Removed - Python 3.3 and 2.6 aren't supported anymore. They may work by chance but any effort to keep them working has ceased. The last Python 2.6 release was on October 29, 2013 and isn't supported by the CPython core team anymore. Major Python packages like Django and Twisted dropped Python 2.6 a while ago already. Python 3.3 never had a significant user base and wasn't part of any distribution's LTS release. ### Added - Added a `drop_missing` argument to `KeyValueRenderer`. If `key_order` is used and a key is missing a value, it's not rendered at all instead of being rendered as `None`. [#67](https://github.com/hynek/structlog/pull/67) ### Fixed - Exceptions without a `__traceback__` are now also rendered on Python 3. - Don't cache loggers in lazy proxies returned from `get_logger()`. This lead to in-place mutation of them if used before configuration which in turn lead to the problem that configuration was applied only partially to them later. [#72](https://github.com/hynek/structlog/pull/72) ## [16.0.0](https://github.com/hynek/structlog/compare/15.3.0...16.0.0) - 2016-01-28 ### Added - Added `structlog.dev.ConsoleRenderer` that renders the event dictionary aligned and with colors. - Added `structlog.processors.UnicodeDecoder` that will decode all byte string values in an event dictionary to Unicode. - Added `serializer` parameter to `structlog.processors.JSONRenderer` which allows for using different (possibly faster) JSON encoders than the standard library. ### Changed - `structlog.processors.ExceptionPrettyPrinter` and `structlog.processors.format_exc_info` now support passing of Exceptions on Python 3. - [*six*](https://six.readthedocs.io/) is now used for compatibility. ### Fixed - The context is now cleaned up when exiting `structlog.threadlocal.tmp_bind` in case of exceptions. [#64](https://github.com/hynek/structlog/issues/64) - Be more more lenient about missing `__name__`s. [#62](https://github.com/hynek/structlog/pull/62) ## [15.3.0](https://github.com/hynek/structlog/compare/15.2.0...15.3.0) - 2015-09-25 ### Added - Officially support Python 3.5. - Added `structlog.ReturnLogger.failure` and `structlog.PrintLogger.failure` as preparation for the new Twisted logging system. ### Fixed - Tolerate frames without a `__name__`, better. [#58](https://github.com/hynek/structlog/pull/58) ## [15.2.0](https://github.com/hynek/structlog/compare/15.1.0...15.2.0) - 2015-06-10 ### Added - Added option to specify target key in `structlog.processors.TimeStamper` processor. [#51](https://github.com/hynek/structlog/pull/51) ### Changed - Allow empty lists of processors. This is a valid use case since [#26](https://github.com/hynek/structlog/issues/26) has been merged. Before, supplying an empty list resulted in the defaults being used. - Better support of `logging.Logger.exception` within *structlog*. [#52](https://github.com/hynek/structlog/pull/52) ### Fixed - Prevent Twisted's `log.err` from quoting strings rendered by `structlog.twisted.JSONRenderer`. ## [15.1.0](https://github.com/hynek/structlog/compare/15.0.0...15.1.0) - 2015-02-24 ### Fixed - Tolerate frames without a `__name__` when guessing callsite names. ## [15.0.0](https://github.com/hynek/structlog/compare/0.4.2...15.0.0) - 2015-01-23 ### Added - Added `structlog.stdlib.add_log_level` and `structlog.stdlib.add_logger_name` processors. [#44](https://github.com/hynek/structlog/pull/44) - Added `structlog.stdlib.BoundLogger.log`. [#42](https://github.com/hynek/structlog/pull/42) - Added `structlog.stdlib.BoundLogger.exception`. [#22](https://github.com/hynek/structlog/pull/22) ### Changed - Pass positional arguments to stdlib wrapped loggers that use string formatting. [#19](https://github.com/hynek/structlog/pull/19) - *structlog* is now dually licensed under the [Apache License, Version 2](https://choosealicense.com/licenses/apache/) and the [MIT](https://choosealicense.com/licenses/mit/) license. Therefore it is now legal to use *structlog* with [GPLv2](https://choosealicense.com/licenses/gpl-2.0/)-licensed projects. [#28](https://github.com/hynek/structlog/pull/28) ## [0.4.2](https://github.com/hynek/structlog/compare/0.4.1...0.4.2) - 2014-07-26 ### Removed - Drop support for Python 3.2. There is no justification to add complexity for a Python version that nobody uses. If you are one of the [0.350%](https://alexgaynor.net/2014/jan/03/pypi-download-statistics/) that use Python 3.2, please stick to the 0.4 branch; critical bugs will still be fixed. ### Added - Officially support Python 3.4. - Allow final processor to return a dictionary. See the adapting chapter. [#26](https://github.com/hynek/structlog/issues/26) - Test Twisted-related code on Python 3 (with some caveats). ### Fixed - Fixed a memory leak in greenlet code that emulates thread locals. It shouldn't matter in practice unless you use multiple wrapped dicts within one program that is rather unlikely. [#8](https://github.com/hynek/structlog/pull/8) - `structlog.PrintLogger` now is thread-safe. - `from structlog import *` works now (but you still shouldn't use it). ## [0.4.1](https://github.com/hynek/structlog/compare/0.4.0...0.4.1) - 2013-12-19 ### Changed - Don't cache proxied methods in `structlog.threadlocal._ThreadLocalDictWrapper`. This doesn't affect regular users. ### Fixed - Various doc fixes. ## [0.4.0](https://github.com/hynek/structlog/compare/0.3.2...0.4.0) - 2013-11-10 ### Added - Added `structlog.processors.StackInfoRenderer` for adding stack information to log entries without involving exceptions. Also added it to default processor chain. [#6](https://github.com/hynek/structlog/pull/6) - Allow optional positional arguments for `structlog.get_logger` that are passed to logger factories. The standard library factory uses this for explicit logger naming. [#12](https://github.com/hynek/structlog/pull/12) - Add `structlog.processors.ExceptionPrettyPrinter` for development and testing when multiline log entries aren't just acceptable but even helpful. - Allow the standard library name guesser to ignore certain frame names. This is useful together with frameworks. - Add meta data (for example, function names, line numbers) extraction for wrapped stdlib loggers. [#5](https://github.com/hynek/structlog/pull/5) ## [0.3.2](https://github.com/hynek/structlog/compare/0.3.1...0.3.2) - 2013-09-27 ### Fixed - Fix stdlib's name guessing. ## [0.3.1](https://github.com/hynek/structlog/compare/0.3.0...0.3.1) - 2013-09-26 ### Fixed - Added forgotten `structlog.processors.TimeStamper` to API documentation. ## [0.3.0](https://github.com/hynek/structlog/compare/0.2.0...0.3.0) - 2013-09-23 ### Changes: - Greatly enhanced and polished the documentation and added a new theme based on Write The Docs, requests, and Flask. - Add Python Standard Library-specific BoundLogger that has an explicit API instead of intercepting unknown method calls. See `structlog.stdlib.BoundLogger`. - `structlog.ReturnLogger` now allows arbitrary positional and keyword arguments. - Add Twisted-specific BoundLogger that has an explicit API instead of intercepting unknown method calls. See `structlog.twisted.BoundLogger`. - Allow logger proxies that are returned by `structlog.get_logger` and `structlog.wrap_logger` to cache the BoundLogger they assemble according to configuration on first use. See the chapter on performance and the `cache_logger_on_first_use` argument of `structlog.configure` and `structlog.wrap_logger`. - Extract a common base class for loggers that does nothing except keeping the context state. This makes writing custom loggers much easier and more straight-forward. See `structlog.BoundLoggerBase`. ## [0.2.0](https://github.com/hynek/structlog/compare/0.1.0...0.2.0) - 2013-09-17 ### Added - Add `key_order` option to `structlog.processors.KeyValueRenderer` for more predictable log entries with any `dict` class. - Enhance Twisted support by offering JSONification of non-structlog log entries. - Allow for custom serialization in `structlog.twisted.JSONRenderer` without abusing `__repr__`. ### Changed - Promote to stable, thus henceforth a strict backwards-compatibility policy is put into effect. - `structlog.PrintLogger` now uses proper I/O routines and is thus viable not only for examples but also for production. ## [0.1.0](https://github.com/hynek/structlog/tree/0.1.0) - 2013-09-16 Initial release. structlog-24.4.0/COPYRIGHT0000644000000000000000000000062714645734712012072 0ustar00Licensed under either of - Apache License, Version 2.0 (LICENSE-APACHE or ) - or MIT license (LICENSE-MIT or ) at your option. Any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions. structlog-24.4.0/README.md0000644000000000000000000001512614645734712012056 0ustar00# *structlog*: Structured Logging for Python

structlog: Structured Logging for Python

Documentation License: MIT / Apache 2.0 DOI Supported Python versions of the current PyPI release. Downloads per month

Simple. Powerful. Fast. Pick three.

*structlog* is *the* production-ready logging solution for Python: - **Simple**: Everything is about **functions** that take and return **dictionaries** – all hidden behind **familiar APIs**. - **Powerful**: Functions and dictionaries aren’t just simple but also powerful. *structlog* leaves *you* in control. - **Fast**: *structlog* is not hamstrung by designs of yore. Its flexibility comes not at the price of performance. Thanks to its flexible design, *you* choose whether you want *structlog* to take care of the **output** of your log entries or whether you prefer to **forward** them to an existing logging system like the standard library's `logging` module. The output format is just as flexible and *structlog* comes with support for JSON, [*logfmt*](https://brandur.org/logfmt), as well as pretty console output out-of-the-box: [![image](https://github.com/hynek/structlog/blob/main/docs/_static/console_renderer.png?raw=true)](https://github.com/hynek/structlog/blob/main/docs/_static/console_renderer.png?raw=true) ## Sponsors *structlog* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). Especially those generously supporting us at the *The Organization* tier and higher:

Please consider joining them to help make structlog’s maintenance more sustainable!

--- *structlog* has been successfully used in production at every scale since **2013**, while embracing cutting-edge technologies like *asyncio*, context variables, or type hints as they emerged. Its paradigms proved influential enough to [help design](https://twitter.com/sirupsen/status/638330548361019392) structured logging [packages across ecosystems](https://github.com/sirupsen/logrus). A short explanation on *why* structured logging is good for you, and why *structlog* is the right tool for the job can be found in the [Why chapter](https://www.structlog.org/en/stable/why.html) of our documentation. Once you feel inspired to try it out, check out our friendly [Getting Started tutorial](https://www.structlog.org/en/stable/getting-started.html). For a fully-fledged zero-to-hero tutorial, check out [*A Comprehensive Guide to Python Logging with structlog*](https://betterstack.com/community/guides/logging/structlog/). If you prefer videos over reading, check out [Markus Holtermann](https://chaos.social/@markush)'s talk *Logging Rethought 2: The Actions of Frank Taylor Jr.*:

## Credits *structlog* is written and maintained by [Hynek Schlawack](https://hynek.me/). The idea of bound loggers is inspired by previous work by [Jean-Paul Calderone](https://github.com/exarkun) and [David Reid](https://github.com/dreid). The development is kindly supported by my employer [Variomedia AG](https://www.variomedia.de/), *structlog*’s [Tidelift subscribers](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek), and all my amazing [GitHub Sponsors](https://github.com/sponsors/hynek). The logs-loving beaver logo has been contributed by [Lynn Root](https://www.roguelynn.com). ## Project Links - [**Get Help**](https://stackoverflow.com/questions/tagged/structlog) (use the *structlog* tag on Stack Overflow) - [**PyPI**](https://pypi.org/project/structlog/) - [**GitHub**](https://github.com/hynek/structlog) - [**Documentation**](https://www.structlog.org/) - [**Changelog**](https://github.com/hynek/structlog/tree/main/CHANGELOG.md) - [**Third-party Extensions**](https://github.com/hynek/structlog/wiki/Third-party-Extensions) ## *structlog* for Enterprise Available as part of the Tidelift Subscription. The maintainers of *structlog* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek) structlog-24.4.0/show_off.py0000644000000000000000000000156214645734712012762 0ustar00""" Show how console logging looks like. This is used for the screenshot in the readme and . """ from dataclasses import dataclass import structlog @dataclass class SomeClass: x: int y: str structlog.stdlib.recreate_defaults() # so we have logger names log = structlog.get_logger("some_logger") log.debug("debugging is hard", a_list=[1, 2, 3]) log.info("informative!", some_key="some_value") log.warning("uh-uh!") log.error("omg", a_dict={"a": 42, "b": "foo"}) log.critical("wtf", what=SomeClass(x=1, y="z")) log2 = structlog.get_logger("another_logger") def make_call_stack_more_impressive(): try: d = {"x": 42} print(SomeClass(d["y"], "foo")) except Exception: log2.exception("poor me") log.info("all better now!", stack_info=True) make_call_stack_more_impressive() structlog-24.4.0/tox.ini0000644000000000000000000000574614645734712012121 0ustar00[tox] min_version = 4 env_list = pre-commit, mypy-pkg, py3{8,9,10,11,12}-{tests,mypy} py3{8,11}-tests-{colorama,be,rich}, docs{,-sponsors}, coverage-report [testenv] package = wheel wheel_build_env = .pkg extras = tests: tests mypy: typing commands = tests: pytest {posargs} mypy: mypy tests/typing # Run oldest and latest under Coverage. # Keep in-sync with coverage `depends below. [testenv:py3{8,12}-tests{,-colorama,-be,-rich}] set_env = py312: COVERAGE_CORE=sysmon deps = coverage[toml] py312: twisted colorama: colorama rich: rich be: better-exceptions commands = coverage run -m pytest {posargs} [testenv:coverage-report] # Keep in-sync with .python-version-default base_python = py312 deps = coverage[toml] skip_install = true parallel_show_output = true # Keep in-sync with test env definition above. depends = py3{8,12}-{tests,colorama,be,rich} commands = coverage combine coverage report [testenv:docs] # Keep base_python in sync with ci.yml/docs and .readthedocs.yaml. base_python = py312 extras = docs commands = sphinx-build -n -T -W -b html -d {envtmpdir}/doctrees docs docs/_build/html sphinx-build -n -T -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html [testenv:docs-watch] package = editable base_python = {[testenv:docs]base_python} extras = {[testenv:docs]extras} deps = watchfiles commands = watchfiles \ --ignore-paths docs/_build/ \ 'sphinx-build -W -n --jobs auto -b html -d {envtmpdir}/doctrees docs docs/_build/html' \ src \ docs [testenv:docs-linkcheck] base_python = {[testenv:docs]base_python} extras = {[testenv:docs]extras} commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/html [testenv:docs-sponsors] description = Ensure sponsor logos are up to date. deps = cogapp commands = cog -rP README.md docs/index.md [testenv:pre-commit] skip_install = true deps = pre-commit commands = pre-commit run --all-files [testenv:mypy-pkg] extras = typing commands = mypy src [testenv:pyright] deps = pyright extras = typing commands = pyright tests/typing [testenv:color-force] help = A visual check that FORCE_COLOR is working. set_env = FORCE_COLOR=1 commands = python -c "import structlog; structlog.get_logger().warning('should be colorful')" [testenv:color-no] help = A visual check that NO_COLOR is working. set_env = NO_COLOR=1 commands = python -c "import structlog; structlog.get_logger().warning('should be plain')" [testenv:docset] deps = doc2dash extras = docs allowlist_externals = rm cp tar commands = rm -rf structlog.docset structlog.tgz docs/_build sphinx-build -n -T -W -b html -d {envtmpdir}/doctrees docs docs/_build/html doc2dash --index-page index.html --icon docs/_static/docset-icon.png --online-redirect-url https://www.structlog.org/en/latest/ docs/_build/html cp docs/_static/docset-icon@2x.png structlog.docset/icon@2x.png tar --exclude='.DS_Store' -cvzf structlog.tgz structlog.docset structlog-24.4.0/.github/CODE_OF_CONDUCT.md0000644000000000000000000001255214645734712014736 0ustar00 # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations structlog-24.4.0/.github/CONTRIBUTING.md0000644000000000000000000002167314645734712014374 0ustar00# How To Contribute > [!IMPORTANT] > This document is mainly to help you to get started by codifying tribal knowledge and expectations and make it more accessible to everyone. > But don't be afraid to open half-finished PRs and ask questions if something is unclear! ## Support In case you'd like to help out but don't want to deal with GitHub, there's a great opportunity: help your fellow developers on [Stack Overflow](https://stackoverflow.com/questions/tagged/structlog)! The official tag is `structlog` and helping out in support frees us up to improve *structlog* instead! ## Workflow First off, thank you for considering to contribute! It's people like *you* who make this project such a great tool for everyone. - No contribution is too small! Please submit as many fixes for typos and grammar bloopers as you can! - Try to limit each pull request to *one* change only. - Since we squash on merge, it's up to you how you handle updates to the `main` branch. Whether you prefer to rebase on `main` or merge `main` into your branch, do whatever is more comfortable for you. - *Always* add tests and docs for your code. This is a hard rule; patches with missing tests or documentation won't be merged. - Consider updating [`CHANGELOG.md`](../CHANGELOG.md) to reflect the changes as observed by people using this library. - Make sure your changes pass our [CI](https://github.com/hynek/structlog/actions). You won't get any feedback until it's green unless you ask for it. For the CI to pass, the coverage must be 100%. If you have problems to test something, open anyway and ask for advice. In some situations, we may agree to add an `# pragma: no cover`. - Once you've addressed review feedback, make sure to bump the pull request with a short note, so we know you're done. - Don't break [backwards-compatibility](SECURITY.md). ## Local Development Environment First, **fork** the repository on GitHub and **clone** it using one of the alternatives that you can copy-paste by pressing the big green button labeled `<> Code`. You can (and should) run our test suite using [*tox*](https://tox.wiki/). However, you'll probably want a more traditional environment as well. We recommend using the Python version from the `.python-version-default` file in the project's root directory, because that's the one that is used in the CI by default, too. If you're using [*direnv*](https://direnv.net), you can automate the creation of the project virtual environment with the correct Python version by adding the following `.envrc` to the project root: ```bash layout python python$(cat .python-version-default) ``` or, if you like [*uv*](https://github.com/astral-sh/uv): ```bash test -d .venv || uv venv --python python$(cat .python-version-default) . .venv/bin/activate ``` > [!WARNING] > - **Before** you start working on a new pull request, use the "*Sync fork*" button in GitHub's web UI to ensure your fork is up to date. > - **Always create a new branch off `main` for each new pull request.** > Yes, you can work on `main` in your fork and submit pull requests. > But this will *inevitably* lead to you not being able to synchronize your fork with upstream and having to start over. Change into the newly created directory and after activating a virtual environment, install an editable version of this project along with its tests requirements: ```console $ pip install -e .[dev] # or `uv pip install -e .[dev]` ``` This will also install *tox* for you. If you use *uv* and want to make the *tox* runs faster, you can also additionally install the [*tox-uv*](https://github.com/tox-dev/tox-uv) plugin using `uv pip install tox-uv`. Now you can run the test suite: ```console $ python -Im pytest ``` When working on the documentation, use: ```console $ tox run -e docs-watch ``` This will build the documentation, watch for changes, and rebuild it whenever you save a file. To just build the documentation and run doctests, use: ```console $ tox run -e docs ``` You will find the built documentation in `docs/_build/html`. ## Code - Obey [PEP 8](https://peps.python.org/pep-0008/) and [PEP 257](https://peps.python.org/pep-0257/). We use the `"""`-on-separate-lines style for docstrings with [Napoleon](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html)-style API documentation: ```python def func(x: str, y: int) -> str: """ Do something. Args: x: A very important argument. y: Another very important argument, but its description is so long that it doesn't fit on one line. So, we start the whole block on a fresh new line to keep the block together. Returns: The result of doing something. """ ``` Please note that unlike everything else, the API docstrings are still reStructuredText. - If you add or change public APIs, tag the docstring using `.. versionadded:: 24.1.0 WHAT` or `.. versionchanged:: 24.1.0 WHAT`. We follow CalVer, so the next version will be the current with with the middle number incremented (for example, `24.1.0` -> `24.2.0`). - We use [Ruff](https://ruff.rs/) to sort our imports, and we follow the [Black](https://github.com/psf/black) code style with a line length of 79 characters. As long as you run our full *tox* suite before committing, or install our [*pre-commit*](https://pre-commit.com/) hooks (ideally you'll do both -- see [*Local Development Environment*](#local-development-environment) above), you won't have to spend any time on formatting your code at all. If you don't, CI will catch it for you -- but that seems like a waste of your time! ## Tests - Write your asserts as `expected == actual` to line them up nicely, and leave an empty line before them: ```python x = f() assert 42 == x.some_attribute assert "foo" == x._a_private_attribute ``` - You can run the test suite runs with all (optional) dependencies against all supported Python versions just as it will in our CI by running `tox`. - Write [good test docstrings](https://jml.io/test-docstrings/). ## Documentation - Use [semantic newlines] in [reStructuredText](https://www.sphinx-doc.org/en/stable/usage/restructuredtext/basics.html) (`*.rst`) and [Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) (`*.md`) files: ```markdown This is a sentence. This is another sentence. This is a new paragraph. ``` - If you start a new section, add two blank lines before and one blank line after the header except if two headers follow immediately after each other: ```markdown # Main Header Last line of previous section. ## Header of New Top Section ### Header of New Section First line of new section. ``` ### Changelog If your change is interesting to end-users, there needs to be an entry in our `CHANGELOG.md`, so they can learn about it. - The changelog follows the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) standard. Add the best-fitting section if it's missing for the current release. We use the following order: `Security`, `Removed`, `Deprecated`, `Added`, `Changed`, `Fixed`. - As with other docs, please use [semantic newlines] in the changelog. - Make the last line a link to your pull request. You probably have to open it first to know the number. - Leave an empty line between entries, so it doesn't look like a wall of text. - Refer to all symbols by their fully-qualified names. For example, `structlog.Foo` -- not just `Foo`. - Wrap symbols like modules, functions, or classes into backticks, so they are rendered in a `monospace font`. - Wrap arguments into asterisks so they are *italicized* like in API documentation: `Added new argument *an_argument*.` - If you mention functions or methods, add parentheses at the end of their names: `structlog.func()` or `structlog.Class.method()`. This makes the changelog a lot more readable. - Prefer simple past tense or constructions with "now". In the `Added` section, you can leave out the "Added" prefix: ```markdown ### Added - `structlog.func()` that does foo. It's pretty cool. [#1](https://github.com/hynek/structlog/pull/1) ### Fixed - `structlog.func()` now doesn't crash the Large Hadron Collider anymore. That was a nasty bug! [#2](https://github.com/hynek/structlog/pull/2) ``` ## See You on GitHub! Again, this whole file is mainly to help you to get started by codifying tribal knowledge and expectations to save you time and turnarounds. It is **not** meant to be a barrier to entry, so don't be afraid to open half-finished PRs and ask questions if something is unclear! Please note that this project is released with a Contributor [Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. Please report any harm to [Hynek Schlawack](https://hynek.me/about/) in any way you find appropriate. [semantic newlines]: https://rhodesmill.org/brandon/2012/one-sentence-per-line/ structlog-24.4.0/.github/FUNDING.yml0000644000000000000000000000005314645734712013745 0ustar00--- github: hynek tidelift: pypi/structlog structlog-24.4.0/.github/PULL_REQUEST_TEMPLATE.md0000644000000000000000000000465214645734712015742 0ustar00# Summary # Pull Request Check List - [ ] Do **not** open pull requests from your `main` branch – **use a separate branch**! - There's a ton of footguns waiting if you don't heed this warning. You can still go back to your project, create a branch from your main branch, push it, and open the pull request from the new branch. - This is not a pre-requisite for your your pull request to be accepted, but **you have been warned**. - [ ] Added **tests** for changed code. - The CI fails with less than 100% coverage. - [ ] **New APIs** are added to our typing tests in [`api.py`](https://github.com/hynek/structlog/blob/main/tests/typing/api.py). - [ ] Updated **documentation** for changed code. - [ ] New functions/classes have to be added to `docs/api.rst` by hand. - [ ] Changed/added classes/methods/functions have appropriate `versionadded`, `versionchanged`, or `deprecated` [directives](http://www.sphinx-doc.org/en/stable/markup/para.html#directive-versionadded). - The next version is the second number in the current release + 1. The first number represents the current year. So if the current version on PyPI is 23.1.0, the next version is gonna be 23.2.0. If the next version is the first in the new year, it'll be 24.1.0. - [ ] Documentation in `.rst` and `.md` files is written using [**semantic newlines**](https://rhodesmill.org/brandon/2012/one-sentence-per-line/). - [ ] Changes (and possible deprecations) are documented in the [**changelog**](https://github.com/hynek/structlog/blob/main/CHANGELOG.md). - [ ] Consider granting [push permissions to the PR branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork), so maintainers can fix minor issues themselves without pestering you. structlog-24.4.0/.github/SECURITY.md0000644000000000000000000000161614645734712013727 0ustar00# Security Policy ## Supported Versions We are following [*CalVer*](https://calver.org) with generous backwards-compatibility guarantees. Therefore we only support the latest version. Put simply, you shouldn't ever be afraid to upgrade as long as you're only using our public APIs. Whenever there is a need to break compatibility, it is announced in the changelog, and raises a `DeprecationWarning` for a year (if possible) before it's finally really broken. You **can't** rely on the default settings and the `structlog.dev` module, though. They may be adjusted in the future to provide a better experience when starting to use *structlog*. So please make sure to **always** properly configure your applications. ## Reporting a Vulnerability To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. structlog-24.4.0/.github/dependabot.yml0000644000000000000000000000017314645734712014763 0ustar00--- version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" structlog-24.4.0/.github/workflows/build-docset.yml0000644000000000000000000000121114645734712017263 0ustar00--- name: Build docset on: push: tags: ["*"] workflow_dispatch: env: PIP_DISABLE_PIP_VERSION_CHECK: 1 PIP_NO_PYTHON_VERSION_WARNING: 1 permissions: {} jobs: docset: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # get correct version - uses: actions/setup-python@v5 with: python-version: "3.12" - run: pip install tox - run: tox run -e docset - run: tar --exclude='.DS_Store' -cvzf structlog.tgz structlog.docset - uses: actions/upload-artifact@v4 with: name: docset path: structlog.tgz structlog-24.4.0/.github/workflows/ci.yml0000644000000000000000000001333014645734712015305 0ustar00--- name: CI on: push: branches: [main] pull_request: workflow_dispatch: env: FORCE_COLOR: "1" # Make tools pretty. PIP_DISABLE_PIP_VERSION_CHECK: "1" PIP_NO_PYTHON_VERSION_WARNING: "1" permissions: {} jobs: build-package: name: Build & verify package runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: hynek/build-and-inspect-python-package@v2 id: baipp outputs: python-versions: ${{ steps.baipp.outputs.supported_python_classifiers_json_array }} tests: name: Tests & API Mypy on ${{ matrix.python-version }} needs: build-package runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: ${{ fromJson(needs.build-package.outputs.python-versions) }} steps: - name: Download pre-built packages uses: actions/download-artifact@v4 with: name: Packages path: dist - run: tar xf dist/*.tar.gz --strip-components=1 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true cache: pip - run: python -Im pip install tox-uv - run: python -Im tox run --installpkg dist/*.whl -f py$(echo ${{ matrix.python-version }} | tr -d .) - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ matrix.python-version }} path: .coverage.* if-no-files-found: ignore coverage: name: Combine & check coverage needs: tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version-file: .python-version-default cache: pip - run: python -Im pip install --upgrade coverage[toml] - uses: actions/download-artifact@v4 with: pattern: coverage-data-* merge-multiple: true - name: Combine coverage & fail if it's <100%. run: | python -Im coverage combine python -Im coverage html --skip-covered --skip-empty # Report and write to summary. python -Im coverage report --format=markdown >> $GITHUB_STEP_SUMMARY # Report again and fail if under 100%. python -Im coverage report --fail-under=100 - name: Upload HTML report if check failed. uses: actions/upload-artifact@v4 with: name: html-report path: htmlcov if: ${{ failure() }} mypy-pkg: name: Type-check package needs: build-package runs-on: ubuntu-latest steps: - name: Download pre-built packages uses: actions/download-artifact@v4 with: name: Packages path: dist - run: tar xf dist/*.tar.gz --strip-components=1 - uses: actions/setup-python@v5 with: python-version-file: .python-version-default allow-prereleases: true cache: pip - run: python -Im pip install tox-uv - run: python -Im tox run --installpkg dist/*.whl -e mypy-pkg pyright: name: Pyright runs-on: ubuntu-latest needs: build-package steps: - name: Download pre-built packages uses: actions/download-artifact@v4 with: name: Packages path: dist - run: tar xf dist/*.tar.gz --strip-components=1 - uses: actions/setup-python@v5 with: python-version-file: .python-version-default allow-prereleases: true cache: pip - run: python -Im pip install tox-uv - run: python -Im tox run --installpkg dist/*.whl -e pyright docs: name: Build docs & run doctests needs: build-package runs-on: ubuntu-latest steps: - name: Download pre-built packages uses: actions/download-artifact@v4 with: name: Packages path: dist - run: tar xf dist/*.tar.gz --strip-components=1 - uses: actions/setup-python@v5 with: # Keep in sync with tox.ini/docs & .readthedocs.yaml python-version: "3.12" cache: pip - run: python -Im pip install tox-uv - run: python -Im tox run -e docs install-dev: name: Verify dev env runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version-file: .python-version-default cache: pip - run: python -Im pip install uv - run: uv venv - run: uv pip install -e .[dev] - run: .venv/bin/python -Ic 'import structlog; print(structlog.__version__)' if: runner.os != 'Windows' - run: .\.venv\Scripts\python.exe -Ic 'import structlog; print(structlog.__version__)' if: runner.os == 'Windows' required-checks-pass: name: Ensure everything required is passing for branch protection if: always() needs: - coverage - docs - install-dev - mypy-pkg - pyright runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} colors: name: Visual check for color settings using env variables needs: build-package runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version-file: .python-version-default cache: pip # tox 4.12.0 started passing FORCE_COLOR and NO_COLOR by default. - run: python -Im pip install 'tox>=4.12.0' tox-uv - run: python -Im tox run -f color structlog-24.4.0/.github/workflows/codeql-analysis.yml0000644000000000000000000000126314645734712020004 0ustar00--- name: CodeQL on: schedule: - cron: "41 3 * * 6" permissions: contents: read jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [python] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - name: Autobuild uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 structlog-24.4.0/.github/workflows/pypi-package.yml0000644000000000000000000000317614645734712017273 0ustar00--- name: Build & maybe upload PyPI package on: push: branches: [main] tags: ["*"] release: types: - published workflow_dispatch: permissions: attestations: write contents: read id-token: write jobs: build-package: name: Build & verify package runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: hynek/build-and-inspect-python-package@v2 with: attest-build-provenance-github: 'true' # Upload to Test PyPI on every commit on main. release-test-pypi: name: Publish in-dev package to test.pypi.org environment: release-test-pypi if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest needs: build-package steps: - name: Download packages built by build-and-inspect-python-package uses: actions/download-artifact@v4 with: name: Packages path: dist - name: Upload package to Test PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: repository-url: https://test.pypi.org/legacy/ # Upload to real PyPI on GitHub Releases. release-pypi: name: Publish released package to pypi.org environment: release-pypi if: github.event.action == 'published' runs-on: ubuntu-latest needs: build-package steps: - name: Download packages built by build-and-inspect-python-package uses: actions/download-artifact@v4 with: name: Packages path: dist - name: Upload package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 structlog-24.4.0/docs/Makefile0000644000000000000000000001271314645734712013166 0ustar00# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = 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) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 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 " singlehtml to make a single large HTML file" @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 " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @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) -n -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." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 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/structlog.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/structlog.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/structlog" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/structlog" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 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." structlog-24.4.0/docs/api.rst0000644000000000000000000002516514645734712013036 0ustar00.. _api: API Reference ============= .. note:: The examples here use a very simplified configuration using the minimalist `structlog.processors.KeyValueRenderer` for brevity and to enable doctests. The output is going to be different (nicer!) with the default configuration. .. testsetup:: * import structlog structlog.configure( processors=[structlog.processors.KeyValueRenderer()], ) .. testcleanup:: * import structlog structlog.reset_defaults() .. module:: structlog `structlog` Package ------------------- .. autofunction:: get_logger .. autofunction:: getLogger .. autofunction:: wrap_logger .. autofunction:: configure .. autofunction:: configure_once .. autofunction:: reset_defaults .. autofunction:: is_configured .. autofunction:: get_config .. autoclass:: BoundLogger :members: new, bind, unbind .. autofunction:: make_filtering_bound_logger .. autofunction:: get_context .. autoclass:: PrintLogger :members: msg, err, debug, info, warning, error, critical, log, failure, fatal .. autoclass:: PrintLoggerFactory .. autoclass:: WriteLogger :members: msg, err, debug, info, warning, error, critical, log, failure, fatal .. autoclass:: WriteLoggerFactory .. autoclass:: BytesLogger :members: msg, err, debug, info, warning, error, critical, log, failure, fatal .. autoclass:: BytesLoggerFactory .. autoexception:: DropEvent .. autoclass:: BoundLoggerBase :members: new, bind, unbind, try_unbind, _logger, _process_event, _proxy_to_logger `structlog.dev` Module ---------------------- .. automodule:: structlog.dev .. autoclass:: ConsoleRenderer :members: get_default_level_styles .. autoclass:: Column .. autoclass:: ColumnFormatter(typing.Protocol) :members: __call__ .. autoclass:: KeyValueColumnFormatter .. autoclass:: LogLevelColumnFormatter .. autofunction:: plain_traceback .. autoclass:: RichTracebackFormatter .. autofunction:: rich_traceback .. autofunction:: better_traceback .. autofunction:: set_exc_info `structlog.testing` Module -------------------------- .. automodule:: structlog.testing .. autofunction:: capture_logs .. autoclass:: LogCapture .. autoclass:: CapturingLogger >>> from pprint import pprint >>> cl = structlog.testing.CapturingLogger() >>> cl.info("hello") >>> cl.info("hello", when="again") >>> pprint(cl.calls) [CapturedCall(method_name='info', args=('hello',), kwargs={}), CapturedCall(method_name='info', args=('hello',), kwargs={'when': 'again'})] .. autoclass:: CapturingLoggerFactory .. autoclass:: CapturedCall .. autoclass:: ReturnLogger :members: msg, err, debug, info, warning, error, critical, log, failure, fatal .. autoclass:: ReturnLoggerFactory `structlog.contextvars` Module ------------------------------ .. automodule:: structlog.contextvars .. autofunction:: bind_contextvars .. autofunction:: bound_contextvars .. autofunction:: get_contextvars .. autofunction:: get_merged_contextvars .. autofunction:: merge_contextvars .. autofunction:: clear_contextvars .. autofunction:: unbind_contextvars .. autofunction:: reset_contextvars `structlog.threadlocal` Module ------------------------------ .. automodule:: structlog.threadlocal :noindex: .. _procs: `structlog.processors` Module ----------------------------- .. automodule:: structlog.processors .. autoclass:: JSONRenderer .. doctest:: >>> from structlog.processors import JSONRenderer >>> JSONRenderer(sort_keys=True)(None, None, {"a": 42, "b": [1, 2, 3]}) '{"a": 42, "b": [1, 2, 3]}' Bound objects are attempted to be serialize using a ``__structlog__`` method. If none is defined, ``repr()`` is used: .. doctest:: >>> class C1: ... def __structlog__(self): ... return ["C1!"] ... def __repr__(self): ... return "__structlog__ took precedence" >>> class C2: ... def __repr__(self): ... return "No __structlog__, so this is used." >>> from structlog.processors import JSONRenderer >>> JSONRenderer(sort_keys=True)(None, None, {"c1": C1(), "c2": C2()}) '{"c1": ["C1!"], "c2": "No __structlog__, so this is used."}' Please note that additionally to strings, you can also return any type the standard library JSON module knows about -- like in this example a list. If you choose to pass a *default* parameter as part of *dumps_kw*, support for ``__structlog__`` is disabled. That can be useful with more elegant serialization methods like `functools.singledispatch`: `Better Python Object Serialization `_. It can also be helpful if you are using *orjson* and want to rely on it to serialize `datetime.datetime` and other objects natively. .. tip:: If you use this processor, you may also wish to add structured tracebacks for exceptions. You can do this by adding the :class:`~structlog.processors.dict_tracebacks` to your list of processors: .. doctest:: >>> structlog.configure( ... processors=[ ... structlog.processors.dict_tracebacks, ... structlog.processors.JSONRenderer(), ... ], ... ) >>> log = structlog.get_logger() >>> var = "spam" >>> try: ... 1 / 0 ... except ZeroDivisionError: ... log.exception("Cannot compute!") {"event": "Cannot compute!", "exception": [{"exc_type": "ZeroDivisionError", "exc_value": "division by zero", "syntax_error": null, "is_cause": false, "frames": [{"filename": "", "lineno": 2, "name": "", "locals": {..., "var": "'spam'"}}]}]} .. autoclass:: KeyValueRenderer .. doctest:: >>> from structlog.processors import KeyValueRenderer >>> KeyValueRenderer(sort_keys=True)(None, None, {"a": 42, "b": [1, 2, 3]}) 'a=42 b=[1, 2, 3]' >>> KeyValueRenderer(key_order=["b", "a"])(None, None, ... {"a": 42, "b": [1, 2, 3]}) 'b=[1, 2, 3] a=42' .. autoclass:: LogfmtRenderer .. doctest:: >>> from structlog.processors import LogfmtRenderer >>> event_dict = {"a": 42, "b": [1, 2, 3], "flag": True} >>> LogfmtRenderer(sort_keys=True)(None, None, event_dict) 'a=42 b="[1, 2, 3]" flag' >>> LogfmtRenderer(key_order=["b", "a"], bool_as_flag=False)(None, None, event_dict) 'b="[1, 2, 3]" a=42 flag=true' .. autoclass:: EventRenamer .. autofunction:: add_log_level .. autoclass:: UnicodeDecoder .. autoclass:: UnicodeEncoder .. autoclass:: ExceptionRenderer .. autofunction:: format_exc_info .. doctest:: >>> from structlog.processors import format_exc_info >>> try: ... raise ValueError ... except ValueError: ... format_exc_info(None, None, {"exc_info": True}) # doctest: +ELLIPSIS {'exception': 'Traceback (most recent call last):... .. autofunction:: dict_tracebacks .. doctest:: >>> from structlog.processors import dict_tracebacks >>> try: ... raise ValueError("onoes") ... except ValueError: ... dict_tracebacks(None, None, {"exc_info": True}) # doctest: +ELLIPSIS {'exception': [{'exc_type': 'ValueError', 'exc_value': 'onoes', ..., 'frames': [{'filename': ... .. autoclass:: StackInfoRenderer .. autoclass:: ExceptionPrettyPrinter .. autoclass:: TimeStamper .. doctest:: >>> from structlog.processors import TimeStamper >>> TimeStamper()(None, None, {}) # doctest: +SKIP {'timestamp': 1378994017} >>> TimeStamper(fmt="iso")(None, None, {}) # doctest: +SKIP {'timestamp': '2013-09-12T13:54:26.996778Z'} >>> TimeStamper(fmt="%Y", key="year")(None, None, {}) # doctest: +SKIP {'year': '2013'} .. autoclass:: MaybeTimeStamper .. doctest:: >>> from structlog.processors import MaybeTimeStamper >>> MaybeTimeStamper()(None, None, {}) # doctest: +SKIP {'timestamp': 1690036074.494428} >>> MaybeTimeStamper()(None, None, {"timestamp": 42}) {'timestamp': 42} .. autoclass:: CallsiteParameter :members: .. autoclass:: CallsiteParameterAdder `structlog.stdlib` Module ------------------------- .. automodule:: structlog.stdlib .. autofunction:: recreate_defaults .. autofunction:: get_logger .. autoclass:: BoundLogger :members: bind, unbind, try_unbind, new, debug, info, warning, warn, error, critical, exception, log, adebug, ainfo, awarning, aerror, acritical, aexception, alog .. autoclass:: AsyncBoundLogger :members: sync_bl .. autoclass:: LoggerFactory :members: __call__ .. autofunction:: render_to_log_kwargs .. autofunction:: filter_by_level .. autofunction:: add_log_level .. autofunction:: add_log_level_number .. autofunction:: add_logger_name .. autofunction:: ExtraAdder .. autoclass:: PositionalArgumentsFormatter .. autoclass:: ProcessorFormatter :members: wrap_for_formatter, remove_processors_meta `structlog.tracebacks` Module ----------------------------- .. automodule:: structlog.tracebacks .. autofunction:: extract .. autoclass:: ExceptionDictTransformer .. autoclass:: Trace .. autoclass:: Stack .. autoclass:: Frame .. autoclass:: SyntaxError_ `structlog.typing` Module ------------------------- .. automodule:: structlog.typing .. autoclass:: BindableLogger Additionally to the methods listed below, bound loggers **must** have a ``__init__`` method with the following signature: .. method:: __init__(self, wrapped_logger: WrappedLogger, processors: Iterable[Processor], context: Context) -> None :noindex: Unfortunately it's impossible to define initializers using :pep:`544` Protocols. They currently also have to carry a `Context` as a ``_context`` attribute. .. note:: Currently Sphinx has no support for Protocols, so please click ``[source]`` for this entry to see the full definition. .. autoclass:: FilteringBoundLogger .. note:: Currently Sphinx has no support for Protocols, so please click ``[source]`` for this entry to see the full definition. .. autoclass:: ExceptionTransformer .. note:: Currently Sphinx has no support for Protocols, so please click ``[source]`` for this entry to see the full definition. .. autodata:: EventDict .. autodata:: WrappedLogger .. autodata:: Processor .. autodata:: Context .. autodata:: ExcInfo .. autodata:: ExceptionRenderer `structlog.twisted` Module -------------------------- .. automodule:: structlog.twisted .. autoclass:: BoundLogger :members: bind, unbind, new, msg, err .. autoclass:: LoggerFactory :members: __call__ .. autoclass:: EventAdapter .. autoclass:: JSONRenderer .. autofunction:: plainJSONStdOutLogger .. autofunction:: JSONLogObserverWrapper .. autoclass:: PlainFileLogObserver structlog-24.4.0/docs/bound-loggers.md0000644000000000000000000002064514645734712014622 0ustar00# Bound Loggers The centerpiece of *structlog* that you will interact with most is called a *bound logger*. It's what you get back from {func}`structlog.get_logger()` and it's called a *bound logger* because you can *bind* key-value pairs to it. As far as *structlog* is concerned, it consists of three parts: ```{image} _static/BoundLogger.svg ``` 1. A *context dictionary* that you can *bind* key-value pairs to. This dictionary is *merged* into each log entry that is logged from *this logger specifically*. You can inspect a context of a *bound logger* by calling {func}`structlog.get_context()` on it. 2. A list of {doc}`processors ` that are called on every log entry. Each processor receives the return value of its predecessor passed as an argument. This list is usually set using {doc}`configuration`. 3. And finally a *logger* that it's wrapping. This wrapped logger is responsible for the *output* of the log entry that has been returned by the last processor. This *can* be standard library's {class}`logging.Logger` like in the image above, but absolutely doesn't have to: By default it's *structlog*'s {class}`~structlog.PrintLogger`. This wrapped logger also is usually set using {doc}`configuration`. :::{important} Bound loggers themselves do *not* do any I/O themselves. All they do is manage the *context* and proxy log calls to a *wrapped logger*. ::: ## Context To manipulate the context dictionary, a *bound logger* can: - Recreate itself with (optional) *additional* context data: {func}`~structlog.BoundLoggerBase.bind` and {func}`~structlog.BoundLoggerBase.new`. - Recreate itself with *less* context data: {func}`~structlog.BoundLoggerBase.unbind` and {func}`~structlog.BoundLoggerBase.try_unbind`. In any case, the original bound logger or its context are never mutated. They always return a *copy* of the bound logger with a *new* context that reflects your changes. This part of the API is defined in the {class}`typing.Protocol` called {class}`structlog.typing.BindableLogger`. The protocol is marked {func}`typing.runtime_checkable` which means that you can check an object for being a *bound logger* using `isinstance(obj, structlog.typing.BindableLogger)`. ## Output Finally, a *bound logger* also **indirectly** exposes the logging methods of the *wrapped logger*. By default, that's a {class}`~structlog.typing.FilteringBoundLogger` that is wrapping a {class}`~structlog.PrintLogger`. They both share the set of log methods that's present in the standard library: `debug()`, `info()`, `warning()`, `error()`, and `critical()`. Whenever you call one of those methods on the *bound logger*, it will: 1. Make a copy of its context -- now it becomes the *event dictionary*, 2. Add the keyword arguments of the method call to the event dict. 3. Add a new key `event` with the value of the first positional argument of the method call to the event dict. 4. Run the processors successively on the event dict. Each processor receives the result of its predecessor. 5. Finally, it takes the result of the final processor and calls the method with the same name – that got called on the *bound logger* – on the wrapped logger. For flexibility, the final processor can return either a string[^str] that is passed directly as a positional parameter, or a tuple `(args, kwargs)` that are passed as `wrapped_logger.log_method(*args, **kwargs)`. [^str]: {any}`str`, {any}`bytes`, or {any}`bytearray` to be exact. ### Step-by-Step Example Assuming you've left the default configuration and have: ```python import structlog logger = structlog.get_logger() log = logger.bind(foo="bar") ``` Now, `log` is a *bound logger* of type {class}`~structlog.typing.FilteringBoundLogger` (but in the default config there's no filtering). `log`'s context is `{"foo": "bar"}` and its wrapped logger is a {class}`structlog.PrintLogger`. If you call `log.info("Hello, %s!", "world", number=42)` now, the following happens: 1. `"world"` gets interpolated into `"Hello, %s!"`, making the event "Hello, world!"[^interpolation]. 2. The *bound logger*'s context gets copied and the key-value pairs from the `info` call are added to it. It becomes an *event dict* and is `{"foo": "bar", "number": 42}` now. 3. The event from step 1 is added too. The *event dict* is `{"foo": "bar", "number": 42, "event": "Hello, world!"}` now. 4. The *event dict* is fed into the [processor chain](processors.md). In this case the processors add a timestamp and the log level name to the *event dict*. Before it hits the last processor, the *event dict* looks something like `{"foo": "bar", "number": 42, "event": "Hello, world!", "level": "info", "timestamp": "2022-10-13 16:29:27"}`. The last processor is {class}`structlog.dev.ConsoleRenderer` and renders the *event dict* into a colorful string[^json]. 5. Finally, the *wrapped logger*'s (a {class}`~structlog.PrintLogger`) `info()` method is called with that string. [^json]: Until this very step, the *event dict* was a dictionary. By replacing the last processor, you decide on the **format** of your logs. For example, if you wanted JSON logs, you just have to replace the last processor with {class}`structlog.processors.JSONRenderer`. [^interpolation]: String interpolation only takes place if you pass positional arguments. (filtering)= ## Filtering by Log Levels Filtering based on log levels can be done in a processor very easily[^stdlib], however that means unnecessary performance overhead through function calls. We care a lot about performance and that's why *structlog*'s default *bound logger* class implements level-filtering as close to the users as possible: in the *bound logger*'s logging methods *before* even creating an *event dict* and starting the processor chain. {func}`structlog.make_filtering_bound_logger` allows you to create a *bound logger* whose log methods with a log level beneath the configured one consist of a plain `return None`. Here's an example: ```pycon >>> import structlog >>> logger = structlog.get_logger() >>> logger.debug("hi!") 2022-10-15 11:39:03 [debug ] hi! >>> import logging >>> structlog.configure(wrapper_class=structlog.make_filtering_bound_logger(logging.INFO)) >>> logger.debug("hi!") # no output! ``` In this example, we first log out using the default logger that doesn't filter at all. Then we change the configuration to filtering at the info level and try again: no log output! Let's have a look at the `debug` method: ```pycon >>> import inspect >>> print(inspect.getsource(logger.debug)) def _nop(self: Any, event: str, **kw: Any) -> Any: return None ``` This is as effective as it gets and usually as flexible as the vast majority of users need. :::{important} *structlog* uses the constants from {mod}`logging`, but does **not** share any code. Passing `20` instead of `logging.INFO` would have worked too. ::: [^stdlib]: And it's in fact supported for standard library logging with the {func}`structlog.stdlib.filter_by_level` processor. ## Wrapping Loggers Manually In practice, you won't be instantiating bound loggers yourself. You will configure *structlog* as explained in the {doc}`next chapter ` and then just call {func}`structlog.get_logger`. However, in some rare cases you may not want to do that. For example because you don't control how you get the logger that you would like to wrap (famous example: Celery). For that times there is the {func}`structlog.wrap_logger` function that can be used to wrap a logger -- optionally without any global state (in other words, configuration): (proc)= ```{doctest} >>> import structlog >>> class CustomPrintLogger: ... def msg(self, message): ... print(message) >>> def proc(logger, method_name, event_dict): ... print("I got called with", event_dict) ... return repr(event_dict) >>> log = structlog.wrap_logger( ... CustomPrintLogger(), ... wrapper_class=structlog.BoundLogger, ... processors=[proc], ... ) >>> log2 = log.bind(x=42) >>> log == log2 False >>> log.msg("hello world") I got called with {'event': 'hello world'} {'event': 'hello world'} >>> log2.msg("hello world") I got called with {'x': 42, 'event': 'hello world'} {'x': 42, 'event': 'hello world'} >>> log3 = log2.unbind("x") >>> log == log3 True >>> log3.msg("nothing bound anymore", foo="but you can structure the event too") I got called with {'foo': 'but you can structure the event too', 'event': 'nothing bound anymore'} {'foo': 'but you can structure the event too', 'event': 'nothing bound anymore'} ``` structlog-24.4.0/docs/conf.py0000644000000000000000000001104014645734712013015 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import os from importlib import metadata # Set canonical URL from the Read the Docs Domain html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") # Tell Jinja2 templates the build is running on Read the Docs if os.environ.get("READTHEDOCS", "") == "True": html_context = {"READTHEDOCS": True} # We want an image in the README and include the README in the docs. suppress_warnings = ["image.nonlocal_uri"] # -- General configuration ---------------------------------------------------- extensions = [ "myst_parser", "notfound.extension", "sphinx.ext.autodoc", "sphinx.ext.autodoc.typehints", "sphinx.ext.napoleon", "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.viewcode", "sphinxcontrib.mermaid", "sphinxext.opengraph", ] myst_enable_extensions = [ "colon_fence", "smartquotes", "deflist", ] mermaid_init_js = "mermaid.initialize({startOnLoad:true,theme:'neutral'});" ogp_image = "_static/structlog_logo.png" # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = [".rst", ".md"] # The master toctree document. master_doc = "index" # General information about the project. project = "structlog" author = "Hynek Schlawack" copyright = f"2013, { author }" # 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. # The full version, including alpha/beta/rc tags. release = metadata.version("structlog") # The short X.Y version. version = release.rsplit(".", 1)[0] if "dev" in release: release = version = "UNRELEASED" exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. default_role = "any" nitpick_ignore = [ ("py:class", "Context"), ("py:class", "EventDict"), ("py:class", "ILogObserver"), ("py:class", "PlainFileObserver"), ("py:class", "Processor"), ("py:class", "Styles"), ("py:class", "WrappedLogger"), ("py:class", "structlog.threadlocal.TLLogger"), ("py:class", "structlog.typing.EventDict"), ("py:class", "ModuleType"), ] # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # Move type hints into the description block, instead of the func definition. autodoc_typehints = "description" autodoc_typehints_description_target = "documented" # -- Options for HTML output -------------------------------------------------- html_theme = "furo" html_theme_options = { "top_of_page_buttons": [], "light_css_variables": { "font-stack": "Inter, sans-serif", "font-stack--monospace": "BerkeleyMono, MonoLisa, ui-monospace, " "SFMono-Regular, Menlo, Consolas, Liberation Mono, monospace", }, } html_logo = "_static/structlog_logo.svg" html_static_path = ["_static"] html_css_files = ["custom.css"] htmlhelp_basename = "structlogdoc" latex_documents = [ ("index", "structlog.tex", "structlog Documentation", "Author", "manual") ] # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "structlog", "structlog 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 = [ ( "index", "structlog", "structlog Documentation", "Author", "structlog", "One line description of project.", "Miscellaneous", ) ] # -- Options for Epub output -------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # GitHub has rate limits linkcheck_ignore = [ r"https://github.com/.*/(issues|pull|compare)/\d+", r"https://twitter.com/.*", ] # Twisted's trac tends to be slow linkcheck_timeout = 300 intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "rich": ("https://rich.readthedocs.io/en/stable/", None), } structlog-24.4.0/docs/configuration.md0000644000000000000000000001147014645734712014716 0ustar00# Configuration The focus of *structlog* has always been to be flexible to a fault. The goal is that a user can use it with *any* logger of their own that is wrapped by *structlog*. That's the reason why there's an overwhelming amount of knobs to tweak, but – ideally – once you find your configuration, you don't touch it ever again and, more importantly: don't see any of it in your application code. --- Let's start at the end and introduce the ultimate convenience function that relies purely on configuration: {func}`structlog.get_logger`. The goal is to reduce your per-file application logging boilerplate to: ``` import structlog logger = structlog.get_logger() ``` To that end, you'll have to call {func}`structlog.configure` on app initialization. You can call {func}`structlog.configure` repeatedly and only set one or more settings -- the rest will not be affected. If necessary, you can always reset your global configuration back to default values using {func}`structlog.reset_defaults`. That can be handy in tests. At any time, you can check whether and how *structlog* is configured using {func}`structlog.is_configured` and {func}`structlog.get_config`}: ```pycon >>> structlog.is_configured() False >>> structlog.configure(logger_factory=structlog.stdlib.LoggerFactory()) >>> structlog.is_configured() True >>> cfg = structlog.get_config() >>> cfg["logger_factory"] ``` :::{important} Since you'll call {func}`structlog.get_logger` in module scope, it runs at import time *before* you had a chance to configure *structlog*. Therefore it returns a **lazy proxy** that returns a correctly configured *bound logger* on its first call to one of the context-managing methods like `bind()`. Thus, you must never call `new()` or `bind()` in module or class scope because , you will receive a logger configured with *structlog*'s default values. Use {func}`~structlog.get_logger`'s `initial_values` to achieve pre-populated contexts. To enable you to log with the module-global logger, it will create a temporary *bound logger* **on each call**. Therefore if you have nothing to bind but intend to do lots of log calls in a function, it makes sense performance-wise to create a local logger by calling `bind()` or `new()` without any parameters. See also {doc}`performance`. ::: ## What To Configure You can find the details in the API documentation of {func}`structlog.configure`, but let's introduce the most important ones at a high level first. ### Wrapper Classes You've met {doc}`bound-loggers` in the last chapter. They're the objects returned by {func}`~structlog.get_logger` and allow to bind key-value pairs into their private context. You can configure their type using the `wrapper_class` keyword. Whenever you bind or unbind data to a *bound logger*, this class is instantiated with the new context and returned. ### Logger Factories We've already talked about wrapped loggers responsible for the output, but we haven't explained where they come from until now. Unlike with *bound loggers*, you often need more flexibility when instantiating them. Therefore you don't configure a class; you configure a *factory* using the `logger_factory` keyword. It's a callable that returns the logger that gets wrapped and returned. In the simplest case, it's a function that returns a logger -- or just a class. But you can also pass in an instance of a class with a `__call__` method for more complicated setups. These will be passed to the logger factories. For example, if you use `structlog.get_logger("a name")` and configure *structlog* to use the standard library {class}`~structlog.stdlib.LoggerFactory`, which has support for positional parameters, the returned logger will have the name `"a name"`. For the common cases of standard library logging and Twisted logging, *structlog* comes with two factories built right in: - {class}`structlog.stdlib.LoggerFactory` - {class}`structlog.twisted.LoggerFactory` So all it takes to use standard library {mod}`logging` for output is: ``` >>> from structlog import get_logger, configure >>> from structlog.stdlib import LoggerFactory >>> configure(logger_factory=LoggerFactory()) >>> log = get_logger() >>> log.critical("this is too easy!") event='this is too easy!' ``` By using *structlog*'s {class}`structlog.stdlib.LoggerFactory`, it is also ensured that variables like function names and line numbers are expanded correctly in your log format. See {doc}`standard-library` for more details. Calling {func}`structlog.get_logger` without configuration gives you a perfectly useful {class}`structlog.PrintLogger`. We don't believe silent loggers are a sensible default. ### Processors You will meet {doc}`processors` in the next chapter. They are configured using the `processors` keyword that takes an {class}`~collections.abc.Iterable` of callables that act as processors. structlog-24.4.0/docs/console-output.md0000644000000000000000000001227214645734712015050 0ustar00# Console Output To make development a more pleasurable experience, *structlog* comes with the {mod}`structlog.dev` module. The highlight is {class}`structlog.dev.ConsoleRenderer` that offers nicely aligned and colorful[^win] console output. [^win]: Requires the [Colorama package](https://pypi.org/project/colorama/) on Windows. If either of the [Rich](https://rich.readthedocs.io/) or [*better-exceptions*](https://github.com/Qix-/better-exceptions) packages is installed, it will also pretty-print exceptions with helpful contextual data. Rich takes precedence over *better-exceptions*, but you can configure it by passing {func}`structlog.dev.plain_traceback` or {func}`structlog.dev.better_traceback` for the `exception_formatter` parameter of {class}`~structlog.dev.ConsoleRenderer`. The following output is rendered using Rich: ```{figure} _static/console_renderer.png Colorful console output by ConsoleRenderer. ``` You can find the code for the output above [in the repo](https://github.com/hynek/structlog/blob/main/show_off.py). To use it, just add it as a renderer to your processor chain. It will recognize logger names, log levels, time stamps, stack infos, and `exc_info` as produced by *structlog*'s processors and render them in special ways. :::{warning} For pretty exceptions to work, {func}`~structlog.processors.format_exc_info` must be **absent** from the processors chain. ::: *structlog*'s default configuration already uses {class}`~structlog.dev.ConsoleRenderer`, therefore if you want nice colorful output on the console, you don't have to do anything except installing Rich or *better-exceptions* (and Colorama on Windows). If you want to use it along with standard library logging, there's the {func}`structlog.stdlib.recreate_defaults` helper. :::{seealso} {doc}`exceptions` for more information on how to configure exception rendering. For the console and beyond. ::: (columns-config)= ## Console Output Configuration :::{versionadded} 23.3.0 ::: You can freely configure how the key-value pairs are formatted: colors, order, and how values are stringified. For that {class}`~structlog.dev.ConsoleRenderer` accepts the *columns* parameter that takes a list of {class}`~structlog.dev.Column`s. It allows you to assign a formatter to each key and a default formatter for the rest (by passing an empty key name). The order of the column definitions is the order in which the columns are rendered; the rest is -- depending on the *sort_keys* argument to {class}`~structlog.dev.ConsoleRenderer` -- either sorted alphabetically or in the order of the keys in the event dictionary. You can use a column definition to drop a key-value pair from the output by returning an empty string from the formatter. When the API talks about "styles", it means ANSI control strings. You can find them, for example, in [Colorama](https://github.com/tartley/colorama). It's best demonstrated by an example: ```python import structlog import colorama cr = structlog.dev.ConsoleRenderer( columns=[ # Render the timestamp without the key name in yellow. structlog.dev.Column( "timestamp", structlog.dev.KeyValueColumnFormatter( key_style=None, value_style=colorama.Fore.YELLOW, reset_style=colorama.Style.RESET_ALL, value_repr=str, ), ), # Render the event without the key name in bright magenta. structlog.dev.Column( "event", structlog.dev.KeyValueColumnFormatter( key_style=None, value_style=colorama.Style.BRIGHT + colorama.Fore.MAGENTA, reset_style=colorama.Style.RESET_ALL, value_repr=str, ), ), # Default formatter for all keys not explicitly mentioned. The key is # cyan, the value is green. structlog.dev.Column( "", structlog.dev.KeyValueColumnFormatter( key_style=colorama.Fore.CYAN, value_style=colorama.Fore.GREEN, reset_style=colorama.Style.RESET_ALL, value_repr=str, ), ), ] ) structlog.configure(processors=structlog.get_config()["processors"][:-1]+[cr]) ``` :::{hint} You can replace only the last processor using: ```python structlog.configure(processors=structlog.get_config()["processors"][:-1]+[cr]) ``` ::: ## Standard Environment Variables *structlog*'s default configuration uses colors if standard out is a TTY (that is, an interactive session). It's possible to override this behavior by setting two standard environment variables to any value except an empty string: - `FORCE_COLOR` *activates* colors, regardless of where output is going. - [`NO_COLOR`](https://no-color.org) *disables* colors, regardless of where the output is going and regardless the value of `FORCE_COLOR`. Please note that `NO_COLOR` disables _all_ styling, including bold and italics. ## Disabling Exception Pretty-Printing If you prefer the default terse Exception rendering, but still want Rich installed, you can disable the pretty-printing by instantiating {class}`structlog.dev.ConsoleRenderer()` yourself and passing `exception_formatter=structlog.dev.plain_traceback`. structlog-24.4.0/docs/contextvars.md0000644000000000000000000001517114645734712014431 0ustar00(contextvars)= # Context Variables ```{testsetup} import structlog ``` ```{testcleanup} import structlog structlog.reset_defaults() ``` The {mod}`contextvars` module in the Python standard library allows having a global *structlog* context that is local to the current execution context. The execution context can be thread-local if using threads, stored in the {mod}`asyncio` event loop, or [*greenlet*](https://greenlet.readthedocs.io/) respectively. For example, you may want to bind certain values like a request ID or the peer's IP address at the beginning of a web request and have them logged out along with the local contexts you build within our views. For that *structlog* provides the {mod}`structlog.contextvars` module with a set of functions to bind variables to a context-local context. This context is safe to be used both in threaded as well as asynchronous code. :::{warning} Since the storage mechanics of your context variables is different for each concurrency method, they are _isolated_ from each other. This can be a problem in hybrid applications like those based on [*starlette*](https://www.starlette.io) (this [includes FastAPI](https://github.com/tiangolo/fastapi/discussions/5999)) where context variables set in a synchronous context don't appear in logs from an async context and vice versa. ::: The general flow is: - Use {func}`structlog.configure` with {func}`structlog.contextvars.merge_contextvars` as your first processor (part of default configuration). - Call {func}`structlog.contextvars.clear_contextvars` at the beginning of your request handler (or whenever you want to reset the context-local context). - Call {func}`structlog.contextvars.bind_contextvars` and {func}`structlog.contextvars.unbind_contextvars` instead of your bound logger's `bind()` and `unbind()` when you want to bind and unbind key-value pairs to the context-local context. You can also use the {func}`structlog.contextvars.bound_contextvars` context manager / decorator. - Use *structlog* as normal. Loggers act as they always do, but the {func}`structlog.contextvars.merge_contextvars` processor ensures that any context-local binds get included in all of your log messages. - If you want to access the context-local storage, you use {func}`structlog.contextvars.get_contextvars` and {func}`structlog.contextvars.get_merged_contextvars`. We're sorry the word *context* means three different things in this itemization depending on ... context. ```{doctest} >>> from structlog.contextvars import ( ... bind_contextvars, ... bound_contextvars, ... clear_contextvars, ... merge_contextvars, ... unbind_contextvars, ... ) >>> from structlog import configure >>> configure( ... processors=[ ... merge_contextvars, ... structlog.processors.KeyValueRenderer(key_order=["event", "a"]), ... ] ... ) >>> log = structlog.get_logger() >>> # At the top of your request handler (or, ideally, some general >>> # middleware), clear the contextvars-local context and bind some common >>> # values: >>> clear_contextvars() >>> bind_contextvars(a=1, b=2) {'a': at ...>, 'b': at ...>} >>> # Then use loggers as per normal >>> # (perhaps by using structlog.get_logger() to create them). >>> log.info("hello") event='hello' a=1 b=2 >>> # Use unbind_contextvars to remove a variable from the context. >>> unbind_contextvars("b") >>> log.info("world") event='world' a=1 >>> # You can also bind key-value pairs temporarily. >>> with bound_contextvars(b=2): ... log.info("hi") event='hi' a=1 b=2 >>> # Now it's gone again. >>> log.info("hi") event='hi' a=1 >>> # And when we clear the contextvars state again, it goes away. >>> # a=None is printed due to the key_order argument passed to >>> # KeyValueRenderer, but it is NOT present anymore. >>> clear_contextvars() >>> log.info("hi there") event='hi there' a=None ``` ## Support for `contextvars.Token` If, for example, your request handler calls a helper function that needs to temporarily override some contextvars before restoring them back to their original values, you can use the {class}`~contextvars.Token`s returned by {func}`~structlog.contextvars.bind_contextvars` along with {func}`~structlog.contextvars.reset_contextvars` to accomplish this (much like how {meth}`contextvars.ContextVar.reset` works): ```python def foo(): bind_contextvars(a=1) _helper() log.info("a is restored!") # a=1 def _helper(): tokens = bind_contextvars(a=2) log.info("a is overridden") # a=2 reset_contextvars(**tokens) ``` (flask-example)= ## Example: Flask and Thread-Local Data Let's assume you want to bind a unique request ID, the URL path, and the peer's IP to every log entry by storing it in thread-local storage that is managed by context variables: ```python import logging import sys import uuid import flask from .some_module import some_function import structlog logger = structlog.get_logger() app = flask.Flask(__name__) @app.route("/login", methods=["POST", "GET"]) def some_route(): # You would put this into some kind of middleware or processor so it's set # automatically for all requests in all views. structlog.contextvars.clear_contextvars() structlog.contextvars.bind_contextvars( view=flask.request.path, request_id=str(uuid.uuid4()), peer=flask.request.access_route[0], ) # End of belongs-to-middleware. log = logger.bind() # do something # ... log.info("user logged in", user="test-user") # ... some_function() # ... return "logged in!" if __name__ == "__main__": logging.basicConfig( format="%(message)s", stream=sys.stdout, level=logging.INFO ) structlog.configure( processors=[ structlog.contextvars.merge_contextvars, # <--!!! structlog.processors.KeyValueRenderer( key_order=["event", "view", "peer"] ), ], logger_factory=structlog.stdlib.LoggerFactory(), ) app.run() ``` `some_module.py`: ```python from structlog import get_logger logger = get_logger() def some_function(): # ... logger.error("user did something", something="shot_in_foot") # ... ``` This would result among other the following lines to be printed: ```text event='user logged in' view='/login' peer='127.0.0.1' user='test-user' request_id='e08ddf0d-23a5-47ce-b20e-73ab8877d736' event='user did something' view='/login' peer='127.0.0.1' something='shot_in_foot' request_id='e08ddf0d-23a5-47ce-b20e-73ab8877d736' ``` As you can see, `view`, `peer`, and `request_id` are present in **both** log entries. structlog-24.4.0/docs/exceptions.md0000644000000000000000000000436714645734712014237 0ustar00# Exceptions While you should use a proper crash reporter like [Sentry](https://sentry.io) in production, *structlog* has helpers for formatting exceptions for humans and machines. All *structlog*'s exception features center around passing an `exc_info` key-value pair in the event dict. There are three possible behaviors depending on its value: 1. If the value is a tuple, render it as if it was returned by {func}`sys.exc_info`. 2. If the value is an Exception, render it. 3. If the value is true but no tuple, call {func}`sys.exc_info` and render that. If there is no `exc_info` key or false, the event dict is not touched. This behavior is analog to the one of the stdlib's logging. ## Transformations *structlog* comes with {class}`structlog.processors.ExceptionRenderer` that deduces and removes the `exc_info` key as outlined above, calls a user-supplied function with the synthesized `exc_info`, and stores its return value in the `exception` key. The most common use-cases are already covered by the following processors: {func}`structlog.processors.format_exc_info` : Formats it to a flat string like the standard library would on the console. {obj}`structlog.processors.dict_tracebacks` : Uses {class}`structlog.tracebacks.ExceptionDictTransformer` to give you a structured and JSON-serializable `exception` key. ## Console Rendering Our {doc}`console-output`'s {class}`structlog.dev.ConsoleRenderer` takes an *exception_formatter* argument that allows for customizing the output of exceptions. {func}`structlog.dev.plain_traceback` : Is the default if neither [Rich] nor [*better-exceptions*] are installed. As the name suggests, it renders a plain traceback. {func}`structlog.dev.better_traceback` : Uses [*better-exceptions*] to render a colorful traceback. : It's the default if *better-exceptions* is installed and Rich is not. {class}`structlog.dev.RichTracebackFormatter` : Uses [Rich] to render a colorful traceback. It's a class because it allows for customizing the output by passing arguments to Rich. : It's the default if Rich is installed. :::{seealso} {doc}`console-output` for more information on *structlog*'s console features. ::: [*better-exceptions*]: https://github.com/qix-/better-exceptions [Rich]: https://github.com/Textualize/rich structlog-24.4.0/docs/frameworks.md0000644000000000000000000001057314645734712014232 0ustar00# Frameworks To have consistent log output, it makes sense to configure *structlog* *before* any logging is done. The best place to perform your configuration varies with applications and frameworks. If you use standard library's {mod}`logging`, it makes sense to configure them next to each other. ## Celery [Celery](https://docs.celeryq.dev/)'s multi-process architecture leads unavoidably to race conditions that show up as interleaved logs. It ships standard library-based helpers in the form of [`celery.utils.log.get_task_logger()`](https://docs.celeryq.dev/en/stable/userguide/tasks.html#logging) that you should use inside of tasks to prevent that problem. The most straight-forward way to integrate that with *structlog* is using {doc}`standard-library` and wrapping that logger using {func}`structlog.wrap_logger`: ```python from celery.utils.log import get_task_logger logger = structlog.wrap_logger(get_task_logger(__name__)) ``` If you want to automatically bind task metadata to your {doc}`contextvars`, you can use [Celery's signals](https://docs.celeryq.dev/en/stable/userguide/signals.html): ```python from celery import signals @signals.task_prerun.connect def on_task_prerun(sender, task_id, task, args, kwargs, **_): structlog.contextvars.bind_contextvars(task_id=task_id, task_name=task.name) ``` See [this issue](https://github.com/hynek/structlog/issues/287) for more details. ## Django [*django-structlog*](https://pypi.org/project/django-structlog/) is a popular and well-maintained package that does all the heavy lifting. ## Flask See Flask's [Logging docs](https://flask.palletsprojects.com/en/latest/logging/). Generally speaking: configure *structlog* *before* instantiating `flask.Flask`. Here's a [signal handler](https://flask.palletsprojects.com/en/latest/signals/) that binds various request details into [*context variables*](contextvars.md): ```python def bind_request_details(sender: Flask, **extras: dict[str, Any]) -> None: structlog.contextvars.clear_contextvars() structlog.contextvars.bind_contextvars( request_id=request.headers.get("X-Unique-ID", "NONE"), peer=peer, ) if current_user.is_authenticated: structlog.contextvars.bind_contextvars( user_id=current_user.get_id(), ) ``` You add it to an existing `app` like this: ```python from flask import request_started request_started.connect(bind_request_details, app) ``` ## Litestar [Litestar](https://docs.litestar.dev/) comes with *structlog* support [out of the box](https://docs.litestar.dev/latest/usage/logging.html). ## OpenTelemetry The [Python OpenTelemetry SDK](https://opentelemetry.io/docs/languages/python/) offers an easy API to get the current span, so you can enrich your logs with a straight-forward processor: ```python from opentelemetry import trace def add_open_telemetry_spans(_, __, event_dict): span = trace.get_current_span() if not span.is_recording(): event_dict["span"] = None return event_dict ctx = span.get_span_context() parent = getattr(span, "parent", None) event_dict["span"] = { "span_id": hex(ctx.span_id), "trace_id": hex(ctx.trace_id), "parent_span_id": None if not parent else hex(parent.span_id), } return event_dict ``` ## Pyramid Configure it in the [application constructor](https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/startup.html#the-startup-process). Here's an example for a Pyramid [*tween*](https://docs.pylonsproject.org/projects/pyramid/en/latest/glossary.html#term-tween) that stores various request-specific data into [*context variables*](contextvars.md): ```python @dataclass class StructLogTween: handler: Callable[[Request], Response] registry: Registry def __call__(self, request: Request) -> Response: structlog.contextvars.clear_contextvars() structlog.contextvars.bind_contextvars( peer=request.client_addr, request_id=request.headers.get("X-Unique-ID", "NONE"), user_agent=request.environ.get("HTTP_USER_AGENT", "UNKNOWN"), user=request.authenticated_userid, ) return self.handler(request) ``` ## Twisted The [plugin definition](https://docs.twisted.org/en/stable/core/howto/plugin.html) is the best place. If your app is not a plugin, put it into your [tac file](https://docs.twisted.org/en/stable/core/howto/application.html). structlog-24.4.0/docs/getting-started.md0000644000000000000000000002434514645734712015161 0ustar00# Getting Started (install)= ## Installation You can install *structlog* from [PyPI](https://pypi.org/project/structlog/) using *pip*: ```console $ python -m pip install structlog ``` If you want pretty exceptions in development (you know you do!), additionally install either [Rich] or [*better-exceptions*]. Try both to find out which one you like better -- the screenshot in the README and docs homepage is rendered by Rich. On **Windows**, you also have to install [Colorama](https://pypi.org/project/colorama/) if you want colorful output beside exceptions. ## Your First Log Entry A lot of effort went into making *structlog* accessible without reading pages of documentation. As a result, the simplest possible usage looks like this: ```{doctest} >>> import structlog >>> log = structlog.get_logger() >>> log.info("hello, %s!", "world", key="value!", more_than_strings=[1, 2, 3]) # doctest: +SKIP 2022-10-07 10:41:29 [info ] hello, world! key=value! more_than_strings=[1, 2, 3] ``` Here, *structlog* takes advantage of its default settings: - Output is sent to **[standard out](https://en.wikipedia.org/wiki/Standard_out#Standard_output_.28stdout.29)** instead of doing nothing. - It **imitates** standard library {mod}`logging`'s **log level names** for familiarity. By default, no level-based filtering is done, but it comes with a **very fast [filtering machinery](filtering)**. - Like in `logging`, positional arguments are [**interpolated into the message string using %**](https://docs.python.org/3/library/stdtypes.html#old-string-formatting). That might look dated, but it's *much* faster than using {any}`str.format` and allows *structlog* to be used as drop-in replacement for {mod}`logging`. If you *know* that the log entry is *always* gonna be logged out, just use [f-strings](https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals) which are the fastest. - All keywords are formatted using {class}`structlog.dev.ConsoleRenderer`. That in turn uses {func}`repr` to serialize **any value to a string**. - It's rendered in nice **{doc}`colors `**. - If you have [Rich] or [*better-exceptions*] installed, **exceptions** will be rendered in **colors** and with additional **helpful information**. Please note that even in most complex logging setups the example would still look just like that thanks to {doc}`configuration`. Using the defaults, as above, is equivalent to: ```python import logging import structlog structlog.configure( processors=[ structlog.contextvars.merge_contextvars, structlog.processors.add_log_level, structlog.processors.StackInfoRenderer(), structlog.dev.set_exc_info, structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False), structlog.dev.ConsoleRenderer() ], wrapper_class=structlog.make_filtering_bound_logger(logging.NOTSET), context_class=dict, logger_factory=structlog.PrintLoggerFactory(), cache_logger_on_first_use=False ) log = structlog.get_logger() ``` :::{note} - {func}`structlog.stdlib.recreate_defaults()` allows you to switch *structlog* to using standard library's `logging` module for output for better interoperability with just one function call. - {func}`~structlog.make_filtering_bound_logger()` (re-)uses {any}`logging`'s log levels, but doesn't use `logging` at all. The exposed API is {class}`~structlog.typing.FilteringBoundLogger`. - For brevity and to enable doctests, all further examples in *structlog*'s documentation use the more simplistic {class}`~structlog.processors.KeyValueRenderer()` without timestamps. ::: There you go, structured logging! However, this alone wouldn't warrant its own package. After all, there's even a [recipe] on structured logging for the standard library. So let's go a step further. (building-ctx)= ## Building a Context Imagine a hypothetical web application that wants to log out all relevant data with just the APIs that we've introduced so far: ```python def view(request): user_agent = request.get("HTTP_USER_AGENT", "UNKNOWN") peer_ip = request.client_addr if something: log.info("something", user_agent=user_agent, peer_ip=peer_ip) return "something" elif something_else: log.info("something_else", user_agent=user_agent, peer_ip=peer_ip) return "something_else" else: log.info("else", user_agent=user_agent, peer_ip=peer_ip) return "else" ``` The calls themselves are nice and straight to the point, however you're repeating yourself all over the place. It's easy to forget to add a key-value pair in one of the incantations. At this point, you'll be tempted to write a closure like: ```python def log_closure(event): log.info(event, user_agent=user_agent, peer_ip=peer_ip) ``` inside of the view. Problem solved? Not quite. What if the parameters are introduced step by step? And do you really want to have a logging closure in each of your views? Let's have a look at a better approach: ```python def view(request): log = log.bind( user_agent=request.get("HTTP_USER_AGENT", "UNKNOWN"), peer_ip=request.client_addr, ) if foo := request.get("foo"): log = log.bind(foo=foo) if something: log.info("something") return "something" elif something_else: log.info("something_else") return "something_else" else: log.info("else") return "else" ``` Suddenly your logger becomes your closure! --- To *structlog*, a log entry is just a dictionary called *event dict\[ionary\]*: - You can pre-build a part of the dictionary step by step. These pre-saved values are called the *context*. - As soon as an *event* happens -- which are the `kwargs` of the log call -- it is merged together with the *context* to an *event dict* and logged out. - Each logger with its context is *immutable*. You manipulate the context by creating new loggers using `bind()` and `unbind()`. The last point is very clean and easy to reason about, but sometimes it's useful to store _some_ data globally. In our example above the peer IP comes to mind. There's no point in extracting it in every view! For that, *structlog* gives you thread-local context storage based on the {mod}`contextvars` module: ```pycon >>> structlog.contextvars.bind_contextvars(peer_ip="1.2.3.4") >>> structlog.get_logger().info("something") 2022-10-10 10:18:05 [info ] something peer_ip=1.2.3.4 ``` See {doc}`contextvars` for more information and a more complete example. ## Manipulating Log Entries in Flight Now that your log events are dictionaries, it's also much easier to manipulate them than if they were plain strings. To facilitate that, *structlog* has the concept of {doc}`processor chains `. A processor is a function that receives the event dictionary along with two other arguments and returns a new event dictionary that may or may not differ from the one it got passed. The next processor in the chain receives that returned dictionary instead of the original one. Let's assume you wanted to add a timestamp to every event dict. The processor would look like this: ```{doctest} >>> import datetime >>> def timestamper(_, __, event_dict): ... event_dict["time"] = datetime.datetime.now().isoformat() ... return event_dict ``` Plain Python, plain dictionaries. Now you have to tell *structlog* about your processor by {doc}`configuring ` it: ```{doctest} >>> structlog.configure(processors=[timestamper, structlog.processors.KeyValueRenderer()]) >>> structlog.get_logger().info("hi") # doctest: +SKIP event='hi' time='2018-01-21T09:37:36.976816' ``` ## Rendering Finally you want to have control over the actual format of your log entries. As you may have noticed in the previous section, renderers are just processors too. The type of the return value that is required from the renderer depends on the input that the *logger* that is wrapped by *structlog* needs. While usually it's a string or bytes, there's no rule saying it _has_ to be a string! So assuming you want to follow [best practices](logging-best-practices.md) and render your event dictionary to JSON that is picked up by a log aggregation system like ELK or Graylog, *structlog* comes with batteries included -- you just have to tell it to use its {class}`~structlog.processors.JSONRenderer`: ```{doctest} >>> structlog.configure(processors=[structlog.processors.JSONRenderer()]) >>> structlog.get_logger().info("hi") {"event": "hi"} ``` ## *structlog* and Standard Library's `logging` While *structlog*'s loggers are very fast and sufficient for the majority of our users, you're not bound to them. Instead, it's been designed from day one to wrap your *existing* loggers and **add** *structure* and *incremental context building* to them. The most prominent example of such an "existing logger" is certainly the logging module in the standard library. To make this common case as simple as possible, *structlog* comes with [some tools](standard-library.md) to help you. As noted before, the fastest way to transform *structlog* into a `logging`-friendly package is calling {func}`structlog.stdlib.recreate_defaults()`. ## asyncio The default *bound logger* that you get back from {func}`structlog.get_logger()` and standard library's {class}`structlog.stdlib.BoundLogger` don't have just the familiar log methods like `debug()` or `info()`, but also their async cousins, that simply prefix the name with an a: ```pycon >>> import asyncio >>> logger = structlog.get_logger() >>> async def f(): ... await logger.ainfo("async hi!") ... >>> logger.info("Loop isn't running yet, but we can log!") 2023-04-06 07:25:48 [info ] Loop isn't running yet, but we can log! >>> asyncio.run(f()) 2023-04-06 07:26:08 [info ] async hi! ``` You can use the sync and async logging methods interchangeably within the same application. ## Liked what you saw? Now you're all set for the rest of the user's guide and can start reading about [bound loggers](bound-loggers.md) -- the heart of *structlog*. ```{include} ../README.md :start-after: :end-before: ``` [*better-exceptions*]: https://github.com/qix-/better-exceptions [recipe]: https://docs.python.org/3/howto/logging-cookbook.html#implementing-structured-logging [Rich]: https://github.com/Textualize/rich structlog-24.4.0/docs/glossary.md0000644000000000000000000000512114645734712013706 0ustar00# Glossary Please feel free to [file an issue](https://github.com/hynek/structlog/issues) if you think some important concept is missing here. :::{glossary} Event Dictionary Often abbreviated as *event dict*. It's a dictionary that contains all the information that is logged, with the `event` key having the special role of being the name of the event. It's the result of the values bound to the {term}`bound logger`'s context and the key-value pairs passed to the logging method. It is then passed through the {term}`processor` chain that can add, modify, and even remove key-value pairs. Bound Logger An instance of a {class}`structlog.typing.BindableLogger` that is returned by either {func}`structlog.get_logger` or the bind/unbind/new methods on it. As the name suggests, it's possible to bind key-value pairs to it -- this data is called the {term}`context` of the logger. Its methods are the user's logging API and depend on the type of the bound logger. The two most common implementations are {class}`structlog.BoundLogger` and {class}`structlog.stdlib.BoundLogger`. Bound loggers are **immutable**. The context can only be modified by creating a new bound logger using its `bind()`and `unbind()` methods. :::{seealso} {doc}`bound-loggers` ::: Context A dictionary of key-value pairs belonging to a {term}`bound logger`. When a log entry is logged out, the context is the base for the event dictionary with the keyword arguments of the logging method call merged in. Bound loggers are **immutable**, so it's not possible to modify a context directly. But you can create a new bound logger with a different context using its `bind()` and `unbind()` methods. Native Loggers Loggers created using {func}`structlog.make_filtering_bound_logger` which includes the default configuration. These loggers are very fast and do **not** use the standard library. Wrapped Logger The logger that is wrapped by *structlog* and that is responsible for the actual output. By default it's a {class}`structlog.PrintLogger` for native logging. Another popular choice is {class}`logging.Logger` for standard library logging. :::{seealso} {doc}`standard-library` ::: Processor A callable that is called on every log entry. It receives the return value of its predecessor as an argument and returns a new event dictionary. This allows for composable transformations of the event dictionary. The result of the final processor is passed to the {term}`wrapped logger`. :::{seealso} {doc}`processors` ::: ::: structlog-24.4.0/docs/index.md0000644000000000000000000000674714645734712013171 0ustar00# structlog *Simple. Powerful. Fast. Pick three.* Release **{sub-ref}`release`** ([What's new?](https://github.com/hynek/structlog/blob/main/CHANGELOG.md)) --- ```{include} ../README.md :start-after: :end-before: ``` ```{include} ../README.md :start-after: :end-before: ``` If you’d like more information on why structured logging in general – and *structlog* in particular – are good ideas, we’ve prepared a [summary](why.md) just for you. Otherwise, let’s dive right in! ```{toctree} :hidden: true why ``` ## Basics The first chapters teach you all you need to use *structlog* productively. They build gently on each other, so ideally, read them in order. If anything seems confusing, don't hesitate to have a look at our {doc}`glossary`! ```{toctree} :maxdepth: 2 :caption: Basics getting-started bound-loggers configuration processors contextvars exceptions ``` ## Development Affordances *structlog*'s focus is production systems, but it comes with **pretty console logging** and handy in-development helpers both for your **comfort** and your code's **quality**. ```{toctree} :maxdepth: 2 :caption: Development Affordances console-output testing typing ``` (integration)= ## Integration with Existing Systems *structlog* is both zero-config as well as highly configurable. You can use it on its own or integrate with existing systems. Dedicated support for the standard library and Twisted is shipped out-of-the-box. ```{toctree} :maxdepth: 2 :caption: Integrations frameworks standard-library twisted ``` ## *structlog* in Practice The following chapters deal with considerations of using *structlog* in the real world. ```{toctree} :maxdepth: 2 :caption: In Practice recipes logging-best-practices performance ``` ## Reference ```{toctree} :maxdepth: 2 :caption: Reference api glossary genindex modindex ``` ## Deprecated Features ```{toctree} :maxdepth: 1 :caption: Deprecated Features thread-local ``` ```{toctree} :hidden: :caption: Meta license PyPI GitHub Changelog Contributing Security Policy Funding ``` structlog-24.4.0/docs/license.md0000644000000000000000000000201614645734712013465 0ustar00# License and Hall of Fame *structlog* is licensed both under the [Apache License, Version 2](https://choosealicense.com/licenses/apache/) and the [MIT license](https://choosealicense.com/licenses/mit/). Any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. --- The reason for that is to be both protected against patent claims by own contributors and still allow the usage within GPLv2 software. For more legal details, see [this issue](https://github.com/pyca/cryptography/issues/1209) on the bug tracker of PyCA's *cryptography* project. The full license texts can be also found in the source code repository: - [Apache License 2.0](https://github.com/hynek/structlog/blob/main/LICENSE-APACHE) - [MIT](https://github.com/hynek/structlog/blob/main/LICENSE-MIT) ## Credits ```{include} ../README.md :parser: myst_parser.sphinx_ :start-after: "## Credits" :end-before: ``` structlog-24.4.0/docs/logging-best-practices.md0000644000000000000000000001147514645734712016410 0ustar00# Logging Best Practices Logging is not a new concept and is in no way unique to Python. Logfiles have existed for decades, and there's little reason to reinvent the wheel in our little world. Therefore, let's rely on proven tools as much as possible and do only the bare minimum inside Python applications[^unix]. A simple but powerful approach is to log to unbuffered [standard out](https://en.wikipedia.org/wiki/Standard_out#Standard_output_.28stdout.29 ) and let other tools take care of the rest. That can be your terminal window while developing; it can be [*systemd*](https://en.wikipedia.org/wiki/Systemd) redirecting your log entries to [*syslogd*](https://en.wikipedia.org/wiki/Syslogd) and rotating them using [*logrotate*](https://github.com/logrotate/logrotate); or it can be your [cluster manager](https://kubernetes.io/docs/concepts/cluster-administration/logging/) forwarding them to an obscenely expensive log aggregator service. It doesn't matter where or how your application runs -- it just works, and the reason why the popular [*Twelve-Factor App* methodology](https://12factor.net/logs) suggests just that. [^unix]: This is obviously a privileged UNIX-centric view but even Windows has tools and means for log management although we won't be able to discuss them here. ## Canonical Log Lines Generally speaking, having as few log entries per request as possible is a good thing. The less noise, the more insights. *structlog*'s ability to {ref}`bind data to loggers incrementally ` -- plus {doc}`loggers that are local to the current execution context ` -- can help you to minimize the output to a *single log entry*. At Stripe, this concept is called [Canonical Log Lines](https://brandur.org/canonical-log-lines). ## Pretty Printing vs. Structured Output Colorful and pretty printed log messages are nice during development when you locally run your code. However, in production you should emit structured output (like JSON) which is a lot easier to parse by log aggregators. Since you already log in a structured way, writing JSON output with *structlog* comes naturally. You can even generate structured exception tracebacks. This makes analyzing errors easier, since log aggregators can render JSON much better than multiline strings with a lot escaped quotation marks. Here is a simple example of how you can have pretty logs during development and JSON output when your app is running in a production context: ```{doctest} >>> import sys >>> import structlog >>> >>> shared_processors = [ ... # Processors that have nothing to do with output, ... # e.g., add timestamps or log level names. ... ] >>> if sys.stderr.isatty(): ... # Pretty printing when we run in a terminal session. ... # Automatically prints pretty tracebacks when "rich" is installed ... processors = shared_processors + [ ... structlog.dev.ConsoleRenderer(), ... ] ... else: ... # Print JSON when we run, e.g., in a Docker container. ... # Also print structured tracebacks. ... processors = shared_processors + [ ... structlog.processors.dict_tracebacks, ... structlog.processors.JSONRenderer(), ... ] >>> structlog.configure(processors) ``` ## Centralized Logging Nowadays you usually don't want your log files in compressed archives distributed over dozens -- if not thousands -- of servers or cluster nodes. You want them in a single location. Parsed, indexed, and easy to search. ### ELK The ELK stack ([**E**lasticsearch][elasticsearch], [**L**ogstash][logstash], [**K**ibana][kibana]) from Elastic is a great way to store, parse, and search your logs. The way it works is that you have local log shippers like [Filebeat] that parse your log files and forward the log entries to your [Logstash] server. Logstash parses the log entries and stores them in [Elasticsearch]. Finally, you can view and search them in [Kibana]. If your log entries consist of a JSON dictionary, this is fairly easy and efficient. All you have to do is to tell [Logstash] either that your log entries are prepended with a timestamp from {class}`~structlog.processors.TimeStamper` or the name of your timestamp field. ### Graylog [Graylog](https://graylog.org/) goes one step further. It not only supports everything those above do (and then some); you can also directly log JSON entries towards it -- optionally even through an AMQP server (like [RabbitMQ](https://www.rabbitmq.com/)) for better reliability. Additionally, [Graylog's Extended Log Format](https://go2docs.graylog.org/current/getting_in_log_data/gelf.html) (GELF) allows for structured data which makes it an obvious choice to use together with *structlog*. [elasticsearch]: https://www.elastic.co/elasticsearch [filebeat]: https://github.com/elastic/beats/tree/main/filebeat [kibana]: https://www.elastic.co/kibana [logstash]: https://www.elastic.co/logstash structlog-24.4.0/docs/make.bat0000644000000000000000000001175614645734712013141 0ustar00@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) 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. singlehtml to make a single large HTML file 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. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs 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 if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 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 if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\structlog.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\structlog.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 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 if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end structlog-24.4.0/docs/performance.md0000644000000000000000000001055614645734712014354 0ustar00# Performance Here are a few hints how to get the best performance out of *structlog* in production: - Use *structlog*'s native *BoundLogger* (created using {func}`structlog.make_filtering_bound_logger`) if you want to use level-based filtering. `return None` is hard to beat. - Avoid (frequently) calling log methods on loggers you get back from {func}`structlog.get_logger` or {func}`structlog.wrap_logger`. Since those functions are usually called in module scope and thus before you are able to configure them, they return a proxy object that assembles the correct logger on demand. Create a local logger if you expect to log frequently without binding: ```python logger = structlog.get_logger() def f(): log = logger.bind() for i in range(1000000000): log.info("iterated", i=i) ``` Since global scope lookups are expensive in Python, it's generally a good idea to copy frequently-used symbols into local scope. - Set the *cache_logger_on_first_use* option to `True` so the aforementioned on-demand loggers will be assembled only once and cached for future uses: ```python configure(cache_logger_on_first_use=True) ``` This has two drawbacks: 1. Later calls of {func}`~structlog.configure` don't have any effect on already cached loggers -- that shouldn't matter outside of {doc}`testing ` though. 2. The resulting bound logger is not pickleable. Therefore, you can't set this option if you, for example, plan on passing loggers around using {mod}`multiprocessing`. - Avoid sending your log entries through the standard library if you can: its dynamic nature and flexibility make it a major bottleneck. Instead use {class}`structlog.WriteLoggerFactory` or -- if your serializer returns bytes (for example, [*orjson*] or [*msgspec*]) -- {class}`structlog.BytesLoggerFactory`. You can still configure `logging` for packages that you don't control, but avoid it for your *own* log entries. - Configure {class}`~structlog.processors.JSONRenderer` to use a faster JSON serializer than the standard library. Possible alternatives are among others are [*orjson*], [*msgspec*], or [RapidJSON](https://pypi.org/project/python-rapidjson/). - Be conscious about whether and how you use *structlog*'s *asyncio* support. While it's true that moving log processing into separate threads prevents your application from hanging, it also comes with a performance cost. Decide judiciously whether or not you're willing to pay that price. If your processor chain has a good and predictable performance without external dependencies (as it should), it might not be worth it. ## Example Here's an example for a production-ready *structlog* configuration that's as fast as it gets: ```python import logging import orjson import structlog structlog.configure( cache_logger_on_first_use=True, wrapper_class=structlog.make_filtering_bound_logger(logging.INFO), processors=[ structlog.contextvars.merge_contextvars, structlog.processors.add_log_level, structlog.processors.format_exc_info, structlog.processors.TimeStamper(fmt="iso", utc=True), structlog.processors.JSONRenderer(serializer=orjson.dumps), ], logger_factory=structlog.BytesLoggerFactory(), ) ``` It has the following properties: - Caches all loggers on first use. - Filters all log entries below the `info` log level **very** efficiently. The `debug` method literally consists of `return None`. - Supports {doc}`contextvars` (thread-local contexts outside of *asyncio*). - Adds the log level name. - Renders exceptions into the `exception` key. - Adds an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp under the `timestamp` key in the UTC timezone. - Renders the log entries as JSON using [*orjson*] which is faster than *plain* logging in {mod}`logging`. - Uses {class}`structlog.BytesLoggerFactory` because *orjson* returns bytes. That saves encoding ping-pong. Therefore a log entry might look like this: ```json {"event":"hello","level":"info","timestamp":"2023-11-02T08:03:38.298565Z"} ``` --- If you need standard library support for external projects, you can either just use a JSON formatter like [*python-json-logger*](https://pypi.org/project/python-json-logger/), or pipe them through *structlog* as documented in {doc}`standard-library`. [*orjson*]: https://github.com/ijl/orjson [*msgspec*]: https://jcristharif.com/msgspec/ structlog-24.4.0/docs/processors.md0000644000000000000000000001356614645734712014261 0ustar00# Processors The true power of *structlog* lies in its *combinable log processors*. A log processor is a regular callable or in other words: A function or an instance of a class with a `__call__()` method. (chains)= ## Chains The *processor chain* is a list of processors. Each processors receives three positional arguments: **logger** : Your wrapped logger object. For example {class}`logging.Logger` or {class}`structlog.typing.FilteringBoundLogger` (default). **method_name** : The name of the wrapped method. If you called `log.warning("foo")`, it will be `"warning"`. **event_dict** : Current context together with the current event. If the context was `{"a": 42}` and the event is `"foo"`, the initial `event_dict` will be `{"a":42, "event": "foo"}`. The return value of each processor is passed on to the next one as `event_dict` until finally the return value of the last processor gets passed into the wrapped logging method. :::{note} *structlog* only looks at the return value of the **last** processor. That means that as long as you control the next processor in the chain (the processor that will get your return value passed as an argument), you can return whatever you want. Returning a modified event dictionary from your processors is just a convention to make processors composable. ::: ### Examples If you set up your logger like: ```python structlog.configure(processors=[f1, f2, f3]) log = structlog.get_logger().bind(x=42) ``` and call `log.info("some_event", y=23)`, it results in the following call chain: ```python wrapped_logger.info( f3(wrapped_logger, "info", f2(wrapped_logger, "info", f1(wrapped_logger, "info", {"event": "some_event", "x": 42, "y": 23}) ) ) ) ``` In this case, `f3` has to make sure it returns something `wrapped_logger.info` can handle (see {ref}`adapting`). For the example with `PrintLogger` above, this means `f3` must return a string. The simplest modification a processor can make is adding new values to the `event_dict`. Parsing human-readable timestamps is tedious, not so [UNIX timestamps](https://en.wikipedia.org/wiki/UNIX_time) -- let's add one to each log entry: ```python import calendar import time def timestamper(logger, log_method, event_dict): event_dict["timestamp"] = calendar.timegm(time.gmtime()) return event_dict ``` :::{important} You're explicitly allowed to modify the `event_dict` parameter, because a copy has been created before calling the first processor. ::: Please note that *structlog* comes with such a processor built in: {class}`~structlog.processors.TimeStamper`. ## Filtering If a processor raises {class}`structlog.DropEvent`, the event is silently dropped. Therefore, the following processor drops every entry: ```python from structlog import DropEvent def dropper(logger, method_name, event_dict): raise DropEvent ``` But we can do better than that! (cond-drop)= How about dropping only log entries that are marked as coming from a certain peer (for example, monitoring)? ```python class ConditionalDropper: def __init__(self, peer_to_ignore): self._peer_to_ignore = peer_to_ignore def __call__(self, logger, method_name, event_dict): """ >>> cd = ConditionalDropper("127.0.0.1") >>> cd(None, None, {"event": "foo", "peer": "10.0.0.1"}) {'peer': '10.0.0.1', 'event': 'foo'} >>> cd(None, None, {"event": "foo", "peer": "127.0.0.1"}) Traceback (most recent call last): ... DropEvent """ if event_dict.get("peer") == self._peer_to_ignore: raise DropEvent return event_dict ``` Since it's so common to filter by the log level, *structlog* comes with {func}`structlog.make_filtering_bound_logger` that filters log entries before they even enter the processor chain. It does **not** use the standard library, but it does use its names and order of log levels. (adapting)= ## Adapting and Rendering An important role is played by the *last* processor because its duty is to adapt the `event_dict` into something the logging methods of the *wrapped logger* understand. With that, it's also the *only* processor that needs to know anything about the underlying system. It can return one of three types: - An Unicode string ({any}`str`), a bytes string ({any}`bytes`), or a {any}`bytearray` that is passed as the first (and only) positional argument to the underlying logger. - A tuple of `(args, kwargs)` that are passed as `log_method(*args, **kwargs)`. - A dictionary which is passed as `log_method(**kwargs)`. Therefore `return "hello world"` is a shortcut for `return (("hello world",), {})` (the example in {ref}`chains` assumes this shortcut has been taken). This should give you enough power to use *structlog* with any logging system while writing agnostic processors that operate on dictionaries. :::{versionchanged} 14.0.0 Allow final processor to return a {any}`dict`. ::: :::{versionchanged} 20.2.0 Allow final processor to return a {any}`bytes`. ::: :::{versionchanged} 21.2.0 Allow final processor to return a {any}`bytearray`. ::: ### Examples The probably most useful formatter for string based loggers is {class}`structlog.processors.JSONRenderer`. Advanced log aggregation and analysis tools like [*Logstash*](https://www.elastic.co/logstash) offer features like telling them "this is JSON, deal with it" instead of fiddling with regular expressions. For a list of shipped processors, check out the {ref}`API documentation `. ## Third-Party Packages *structlog* was specifically designed to be as composable and reusable as possible, so whatever you're missing: chances are, you can solve it with a processor! Since processors are self-contained callables, it's easy to write your own and to share them in separate packages. We collect those packages in our [GitHub Wiki](https://github.com/hynek/structlog/wiki/Third-Party-Extensions) and encourage you to add your package too! structlog-24.4.0/docs/recipes.md0000644000000000000000000001623514645734712013505 0ustar00# Recipes Because *structlog* is entirely based on dictionaries and callables, the sky is the limit with what you can achieve. That can be daunting in the beginning, so here are a few examples of tasks that have come up repeatedly. Please note that recipes related to integration with frameworks have an [own chapter](frameworks.md). (rename-event)= ## Renaming the `event` Key The name of the event is hard-coded in *structlog* to `event`. But that doesn't mean it has to be called that in your logs. With the {class}`structlog.processors.EventRenamer` processor, you can, for instance, rename the log message to `msg` and use `event` for something custom, that you bind to `_event` in your code: ```pycon >>> from structlog.processors import EventRenamer >>> event_dict = {"event": "something happened", "_event": "our event!"} >>> EventRenamer("msg", "_event")(None, None, event_dict) {'msg': 'something happened', 'event': 'our event!'} ``` (finer-filtering)= ## Fine-Grained Log-Level Filtering *structlog*'s native log levels as provided by {func}`structlog.make_filtering_bound_logger` only know **one** log level – the one that is passed to `make_filtering_bound_logger()`. Sometimes, that can be a bit too coarse, though. You can achieve finer control by adding the {class}`~structlog.processors.CallsiteParameterAdder` processor and writing a simple processor that acts on the call site data added. Let's assume you have the following code: ```python logger = structlog.get_logger() def f(): logger.info("f called") def g(): logger.info("g called") f() g() ``` And you don't want to see log entries from function `f`. You add {class}`~structlog.processors.CallsiteParameterAdder` to the processor chain and then look at the `func_name` field in the *event dict*: ```python def filter_f(_, __, event_dict): if event_dict.get("func_name") == "f": raise structlog.DropEvent return event_dict structlog.configure( processors=[ structlog.processors.CallsiteParameterAdder( [structlog.processors.CallsiteParameter.FUNC_NAME] ), filter_f, # <-- your processor! structlog.processors.KeyValueRenderer(), ] ) ``` Running this gives you: ``` event='g called' func_name='g' ``` {class}`~structlog.processors.CallsiteParameterAdder` is *very* powerful in what info it can add, so your possibilities are limitless. Pick the data you're interested in from the {class}`structlog.processors.CallsiteParameter` {class}`~enum.Enum`. (custom-wrappers)= ## Custom Wrappers ```{testsetup} import structlog structlog.configure( processors=[structlog.processors.KeyValueRenderer()], ) ``` ```{testcleanup} import structlog structlog.reset_defaults() ``` The type of the *bound loggers* that are returned by {func}`structlog.get_logger()` is called the *wrapper class*, because it wraps the original logger that takes care of the output. This wrapper class is [configurable](configuration.md). Originally, *structlog* used a generic wrapper class {class}`structlog.BoundLogger` by default. That class still ships with *structlog* and can wrap *any* logger class by intercepting unknown method names and proxying them to the wrapped logger. Nowadays, the default is a {class}`structlog.typing.FilteringBoundLogger` that imitates standard library’s log levels with the possibility of efficiently filtering at a certain level (inactive log methods are a plain `return None` each). If you’re integrating with {mod}`logging` or Twisted, you may want to use one of their specific *bound loggers* ({class}`structlog.stdlib.BoundLogger` and {class}`structlog.twisted.BoundLogger`, respectively). --- On top of that all, you can also write your own wrapper classes. To make it easy for you, *structlog* comes with the class {class}`structlog.BoundLoggerBase` which takes care of all data binding duties so you just add your log methods if you choose to sub-class it. (wrapper-class-example)= ### Example It’s easiest to demonstrate with an example: ```{doctest} >>> from structlog import BoundLoggerBase, PrintLogger, wrap_logger >>> class SemanticLogger(BoundLoggerBase): ... def info(self, event, **kw): ... if not "status" in kw: ... return self._proxy_to_logger("info", event, status="ok", **kw) ... else: ... return self._proxy_to_logger("info", event, **kw) ... ... def user_error(self, event, **kw): ... self.info(event, status="user_error", **kw) >>> log = wrap_logger(PrintLogger(), wrapper_class=SemanticLogger) >>> log = log.bind(user="fprefect") >>> log.user_error("user.forgot_towel") user='fprefect' status='user_error' event='user.forgot_towel' ``` You can observe the following: - The wrapped logger can be found in the instance variable {attr}`structlog.BoundLoggerBase._logger`. - The helper method {meth}`structlog.BoundLoggerBase._proxy_to_logger` that is a [DRY] convenience function that runs the processor chain, handles possible {class}`structlog.DropEvent`s and calls a named function on `_logger`. - You can run the chain by hand through using {meth}`structlog.BoundLoggerBase._process_event` . These two methods and one attribute are all you need to write own *bound loggers*. [dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself ## Passing Context to Worker Threads Thread-local context data based on [context variables](contextvars.md) is -- as the name says -- local to the thread that binds it. When using threads to process work in parallel, you have to pass the thread-local context **into** the worker threads. One way is to retrieve the context vars and pass them along to the worker threads. Then, Inside of the worker, re-bind them using `bind_contextvars`. The following example uses [*pathos*](https://pypi.org/project/pathos/) to create a `ThreadPool`. The context variables are retrieved and passed as the first argument to the partial function. The pool invokes the partial function, once for each element of `workers`. Inside of `do_some_work`, the context vars are bound and a message about the great work being performed is logged -- including the `request_id` key / value pair. ``` from functools import partial import structlog from structlog.contextvars import bind_contextvars from pathos.threading import ThreadPool logger = structlog.get_logger(__name__) def do_some_work(ctx, this_worker): bind_contextvars(**ctx) logger.info("WorkerDidSomeWork", worker=this_worker) def structlog_with_threadpool(f): ctx = structlog.contextvars.get_contextvars() func = partial(f, ctx) workers = ["1", "2", "3"] with ThreadPool() as pool: return list(pool.map(func, workers)) def manager(request_id: str): bind_contextvars(request_id=request_id) logger.info("StartingWorkers") structlog_with_threadpool(do_some_work) ``` See the [issue 425](https://github.com/hynek/structlog/issues/425) for a more complete example. ## Switching Console Output to Standard Error When using structlog without standard library integration and want the log output to go to standard error (*stderr*) instead of standard out (*stdout*), you can switch with a single line of configuration: ```python structlog.configure(logger_factory=structlog.PrintLoggerFactory(sys.stderr)) ``` structlog-24.4.0/docs/standard-library.md0000644000000000000000000005341414645734712015315 0ustar00# Standard Library Logging Ideally, *structlog* should be able to be used as a drop-in replacement for standard library's {mod}`logging` by wrapping it. In other words, you should be able to replace your call to {func}`logging.getLogger` by a call to {func}`structlog.get_logger` and things should keep working as before (if *structlog* is configured right, see {ref}`stdlib-config` below). If you run into incompatibilities, it is a *bug* so please take the time to [report it](https://github.com/hynek/structlog/issues)! If you're a heavy `logging` user, your [help](https://github.com/hynek/structlog/issues?q=is%3Aopen+is%3Aissue+label%3Astdlib) to ensure a better compatibility would be highly appreciated! :::{important} The quickest way to get started with *structlog* and `logging` is {func}`structlog.stdlib.recreate_defaults()`. It will recreate the default configuration on top of `logging` and optionally configure `logging` for you. ::: ## Just Enough `logging` If you want to use *structlog* with `logging`, you should have at least a fleeting understanding on how the standard library operates because *structlog* will *not* do any magic things in the background for you. Most importantly you have to *configure* the `logging` system *additionally* to configuring *structlog*. Usually it is enough to use: ``` import logging import sys logging.basicConfig( format="%(message)s", stream=sys.stdout, level=logging.INFO, ) ``` This will send all log messages with the [log level](https://docs.python.org/3/library/logging.html#logging-levels) `logging.INFO` and above (that means that, for example, `logging.debug` calls are ignored) to standard out without any special formatting by the standard library. If you require more complex behavior, please refer to the standard library's `logging` documentation. ## Concrete Bound Logger *structlog* ships a stdlib-specific [*bound logger*](bound-loggers.md) that mirrors the log methods of standard library's {any}`logging.Logger` with correct type hints. If you want to take advantage of said type hints, you have to either annotate the logger coming from {func}`structlog.get_logger`, or use {func}`structlog.stdlib.get_logger()` that has the appropriate type hints. Please note though, that it will neither configure nor verify your configuration. It will call `structlog.get_logger()` just like if you would've called it -- the only difference are the type hints. See also {doc}`typing`. ### `asyncio` For `asyncio` applications, you may not want your whole application to block while the processor chain is formatting your log entries. For that use case *structlog* comes with a set of non-standard methods that will do all processing in a thread pool executor. They have the same names as the regular methods, except they are prefixed by an `a`. So instead of `logger.info("event!")` you write `await logger.ainfo("event!)`. No extra configuration is necessary and you can mix-and-match both types of methods within the same application. This means an increased computational cost per log entry, but your application will not block because of logging. ```{versionadded} 23.1.0 ``` --- *structlog* also comes with {class}`structlog.stdlib.AsyncBoundLogger` that blankly makes all logging methods asynchronous (in other words, you have to use `await log.info()` instead of just `log.info()`). To use it, {doc}`configure ` *structlog* to use `AsyncBoundLogger` as `wrapper_class`. ```{versionadded} 20.2.0 ``` ```{deprecated} 23.1.0 ``` ## Processors *structlog* comes with a few standard library-specific processors: {func}`~structlog.stdlib.render_to_log_kwargs`: : Renders the event dictionary into keyword arguments for `logging.log` that attaches everything except the `event` field to the *extra* argument. This is useful if you want to render your log entries entirely within `logging`. {func}`~structlog.stdlib.filter_by_level`: : Checks the log entry's log level against the configuration of standard library's logging. Log entries below the threshold get silently dropped. Put it at the beginning of your processing chain to avoid expensive operations from happening in the first place. {func}`~structlog.stdlib.add_logger_name`: : Adds the name of the logger to the event dictionary under the key `logger`. {func}`~structlog.stdlib.ExtraAdder`: : Add extra attributes of `logging.LogRecord` objects to the event dictionary. This processor can be used for adding data passed in the `extra` parameter of the `logging` module's log methods to the event dictionary. {func}`~structlog.stdlib.add_log_level`: : Adds the log level to the event dictionary under the key `level`. {func}`~structlog.stdlib.add_log_level_number`: : Adds the log level number to the event dictionary under the key `level_number`. Log level numbers map to the log level names. The Python stdlib uses them for filtering logic. This adds the same numbers so users can leverage similar filtering. Compare: ``` level in ("warning", "error", "critical") level_number >= 30 ``` The mapping of names to numbers is in `structlog.stdlib._NAME_TO_LEVEL`. {func}`~structlog.stdlib.PositionalArgumentsFormatter`: : This processes and formats positional arguments (if any) passed to log methods in the same way the `logging` module would do, for example, `logger.info("Hello, %s", name)`. *structlog* also comes with {class}`~structlog.stdlib.ProcessorFormatter` which is a `logging.Formatter` that enables you to format non-*structlog* log entries using *structlog* renderers *and* multiplex *structlog*’s output with different renderers (see [below](processor-formatter) for an example). (stdlib-config)= ## Suggested Configurations :::{note} We do appreciate that fully integrating *structlog* with standard library's `logging` is fiddly when done for the first time. This is the price of flexibility and unfortunately -- given the different needs of our users -- we can't make it any simpler without compromising someone's use-cases. However, once it is set up, you can rely on not having to ever touch it again. ::: Depending *where* you'd like to do your formatting, you can take one of four approaches: ### Don't Integrate The most straight-forward option is to configure standard library `logging` close enough to what *structlog* is logging and leaving it at that. Since these are usually log entries from third parties that don't take advantage of *structlog*'s features, this is surprisingly often a perfectly adequate approach. For instance, if you log JSON in production, configure `logging` to use [*python-json-logger*] to make it print JSON too, and then tweak the configuration to match their outputs. You can also use {class}`~structlog.stdlib.ProcessorFormatter` as a formatter for `logging` to get the same output for both *structlog* and `logging` log entries -- see [below](processor-formatter) for an example. :::{note} If you want to use same file (for example, `sys.stdout` or `sys.stderr`) for both *structlog* and `logging.StreamHandler` output, you must use {class}`~structlog.WriteLogger` instead of {class}`~structlog.PrintLogger`. This is because {class}`~structlog.PrintLogger` uses `print(log, file=file, flush=True)` to write log, and `print` writes the `log` message and a newline ("\n") to the stream separately. This can cause interleaving of log entries from *structlog* and `logging` loggers. {class}`~structlog.WriteLogger` writes log entries atomically to the file (for example, `file.write(log+"\n")`). ::: ### Rendering Within *structlog* This is the simplest approach where *structlog* does all the heavy lifting and passes a fully-formatted string to `logging`. Chances are, this is all you need. ```{mermaid} :align: center flowchart TD User structlog stdlib[Standard Library\ne.g. logging.StreamHandler] User --> |"structlog.get_logger().info('foo')"| structlog User --> |"logging.getLogger().info('foo')"| stdlib structlog --> |"logging.getLogger().info(#quot;{'event': 'foo'}#quot;)"| stdlib ==> Output Output ``` A basic configuration to output structured logs in JSON format looks like this: ```python import structlog structlog.configure( processors=[ # If log level is too low, abort pipeline and throw away log entry. structlog.stdlib.filter_by_level, # Add the name of the logger to event dict. structlog.stdlib.add_logger_name, # Add log level to event dict. structlog.stdlib.add_log_level, # Perform %-style formatting. structlog.stdlib.PositionalArgumentsFormatter(), # Add a timestamp in ISO 8601 format. structlog.processors.TimeStamper(fmt="iso"), # If the "stack_info" key in the event dict is true, remove it and # render the current stack trace in the "stack" key. structlog.processors.StackInfoRenderer(), # If the "exc_info" key in the event dict is either true or a # sys.exc_info() tuple, remove "exc_info" and render the exception # with traceback into the "exception" key. structlog.processors.format_exc_info, # If some value is in bytes, decode it to a Unicode str. structlog.processors.UnicodeDecoder(), # Add callsite parameters. structlog.processors.CallsiteParameterAdder( { structlog.processors.CallsiteParameter.FILENAME, structlog.processors.CallsiteParameter.FUNC_NAME, structlog.processors.CallsiteParameter.LINENO, } ), # Render the final event dict as JSON. structlog.processors.JSONRenderer() ], # `wrapper_class` is the bound logger that you get back from # get_logger(). This one imitates the API of `logging.Logger`. wrapper_class=structlog.stdlib.BoundLogger, # `logger_factory` is used to create wrapped loggers that are used for # OUTPUT. This one returns a `logging.Logger`. The final value (a JSON # string) from the final processor (`JSONRenderer`) will be passed to # the method of the same name as that you've called on the bound logger. logger_factory=structlog.stdlib.LoggerFactory(), # Effectively freeze configuration after creating the first bound # logger. cache_logger_on_first_use=True, ) ``` To make your program behave like a proper [*12 Factor App*](https://12factor.net/logs) that outputs only JSON to `stdout`, configure the `logging` module like this: ``` import logging import sys logging.basicConfig( format="%(message)s", stream=sys.stdout, level=logging.INFO, ) ``` In this case *only* your own logs are formatted as JSON: ```pycon >>> structlog.get_logger("test").warning("hello") {"event": "hello", "logger": "test", "level": "warning", "timestamp": "2017-03-06T07:39:09.518720Z"} >>> logging.getLogger("test").warning("hello") hello ``` ### Rendering Using `logging`-based Formatters You can choose to use *structlog* only for building the event dictionary and leave all formatting -- additionally to the output -- to the standard library. ```{mermaid} :align: center flowchart TD User structlog stdlib[Standard Library\ne.g. logging.StreamHandler] User --> |"structlog.get_logger().info('foo', bar=42)"| structlog User --> |"logging.getLogger().info('foo')"| stdlib structlog --> |"logging.getLogger().info('foo', extra={"bar": 42})"| stdlib ==> Output Output ``` ```python import structlog structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), # Transform event dict into `logging.Logger` method arguments. # "event" becomes "msg" and the rest is passed as a dict in # "extra". IMPORTANT: This means that the standard library MUST # render "extra" for the context to appear in log entries! See # warning below. structlog.stdlib.render_to_log_kwargs, ], logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) ``` Now you have the event dict available within each log record. If you want *all* your log entries (meaning: also those not from your application / *structlog*) to be formatted as JSON, you can use the [*python-json-logger*] library: ```python import logging import sys from pythonjsonlogger import jsonlogger handler = logging.StreamHandler(sys.stdout) handler.setFormatter(jsonlogger.JsonFormatter()) root_logger = logging.getLogger() root_logger.addHandler(handler) ``` Now both *structlog* and `logging` will emit JSON logs: ```pycon >>> structlog.get_logger("test").warning("hello") {"message": "hello", "logger": "test", "level": "warning"} >>> logging.getLogger("test").warning("hello") {"message": "hello"} ``` :::{warning} With this approach, it's the standard library `logging` formatter's duty to do something useful with the event dict. In the above example that's `jsonlogger.JsonFormatter`. Keep this in mind if you only get the event name without any context, and exceptions are ostensibly swallowed. ::: (processor-formatter)= ### Rendering Using *structlog*-based Formatters Within `logging` Finally, the most ambitious approach. Here, you use *structlog*'s {class}`~structlog.stdlib.ProcessorFormatter` as a {any}`logging.Formatter` for both `logging` as well as *structlog* log entries. Consequently, the output is the duty of the standard library too. ```{mermaid} :align: center flowchart TD User structlog structlog2[structlog] stdlib["Standard Library"] User --> |"structlog.get_logger().info(#quot;foo#quot;, bar=42)"| structlog User --> |"logging.getLogger().info(#quot;foo#quot;)"| stdlib structlog --> |"logging.getLogger().info(event_dict, {#quot;extra#quot;: {#quot;_logger#quot;: logger, #quot;_name#quot;: name})"| stdlib stdlib --> |"structlog.stdlib.ProcessorFormatter.format(logging.Record)"| structlog2 structlog2 --> |"Returns a string that is passed into logging handlers.\nThis flow is controlled by the logging configuration."| stdlib2 stdlib2[Standard Library\ne.g. logging.StreamHandler] ==> Output ``` {class}`~structlog.stdlib.ProcessorFormatter` has two parts to its API: 1. On the *structlog* side, the {doc}`processor chain ` must be configured to end with {func}`structlog.stdlib.ProcessorFormatter.wrap_for_formatter` as the renderer. It converts the processed event dictionary into something that `ProcessorFormatter` understands. 2. On the `logging` side, you must configure `ProcessorFormatter` as your formatter of choice. `logging` then calls `ProcessorFormatter`'s `format()` method. For that, `ProcessorFormatter` wraps a processor chain that is responsible for rendering your log entries to strings. Thus, the simplest possible configuration looks like the following: ```python import logging import structlog structlog.configure( processors=[ # Prepare event dict for `ProcessorFormatter`. structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=structlog.stdlib.LoggerFactory(), ) formatter = structlog.stdlib.ProcessorFormatter( processors=[structlog.dev.ConsoleRenderer()], ) handler = logging.StreamHandler() # Use OUR `ProcessorFormatter` to format all `logging` entries. handler.setFormatter(formatter) root_logger = logging.getLogger() root_logger.addHandler(handler) root_logger.setLevel(logging.INFO) ``` which will allow both of these to work in other modules: ```pycon >>> import logging >>> import structlog >>> logging.getLogger("stdlog").info("woo") woo _from_structlog=False _record= >>> structlog.get_logger("structlog").info("amazing", events="oh yes") amazing _from_structlog=True _record= events=oh yes ``` Of course, you probably want timestamps and log levels in your output. The `ProcessorFormatter` has a `foreign_pre_chain` argument which is responsible for adding properties to events from the standard library -- in other words, those that do not originate from a *structlog* logger -- and which should in general match the `processors` argument to {func}`structlog.configure` so you get a consistent output. `_from_structlog` and `_record` allow your processors to determine whether the log entry is coming from *structlog*, and to extract information from `logging.LogRecord`s and add them to the event dictionary. However, you probably don't want to have them in your log files, thus we've added the `ProcessorFormatter.remove_processors_meta` processor to do so conveniently. For example, to add timestamps, log levels, and traceback handling to your logs without `_from_structlog` and `_record` noise you should do: ```python timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") shared_processors = [ structlog.stdlib.add_log_level, timestamper, ] structlog.configure( processors=shared_processors + [ structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, ) formatter = structlog.stdlib.ProcessorFormatter( # These run ONLY on `logging` entries that do NOT originate within # structlog. foreign_pre_chain=shared_processors, # These run on ALL entries after the pre_chain is done. processors=[ # Remove _record & _from_structlog. structlog.stdlib.ProcessorFormatter.remove_processors_meta, structlog.dev.ConsoleRenderer(), ], ) ``` which (given the same `logging.*` calls as in the previous example) will result in: ```pycon >>> logging.getLogger("stdlog").info("woo") 2021-11-15 11:41:47 [info ] woo >>> structlog.get_logger("structlog").info("amazing", events="oh yes") 2021-11-15 11:41:47 [info ] amazing events=oh yes ``` This allows you to set up some sophisticated logging configurations. For example, to use the standard library's `logging.config.dictConfig` to log colored logs to the console and plain logs to a file you could do: ```python import logging.config import structlog timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") pre_chain = [ # Add the log level and a timestamp to the event_dict if the log entry # is not from structlog. structlog.stdlib.add_log_level, # Add extra attributes of LogRecord objects to the event dictionary # so that values passed in the extra parameter of log methods pass # through to log output. structlog.stdlib.ExtraAdder(), timestamper, ] def extract_from_record(_, __, event_dict): """ Extract thread and process names and add them to the event dict. """ record = event_dict["_record"] event_dict["thread_name"] = record.threadName event_dict["process_name"] = record.processName return event_dict logging.config.dictConfig( { "version": 1, "disable_existing_loggers": False, "formatters": { "plain": { "()": structlog.stdlib.ProcessorFormatter, "processors": [ structlog.stdlib.ProcessorFormatter.remove_processors_meta, structlog.dev.ConsoleRenderer(colors=False), ], "foreign_pre_chain": pre_chain, }, "colored": { "()": structlog.stdlib.ProcessorFormatter, "processors": [ extract_from_record, structlog.stdlib.ProcessorFormatter.remove_processors_meta, structlog.dev.ConsoleRenderer(colors=True), ], "foreign_pre_chain": pre_chain, }, }, "handlers": { "default": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "colored", }, "file": { "level": "DEBUG", "class": "logging.handlers.WatchedFileHandler", "filename": "test.log", "formatter": "plain", }, }, "loggers": { "": { "handlers": ["default", "file"], "level": "DEBUG", "propagate": True, }, }, } ) structlog.configure( processors=[ structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), timestamper, structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) ``` This defines two formatters: one plain and one colored. Both are run for each log entry. Log entries that do not originate from *structlog*, are additionally pre-processed using a cached `timestamper` and {func}`~structlog.stdlib.add_log_level`. Additionally, for both `logging` and *structlog* -- but only for the colorful logger -- we also extract some data from {class}`logging.LogRecord`: ```pycon >>> logging.getLogger().warning("bar") 2021-11-15 13:26:52 [warning ] bar process_name=MainProcess thread_name=MainThread >>> structlog.get_logger("structlog").warning("foo", x=42) 2021-11-15 13:26:52 [warning ] foo process_name=MainProcess thread_name=MainThread x=42 >>> pathlib.Path("test.log").read_text() 2021-11-15 13:26:52 [warning ] bar 2021-11-15 13:26:52 [warning ] foo x=42 ``` (Sadly, you have to imagine the colors in the first two outputs.) If you leave `foreign_pre_chain` as `None`, formatting will be left to `logging`. Meaning: you can define a `format` for {class}`~structlog.stdlib.ProcessorFormatter` too! [*python-json-logger*]: https://github.com/madzak/python-json-logger structlog-24.4.0/docs/testing.md0000644000000000000000000000450714645734712013527 0ustar00# Testing *structlog* comes with tools for testing the logging behavior of your application. If you need functionality similar to {meth}`unittest.TestCase.assertLogs`, or you want to capture all logs for some other reason, you can use the {func}`structlog.testing.capture_logs` context manager: ```{doctest} >>> from structlog import get_logger >>> from structlog.testing import capture_logs >>> with capture_logs() as cap_logs: ... get_logger().bind(x="y").info("hello") >>> cap_logs [{'x': 'y', 'event': 'hello', 'log_level': 'info'}] ``` Note that inside the context manager all configured processors are disabled. :::{note} `capture_logs()` relies on changing the configuration. If you have *cache_logger_on_first_use* enabled for {doc}`performance `, any cached loggers will not be affected, so it’s recommended you do not enable it during tests. ::: You can build your own helpers using {class}`structlog.testing.LogCapture`. For example a [*pytest*](https://docs.pytest.org/) fixture to capture log output could look like this: ``` @pytest.fixture(name="log_output") def fixture_log_output(): return LogCapture() @pytest.fixture(autouse=True) def fixture_configure_structlog(log_output): structlog.configure( processors=[log_output] ) def test_my_stuff(log_output): do_something() assert log_output.entries == [...] ``` --- You can also use {class}`structlog.testing.CapturingLogger` (directly, or via {class}`~structlog.testing.CapturingLoggerFactory` that always returns the same logger) that is more low-level and great for unit tests: ```{doctest} >>> import structlog >>> cf = structlog.testing.CapturingLoggerFactory() >>> structlog.configure(logger_factory=cf, processors=[structlog.processors.JSONRenderer()]) >>> log = get_logger() >>> log.info("test!") >>> cf.logger.calls [CapturedCall(method_name='info', args=('{"event": "test!"}',), kwargs={})] ``` ```{testcleanup} import structlog structlog.reset_defaults() ``` --- Additionally *structlog* also ships with a logger that just returns whatever it gets passed into it: {class}`structlog.testing.ReturnLogger`. ```{doctest} >>> from structlog import ReturnLogger >>> ReturnLogger().info(42) == 42 True >>> obj = ["hi"] >>> ReturnLogger().info(obj) is obj True >>> ReturnLogger().info("hello", when="again") (('hello',), {'when': 'again'}) ``` structlog-24.4.0/docs/thread-local.md0000644000000000000000000001517614645734712014415 0ustar00# Legacy Thread-local Context :::{attention} The `structlog.threadlocal` module is deprecated as of *structlog* 22.1.0 in favor of {doc}`contextvars`. The standard library {mod}`contextvars` module provides a more feature-rich superset of the thread-local APIs and works with thread-local data, async code, and greenlets. Therefore, as of 22.1.0, the `structlog.threadlocal` module is frozen and will be removed after May 2023. ::: ```{testsetup} * import structlog structlog.configure( processors=[structlog.processors.KeyValueRenderer()], ) ``` ```{testcleanup} * import structlog structlog.reset_defaults() ``` ## The `merge_threadlocal` Processor *structlog* provides a simple set of functions that allow explicitly binding certain fields to a global (thread-local) context and merge them later using a processor into the event dict. The general flow of using these functions is: - Use {func}`structlog.configure` with {func}`structlog.threadlocal.merge_threadlocal` as your first processor. - Call {func}`structlog.threadlocal.clear_threadlocal` at the beginning of your request handler (or whenever you want to reset the thread-local context). - Call {func}`structlog.threadlocal.bind_threadlocal` as an alternative to your bound logger's `bind()` when you want to bind a particular variable to the thread-local context. - Use *structlog* as normal. Loggers act as they always do, but the {func}`structlog.threadlocal.merge_threadlocal` processor ensures that any thread-local binds get included in all of your log messages. - If you want to access the thread-local storage, you use {func}`structlog.threadlocal.get_threadlocal` and {func}`structlog.threadlocal.get_merged_threadlocal`. These functions map 1:1 to the {doc}`contextvars` APIs, so please use those instead: - {func}`structlog.contextvars.merge_contextvars` - {func}`structlog.contextvars.clear_contextvars` - {func}`structlog.contextvars.bind_contextvars` - {func}`structlog.contextvars.get_contextvars` - {func}`structlog.contextvars.get_merged_contextvars` ## Thread-local Contexts *structlog* also provides thread-local context storage in a form that you may already know from [*Flask*](https://flask.palletsprojects.com/en/latest/design/#thread-locals) and that makes the *entire context* global to your thread or greenlet. This makes its behavior more difficult to reason about which is why we generally recommend to use the {func}`~structlog.contextvars.merge_contextvars` route. Therefore, there are currently no plans to re-implement this behavior on top of context variables. ### Wrapped Dicts In order to make your context thread-local, *structlog* ships with a function that can wrap any dict-like class to make it usable for thread-local storage: {func}`structlog.threadlocal.wrap_dict`. Within one thread, every instance of the returned class will have a *common* instance of the wrapped dict-like class: ```{doctest} >>> from structlog.threadlocal import wrap_dict >>> WrappedDictClass = wrap_dict(dict) >>> d1 = WrappedDictClass({"a": 1}) >>> d2 = WrappedDictClass({"b": 2}) >>> d3 = WrappedDictClass() >>> d3["c"] = 3 >>> d1 is d3 False >>> d1 == d2 == d3 == WrappedDictClass() True >>> d3 # doctest: +ELLIPSIS ``` To enable thread-local context use the generated class as the context class: ```python configure(context_class=WrappedDictClass) ``` :::{note} Creation of a new `BoundLogger` initializes the logger's context as `context_class(initial_values)`, and then adds any values passed via `.bind()`. As all instances of a wrapped dict-like class share the same data, in the case above, the new logger's context will contain all previously bound values in addition to the new ones. ::: `structlog.threadlocal.wrap_dict` returns always a completely *new* wrapped class: ```{doctest} >>> from structlog.threadlocal import wrap_dict >>> WrappedDictClass = wrap_dict(dict) >>> AnotherWrappedDictClass = wrap_dict(dict) >>> WrappedDictClass() != AnotherWrappedDictClass() True >>> WrappedDictClass.__name__ # doctest: +SKIP WrappedDict-41e8382d-bee5-430e-ad7d-133c844695cc >>> AnotherWrappedDictClass.__name__ # doctest: +SKIP WrappedDict-e0fc330e-e5eb-42ee-bcec-ffd7bd09ad09 ``` In order to be able to bind values temporarily to a logger, `structlog.threadlocal` comes with a [context manager](https://docs.python.org/2/library/stdtypes.html#context-manager-types): {func}`structlog.threadlocal.tmp_bind`: ```{testsetup} ctx from structlog import PrintLogger, wrap_logger from structlog.threadlocal import tmp_bind, wrap_dict WrappedDictClass = wrap_dict(dict) log = wrap_logger(PrintLogger(), context_class=WrappedDictClass) ``` ```{doctest} ctx >>> log.bind(x=42) # doctest: +ELLIPSIS , ...)> >>> log.msg("event!") x=42 event='event!' >>> with tmp_bind(log, x=23, y="foo") as tmp_log: ... tmp_log.msg("another event!") x=23 y='foo' event='another event!' >>> log.msg("one last event!") x=42 event='one last event!' ``` The state before the `with` statement is saved and restored once it's left. If you want to detach a logger from thread-local data, there's {func}`structlog.threadlocal.as_immutable`. #### Downsides & Caveats The convenience of having a thread-local context comes at a price though: :::{warning} - If you can't rule out that your application re-uses threads, you *must* remember to **initialize your thread-local context** at the start of each request using {func}`~structlog.BoundLogger.new` (instead of {func}`~structlog.BoundLogger.bind`). Otherwise you may start a new request with the context still filled with data from the request before. - **Don't** stop assigning the results of your `bind()`s and `new()`s! **Do**: ``` log = log.new(y=23) log = log.bind(x=42) ``` **Don't**: ``` log.new(y=23) log.bind(x=42) ``` Although the state is saved in a global data structure, you still need the global wrapped logger produce a real bound logger. Otherwise each log call will result in an instantiation of a temporary BoundLogger. See `configuration` for more details. - It [doesn't play well](https://github.com/hynek/structlog/issues/296) with `os.fork` and thus `multiprocessing` (unless configured to use the `spawn` start method). ::: ## API ```{eval-rst} .. module:: structlog.threadlocal .. autofunction:: bind_threadlocal .. autofunction:: unbind_threadlocal .. autofunction:: bound_threadlocal .. autofunction:: get_threadlocal .. autofunction:: get_merged_threadlocal .. autofunction:: merge_threadlocal .. autofunction:: clear_threadlocal .. autofunction:: wrap_dict .. autofunction:: tmp_bind(logger, **tmp_values) .. autofunction:: as_immutable ``` structlog-24.4.0/docs/twisted.md0000644000000000000000000001032214645734712013525 0ustar00# Twisted :::{warning} Since `sys.exc_clear` has been dropped in Python 3, there is currently no way to avoid multiple tracebacks in your log files if using *structlog* together with Twisted on Python 3. ::: :::{note} *structlog* currently only supports the legacy -- but still perfectly working -- Twisted logging system found in `twisted.python.log`. ::: ## Concrete Bound Logger To make *structlog*'s behavior less magical, it ships with a Twisted-specific wrapper class that has an explicit API instead of improvising: `structlog.twisted.BoundLogger`. It behaves exactly like the generic `structlog.BoundLogger` except: - it's slightly faster due to less overhead, - has an explicit API ({func}`~structlog.twisted.BoundLogger.msg` and {func}`~structlog.twisted.BoundLogger.err`), - hence causing less cryptic error messages if you get method names wrong. In order to avoid that *structlog* disturbs your CamelCase harmony, it comes with an alias for `structlog.get_logger` called `structlog.getLogger`. ## Processors *structlog* comes with two Twisted-specific processors: {func}`structlog.twisted.EventAdapter` : This is useful if you have an existing Twisted application and just want to wrap your loggers for now. It takes care of transforming your event dictionary into something [twisted.python.log.err](https://docs.twisted.org/en/stable/api/twisted.python.log.html#err) can digest. For example: ```python def onError(fail): failure = fail.trap(MoonExploded) log.err(failure, _why="event-that-happened") ``` will still work as expected. Needs to be put at the end of the processing chain. It formats the event using a renderer that needs to be passed into the constructor: ```python configure(processors=[EventAdapter(KeyValueRenderer()]) ``` The drawback of this approach is that Twisted will format your exceptions as multi-line log entries which is painful to parse. Therefore *structlog* comes with: {func}`structlog.twisted.JSONRenderer` : Goes a step further and circumvents Twisted logger's Exception / Failure handling and renders it itself as JSON strings. That gives you regular and simple-to-parse single-line JSON log entries no matter what happens. ## Bending Foreign Logging To Your Will *structlog* comes with a wrapper for Twisted's log observers to ensure the rest of your logs are in JSON too: `structlog.twisted.JSONLogObserverWrapper`. What it does is determining whether a log entry has been formatted by `structlog.twisted.JSONRenderer` and if not, converts the log entry to JSON with `event` being the log message and putting Twisted's `system` into a second key. So for example: ``` 2013-09-15 22:02:18+0200 [-] Log opened. ``` becomes: ``` 2013-09-15 22:02:18+0200 [-] {"event": "Log opened.", "system": "-"} ``` There is obviously some redundancy here. Also, I'm presuming that if you write out JSON logs, you're going to let something else parse them which makes the human-readable date entries more trouble than they're worth. To get a clean log without timestamps and additional system fields (`[-]`), *structlog* comes with `structlog.twisted.PlainFileLogObserver` that writes only the plain message to a file and `structlog.twisted.plainJSONStdOutLogger` that composes it with the aforementioned `structlog.twisted.JSONLogObserverWrapper` and gives you a pure JSON log without any timestamps or other noise straight to [standard out]: ```console $ twistd -n --logger structlog.twisted.plainJSONStdOutLogger web {"event": "Log opened.", "system": "-"} {"event": "twistd 13.1.0 (python 2.7.3) starting up.", "system": "-"} {"event": "reactor class: twisted...EPollReactor.", "system": "-"} {"event": "Site starting on 8080", "system": "-"} {"event": "Starting factory ", ...} ... ``` ## Suggested Configuration ```python import structlog structlog.configure( processors=[ structlog.processors.StackInfoRenderer(), structlog.twisted.JSONRenderer() ], context_class=dict, logger_factory=structlog.twisted.LoggerFactory(), wrapper_class=structlog.twisted.BoundLogger, cache_logger_on_first_use=True, ) ``` See also {doc}`logging-best-practices`. [standard out]: https://en.wikipedia.org/wiki/Standard_out#Standard_output_.28stdout.29 structlog-24.4.0/docs/typing.md0000644000000000000000000000355514645734712013366 0ustar00# Type Hints Static type hints -- together with a type checker like [Mypy](https://mypy.readthedocs.io/en/stable/) -- are an excellent way to make your code more robust, self-documenting, and maintainable in the long run. And as of 20.2.0, *structlog* comes with type hints for all of its APIs. Since *structlog* is highly configurable and tries to give a clean façade to its users, adding types without breaking compatibility -- while remaining useful! -- was a formidable task. --- The main problem is that `structlog.get_logger()` returns whatever you've configured the *bound logger* to be. The only commonality are the binding methods like `bind()` and we've extracted them into the {class}`structlog.typing.BindableLogger` {class}`~typing.Protocol`. But using that as a return type is worse than useless, because you'd have to use {func}`typing.cast` on every logger returned by `structlog.get_logger()`, if you wanted to actually call any logging methods. The second problem is that said `bind()` and its cousins are inherited from a common base class (a [big](https://www.youtube.com/watch?v=3MNVP9-hglc) [mistake](https://python-patterns.guide/gang-of-four/composition-over-inheritance/) in hindsight) and can't know what concrete class subclasses them and therefore what type they are returning. The chosen solution is adding {func}`structlog.stdlib.get_logger()` that just calls `structlog.get_logger()` but has the correct type hints and adding `structlog.stdlib.BoundLogger.bind` et al that also only delegate to the base class. `structlog.get_logger()` is typed as returning {any}`typing.Any` so you can use your own type annotation and stick to the old APIs, if that's what you prefer: ``` import structlog logger: structlog.stdlib.BoundLogger = structlog.get_logger() logger.info("hi") # <- ok logger.msg("hi") # <- Mypy: 'error: "BoundLogger" has no attribute "msg"' ``` structlog-24.4.0/docs/why.md0000644000000000000000000001077314645734712012663 0ustar00# Why … ## … Structured Logging? > I believe the widespread use of format strings in logging is based on two presumptions: > > - The first level consumer of a log message is a human. > - The programmer knows what information is needed to debug an issue. > > I believe these presumptions are **no longer correct** in server side software. > > —[Paul Querna](https://paul.querna.org/articles/2011/12/26/log-for-machines-in-json/) Structured logging means that you don't write hard-to-parse and hard-to-keep-consistent prose in your log entries. Instead, you log *events* that happen in a *context* of key-value pairs. :::{tip} More general advice about production-grade logging can be found in the later chapter on {doc}`logging-best-practices`. ::: ## … structlog? ### Easier Logging You can stop writing prose and start thinking in terms of an event that happens in the context of key-value pairs: ```pycon >>> from structlog import get_logger >>> log = get_logger() >>> log.info("key_value_logging", out_of_the_box=True, effort=0) 2020-11-18 09:17:09 [info ] key_value_logging effort=0 out_of_the_box=True ``` Each log entry is a meaningful dictionary instead of an opaque string now! That said, *structlog* is not taking anything away from you. You can still use string interpolation using positional arguments: ```pycon >>> log.info("Hello, %s!", "world") 2022-10-10 07:19:25 [info ] Hello, world! ``` ### Data Binding Since log entries are dictionaries, you can start binding and re-binding key-value pairs to your loggers to ensure they are present in every following logging call: ```pycon >>> log = log.bind(user="anonymous", some_key=23) >>> log = log.bind(user="hynek", another_key=42) >>> log.info("user.logged_in", happy=True) 2020-11-18 09:18:28 [info ] user.logged_in another_key=42 happy=True some_key=23 user=hynek ``` You can also bind key-value pairs to {doc}`context variables ` that look global, but are local to your thread or *asyncio* context -- which usually means your web request. ### Powerful Pipelines Each log entry goes through a [processor pipeline](processors.md) that is just a chain of functions that receive a dictionary and return a new dictionary that gets fed into the next function. That allows for simple but powerful data manipulation: ```python def timestamper(logger, log_method, event_dict): """Add a timestamp to each log entry.""" event_dict["timestamp"] = time.time() return event_dict ``` There are [plenty of processors](structlog.processors) for most common tasks coming with *structlog*: - Collectors of [call stack information](structlog.processors.StackInfoRenderer) ("How did this log entry happen?"), - …and [exceptions](structlog.processors.format_exc_info) ("What happened‽"). - Flexible [timestamping](structlog.processors.TimeStamper). ### Formatting *structlog* is completely flexible about *how* the resulting log entry is emitted. Since each log entry is a dictionary, it can be formatted to **any** format: - A colorful key-value format for [local development](console-output.md), - [JSON](structlog.processors.JSONRenderer) of [*logfmt*](structlog.processors.LogfmtRenderer) for easy parsing, - or some standard format you have parsers for like *nginx* or Apache *httpd*. Internally, formatters are processors whose return value (usually a string) is passed into loggers that are responsible for the output of your message. *structlog* comes with multiple useful formatters out-of-the-box. ### Output *structlog* is also flexible with the final output of your log entries: - A **built-in** lightweight printer like in the examples above. Easy to use and fast. - Use the [**standard library**](standard-library.md)'s or [**Twisted**](twisted.md)'s logging modules for compatibility. In this case *structlog* works like a wrapper that formats a string and passes them off into existing systems that won't know that *structlog* even exists. Or the other way round: *structlog* comes with a `logging` formatter that allows for processing third party log records. - Don't format it to a string at all! *structlog* passes you a dictionary and you can do with it whatever you want. Reported use cases are sending them out via network or saving them to a database. ### Highly Testable *structlog* is thoroughly tested and we see it as our duty to help you to achieve the same in *your* applications. That's why it ships with a [test helpers](testing.md) to introspect your application's logging behavior with little-to-no boilerplate. structlog-24.4.0/docs/_static/BoundLogger.svg0000644000000000000000000015476014645734712016115 0ustar00structlog-24.4.0/docs/_static/Justfile0000644000000000000000000000054314645734712014662 0ustar00rebuild-logos: && compress-logos magick structlog_logo.svg structlog_logo.png magick structlog_logo.svg -resize 220 structlog_logo_small.png magick structlog_logo.svg -resize 16x16 docset-icon.png magick structlog_logo.svg -resize 32x32 docset-icon@2x.png compress-logos: svgo *.svg oxipng --opt max --strip safe --zopfli *.png structlog-24.4.0/docs/_static/console_renderer.png0000644000000000000000000075742414645734712017231 0ustar00PNG  IHDR 6Eqn iCCPiccxڬhmٶhTٶm۶m۶} Պ`4.,KkͶ [CVc=V`:ikD"g'Wz^kkDt@3lǃe/Fzvŏ9ߪ8=*+f*v:ߛJizV2%}bl ؾSzjZ:R}ֵ Z?KU,tc2Ns |-pIRΚnN*nvʦV:ەSƖגP܆SuN]rw>3VK Z'U9uA%RSm81\tA9m jLYRMݦ]ٴœdo{Hoj='{rNk(ǣ,eQOԕ~'xjl` k)S& 1mL< <ZwUcv>n3X`yVaMcc`$ 氐jh۲8{srs gr.q9p#q7(O<)_Ȋ+˖2Z& 'KA%&.tn*)r\,Wr<,O|Al~{m۶m۶ٶm۶mIc'  !d>~?߅E6QJ01U{ELAdeBbSO/UVU N UZuDVt^]UCt^ͤ3ML2~s|֦El=N+!{~w]&Wֵp,]to)}QS&ο2 C0( ;µ%jيmvͶm۶m۶m۶J@`48<s*Xl GPvHAAbkp%< ^# Aj"ݑY>U2hktƚ`#ENï? (A"&ۈ;dEV!s#k T#j$Ig]tU>MaXӞb^(k-^qׂ^oa@ .K@qxG'%d^ roy|Sɫ$4MZ0u\õ2ZOmvQa::8:.;s9V9lk;;nIzzzy xmoW*+{u@ ,rf. K?Dܑ&Q(Z!:,z0'uk 8Q91:q<3Y4/;#KuNmLK{ӭ+3ZIfq. }7`60-jl-N?RjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparacf fJKc`wd`!j`B3u@*00lb~P(ā^6~+#d@<4Tti y{դԾIDATx3][W^mǶm۶ض&M6UX"nyk 46 1Ht Ht@ '䂂>5Bx|O?"Ig,inΞ%YC_m~lMU,JͲ+=*Sd,ERx_&,9x ٹ&P$u*_}Dאi3 tmW\T_K݌caqm:;iD0a)^]mᅭ;F}UXrV}!$$=W~a#79룉f߹w(=qa$:-ë{V]Svpϕ$:[7-6BHtƴ $g4}O9DGBVfZ[_.1/UJo_7$:N[9ݫ􂽳"g"HcL&0=2.θ^ad:ouw;-VٻNۇ]ez[?ss߽{kיW^{srUk].Nzsu,^dޢef32=|]{|ͷ_q&~ү~ \+Ҳm'8w߸q3Y$Sт#8=[bL4+1;96:d&Szdw<*:dƌؽ}zMs٫JHZ?A#UfA$B!B!BB⳿ot5 ǟLfgE]poeNICiZTGBJXGEשG?4Ξ;'`~ٱ{P?;PZT8y8ݾ} VRBD7i|s=+3CW[f@P G2dQI5`.ec6L\;[2():pio!̢`G%mY ~aek[A=cAg OT2 >ŵj-bB!B!B V0([FxDHK.&COht12uca= ֭Rk.ޛ 6Z!<։m._S=նp[H)aRQQ& w`ִQ!β MBK!zhO ū>!-1u'Q:K(uUoe~yZ6@ɝZBxP*Y 5ѩ NщmT%(;eB$$$MUԮ֬qJe!B!B!BEW~Am}ix=0…H*B"1M 917ě;cDzI|$zh:l -[D SDɠd*8n;R*9~Oԓz_ܒqjcd1}K8dBKBt.0o*f}j(A2ѫhwU)tC۱$xQ !B!B!*^9?3-,q-z5}'ңXz٦mX,PE3퍷ޑ/ZNT'9Sa٪ ;3+[I%bG) bP릿4RMQ]G[N_\'%ј{sqioQTQsNQܑ* Y0e1B!B!B e|>ǠhE֎2Qsy'|O:3[5hf:۰ϴHO?q>llϐ@IA՘O?d{_Nd:eaX@|o*-M 5Bd +у˩(J(,MlF48NNPw4~x] !B!B!*V ~nïK%@4_$ -_~ML^px\A+:gdipHoO{v5lF+0|.gwWMVCF-ޮepS2A7B:!$ A2-[.XP0 X)հ.QBa-(&aO5uEQUtу}$\G3)BPDϫU(\EY'B!B!PFl`Ptz3\~=x[~~c]+:G=ook74wJa0X҆yi-!βyQ21c EZ3?gBշ괐7,^JSnQb;(=HMPҧL@xzҖH!B!B!$PѕOK~V`]=eg3G%xteZLPWz,^N}ET99tg(la-4A4 )=Ptū0(Rj(Q.#yߝӤZKoaxG2:ߢ+ȞLҢtQi>h(A6~$$B!B!`蚤63Ogc:.{&*:0j"*dJ!Tt6~;v{Pr' UtS p%-=' l.Pi.BV? o\H6QRhq)ۡatR+Xx V 2˱XY bKCe$B!B!BH@ytAu Qt}Rܶs,:x]IC?+YpiTt߾*C'3N-Π"q_%.k[ˊl]>1c2i||%7*2\{ԯBֱ&E'rZ oR$-iB!B!B ]|r9gDbO0E7cB9{_㧴n+ W|dɧ--\zUB^zm翐QYc*BrgZ7oJf۰GAW_O9O.cI3W^>3 _g=蹵\) 㾼 kHy硴|WtZghrNiYf ^9 (Sׁ3(Z)b(S(Xŷ(p̩wEgR$-q,t`f wJ!B!B!BEE|H@*۷ws44~b\sSfJ9{ 6:<֩B%Shpvާ{PvSҥ˒sut,l sMɨ>rJK Bsh̄T;zr[hR> ٽ8ԣ@9pI[b:ߢ+ݧN@Í`fSi[솨JC|OH* B!B!B ]& ~nKStIJU0mٜӳ“94s֝){%*9_{n\luEgݻ3.JTVUn$Ap6EI`J{Xͷ&ikz(E3H;j* %VQ2nX*@ϫ/Ǘ)hoQo51UrBT0 Q?'wZJ!B!B!BE7(Aэ<VtRfkbfZ} /SAݺu NEƹϟ}Ϟ; m:(.zHw%YC VdZY* BO~\=+J67 -_QE(yeΑ>hdu,EWtj=9$?@ۗ< KK>$B!B!B!VtcР誷`E'L>f7sЬ3k+: A7NR]L#'XrZ:-!޺YA(:`7a~8C]](e[ֱE^ѡ8K׉,CjEk5kc'!B!B!B!$A?s#lVtX~\z-_ǝg%qN?\|wqolߵ*իwظe+NEPUz ;m Hah4PZoEf䠨2ֱE^EF#D؟_kZ4]B!B!BFEwp,BԽ[avMy|Ҟ]Sz{IDWҖ`fMc@NV3:(vdUPI. ]Z M(Ht۴=vfn}“@ ,`\x$Ht<}"ݻL 9 $:.GRw5ݨe{$y@Zv DWn;Q_OEXj7n 3#[<0sb$:$:$:R!@$:$:JW{ڴ4aa'ϊ3s(~jBu?WׁOD@z-{,d+P4%{=%S7-_J/4_,y-W=V΂!U`UeY(U?gQgd,Z*S򁌅K%T?ԭ@B'aD͈W|9{gŲ_!:I- ı1 OǑsߵn {r;C]P{OUuMgR{V(W{gL!ƯAFu<(,hi"r}–H-#Y\EUqT󁋋#:.k;|')! ,'Ny9∎#:k'Qɇ[4=#c^1 `_bу]X~qͷG RJsVF.8㈎k*dGt/26[&$?;xTh@V"`˸y&/75cm826DSqVŝ=ֈ&tn`{q_c138eoPpyu3C %@`$𕠰_=u7X epR,߲ܝ 0@Gs;A$[pDgl∎∎#:踸8bqD5j΂!;#h=Gts-936qDgkBROZӅu#:.'sdGt M\n6C̜0 UQM]]vu刎GtV|13Ĩ 6HݛQ(fUy *V>6Cp!R,:.^mqd:H lD'}<.d d@ Kwb&?4'Ȅ 6ps:MC L@:VZS0 "; %J|N,Z$ 2xJ~_pD8∎#:...8∎#,o8rwnOD` ~ق`F`S82\]0[Nh䈎Gt*DQJ8Յ"jZ;H1jB:s+`ߠw)&Z8}$râ>BW[0@hKid= K3 Dio`j `Iг ef v$(R,تh8JRVhO*0bBPHZGkmkez^`1.0?^Iϓ_ZFhѴ-[Ŋ̅>X0xf>,ZnEݝzшW1گX]YR>&,Edox1t;_O~|zKb5//~zɯ9|!1Xrf̳t}too=ꭇ-]#h4t oc}/̃=HHȐ]?~rKFYHqmݻ}Y`C~ÿ>'Ѱ H0e.4"jXRxX[2+U~Eͧ¯v?oLt}!6ś?K|0~qqqD'2={:joUv|8c+>ю4sMϓ†V0a=i.t^-7>pޯQm$zkɟ W&mۜחCe7 /L38%&g`˃ڞ0L۲'^A*I_I[WYTw})1߃zEhfKp_-#51"EɈny.r'\/9^4l V>jՂ:5GO'Hit:=Ep}g\}o.`Y.љ8Þ(Kjn&tL Dgmlu&X!V tO/>@a䀦1ы#: &ŸH,:?.\c`$.9?r[ƩAtS+,|ZtA9qNB`Iߓ ]Rbo%ؔԴgO Z6zDG 3ОTab儐i+8-tRDa%XzٻaRDgbPr%G ^e?6ml|čmj׼_Dǟ|,'r2B.^8䍇7϶= DG`ȥĜUV\1"}b}^z] '@,^Dnm/{ C" gtzDY9 钸ǥ{^y|DSR+6p/Xs+Ƴψ%PZ)@NOW񋋋#:3yg<_j8*]U"HoOs,K4З. -`3P!UδjcB$Fݑص{:A[=~/K8L\='/.Sw3mTRcpݜ+u|*J&fl+s~yf*3J$.^NiT-°\ް7/Gt\\Ť抡 .tA:NTZ&퉓w :ωP?7fGel`D7tZTr#hI:crʧi 4c;i,nOb"$?X 0,Bu@Tt᪸|IF ( 1jSoN|R@t3G BPlDG@fdnIǟї~!F 7!1]{!G6#bB8dGt&[MI ?C&A ~1I_"s/LO]xmC#-B`gKžl#376|0~qqqDVrY>'(榶7"6GVj#HǧϷ"lԞg[DD'a%c'0:$I ˽B9ђ]*i.β8#9^=f?RpnwOqFGK?Rӵ/aLq qDZsL&Fm˅; NϜojlӞ#:.. 0MψpOHX OFa`#} l>U|D0uYJ }^t"3crrOj(:/R\ߓbkzaMB>@_r-gAFۧѹ?M 'x5gοoDKt!OhB;ri"泈.)xI;蔏蒲tRDކ;؈N󁋋Kjk9+lڽKh}~ȑ8yx?,ו0]Ӊ_qB;rf4U $|5,-dDIvd ⦆]i{^!a!:k '$rlPzlWi4naL['w#IH/TaI+@$/":bM(ݴsyvaίڙ靓#:..PK8F,q@ ZD'Rm©=I8ѱAct䙫byG(מ;$8  +`ky}>toPpKYX; xkfd㝒QyGN+h.?P`= \m}kFa66pqKCzmUc=h؈Qdkr\*`yV*]DvA)!m^L'(9qD \-"*<ϑG J%߶ߊ_;c؈oߏ {-Qkd !2"EGh (s̞tF聰Z4#:~CRb{ʺ}ڠsXu.뀐5乗&]bk_mԠv{U!UciƛDfKl ϟqBjMG<`޻AhTYӉPLg65%kf3ICm2m1[B27桢}褯$QRQoЋٜF`A{Aq K/ OGw5A(D=LD&GN%U`(DœxJN)fc? XsUE_"3q#$*a.Gvf+?|cVژGt,~wlB2EBceqҸ7И}R"JP cuP~w[!""XN&]I{Ԑ"`~ (fuQx ?y>]2tpߓ8ci7Rbf{*v1]@bY[ۃ3ͷڢA)!|D*ꂖ(F蔏Y:ܟ1@fe # to&,hS@tP;7 dDtcߠ=l׾ 4%CN?TcȽ wSu.H/ ԶYNT4!z%U6"VupClDGY |2ZDL"fkT-3D"Ij)]EqG0u&Z8!5"i߲JhMED>4[ڝ|L9NJ (A{p_ &ŃFtDQs+:!'rc?(Йy~>Ɗ,2ccl+sNy$_Z۪2g{Xt ?0iR ۣ 퀻IeF8YBޝ dhD(F"&(?*=B7DɈ(=@1 Gt-^PE d磏"5\tׁ Ct"ʈ(=@1bhJZNݥuPC&m-J1"h­ p,":νؽ>["((,"1ea"˯ĂeĈL4A2Y|&*l]3L?žBMmDQ0<$uO3qg30褯 '%ZСևڀM2ʯ+)c%$":=a1J!#:|՛=V[lA=a"g̷FN[gͽ8!..薭ۈ@(H1cMm[i$i{.FddO1bS/ĵ AKr!: }򏡺`U>Aڕ ؔidߺ2Ip#īۇa#E̵P,p-m؄eY]@8NL*&㤁|sE"J³=&YjޭCté ߰Q.l*BmzdKOD7...cEt2G"@#/`F=1KU[h錸 L$57IBcZeЅ:<21k]!NJBq#TڀMKqZ":kD!ǣH=.w\A#::X5B[fڻpDgnق`Λ8Of|YmoܑF...DξW cϧZ:I_p8ⴆ]NKyǠPjXD'}Bq$$%dID{rUNcxd?ۼ3Z a)t1N9/(ScBbXوNTaWљɠ_3lt"?yRG,Bt 118CX }b}ӧNtu)XvŒ0m8 َt{ ƝCgٖH'XyǠpOj9{Qb-:렀@"zFΟgV?}M~kKCDOݣ%Eѝ{-UbrKДAtfPDG/;cn>Z`4D$F{-lF(z!h]?...FtYL`iq %s--E+h+#e!,RVnGԣh!}UڀMCӧ (#YՌ Fm]TA"@dGtz'M0t±43w~Ymqq0 շ7>9l\2,4݂Y}AOնu }Õ_t`St׷Kwh=I{cx8X$FS,x`!`u$ch DX|BKJ! {R? Wl g|9Ftb90g =rb5ܵvX'CH}yX1)8JmشS̍;X;|~EY?z^DGb#8>~JD'aJGtm28|v˃OAm=JCtsh:ʊ>pqq#cE|HlbMٓzhFtM?R'eG?AtWB ݾ7S4rz6`A02DgY,҉'UO#:63߷ҙN1:޶ Eh7&5:fr/Gt\\/.}jr]|裞 $:|VZ ѱ19r0*M:MHT(_Q$].ÀB官T͂Nt>>4pNOn'u:ܛ>gN <1Dޕ@fZ?7S9 S-_;pيqIGcIWL>Kl }DtPaPѥpD]PX$^(*%Wλ_7u . 4.2+aP]&im%]e.1Wzُ ]ZhJ!:P>#if[C^@iQ;2uGˊ>pqq=cQ|,6ht%.0A;؈y><+p״CtWRPM~ti6 #CtHh) -1 m/2#~Ap꫒!;%$NLsL O1965Q0A;8zA$9>TՌxPµ;Sl$/anq oxLD7@4c@%r #;4ܞT.{G/LQP&XJf[b a = ;L\uzT,dGt=U4p׽U8;#q(vBٱM|kTAi Ds6f( GUtLv%,Ats`BKj{@#8s_K#N3k%H3P*+;k4SN !G&fY[:>XL'HG>AS<(I~!No}]I];{tD'󁋋"@eUv|*嘌`}tWQۤ !wb/;bY:b#:]rڏ ] -YBtVRܢ:򳡑&hjZQ =4ɦ@ӍloKa^RpƏv#Ӭ$ Mqq`U39(,qz@,V1$$A$/38%":ïԏkqප HOIKwR"޸y4yS!f4D_N{@G+'ߺ]VD'󁋋HL9DE}2:dUgmcn[afK!NKwWu}m؈uݜU]3/2qmyB*ɻ;m`RJٻI/Y \FM 8]v 6: LIq#:[!~^&έ˝Gc nU; /Wd߁thvܗu^mbtUOhሎEGtC2'] 툤_qsgH4c@*\!᠟{KK 4": 렄(VPV'&}OVv DتTpKYKzOq6D8D'}O*0bs u#-bJ#jklB; lh/tvE{vw3!-Y ёz f $+qDY bna7u j}q;fTGkK ^Mp-WT~CtC}hJl6t,$,K:_i`?á?LQ![vk# Gt?-I>0 ε_k;i&1HGt^BYewb3}eV.'&,ll6`A02DgUN`ifNXj{]Ft G~^Sh2U'ίvMZ81d"#LPLnz``wtI!`|,2|# rrn+^DgjBíPDtL@}FP&$5(G8XFuNiSGt*p]eW :"8-$"b>4p,}O2.Gx00TA2CD-9fa^6p21 v-G9m]gPDgZ K'r8ݬ̉z݅7^: B>rP}#{/s6g* S,#B͹nk].gK|A~GtU̾ѓw W^)BݾLb3:e\X@k pKY@~1YTגQ!hD)m6@t72qوn:[ۉj;2R]Ur\K- YޝT}6`&Z| znS8㈎!~n<z9!M 3LqAaNP 1RbWqa :"͝HWazُcw9hJ : PlPԐ} K:K TU$:[xTN#:hjiA6G!uu IcˑU$dHrZ]|o1D$XqNJjkmQ[(X$WH*z"M8d]+/\@D#:Q=eUN!P}drWmN7:8"9FfdyR#p$U8zB])8?T\VAJjb< ;AΏ)9=rĐw"J4I8eھ/PeqO!C]MLRDICU[wc)|6.KwŤ)^,03-x׬ 0a)7v#(G(/wrٳ0Ԏ[(>T fQ\sq/#O&3S3[pqqb(hj d"t:"9$ *$oWrsd%MPnP ˜?C&!*盽ꇣxrgcHDEf,tM}wqu87Z֎ "펗"Wuƶ`DtI,l@5lHq܆(33333baN̏u#+Mx~ϡ'c=IK̩{F&҅8Mwi\MDtrFxAFMzK^5Uަ0~g'c@%SdPŢWd۬l>(opisKҵĂW96*3ܝ-p5*7R2ƹnkN)%%b1:릲7!sJkr!X0Md@9|%%:v*# ?Vl Ed4zYѪ"+'3w/2|5|o ӳ伺f3v1wVLr3 #EB% rl@_@ؔ5/քUd 3*W[TȻל@+l":F C~d-<ةLߪT˵j` }su׊DtCϚm *՚2]ٿǞݢ-4 j44Vf.-:P?f*)P5B|W.m%qm_Dtk:>|LnCN*z,>NT%No?Uh-AveGN^s/_7n;{#'Pl>>PQ!C$ur۸giw|Ophu  @D ":Dti.o%Y3=MñݿފY_N])5X͡fz9dSpIhjNN[Us; ï5]vtidFm[1f}ۮ{7ggi/\7?Ꝩ̇yɿ{z@@Ê]lorg$ Z}ߩ |zO>\yF}e&z[i?jh} %*w֊. smCǙΡ:u| 9WT!cF~_;$eߡ":#-;iRJ?$4ZIӘ>`ɴsV%n|RRV(?TqԤ׷4,WKV's0¾Q}YʲM2Z ldeۑ{}GVnN9"_7'Ѳ``0f0mȤ@)c @>"K­IeT\{tqx~1(VވnlO?oe;)t+n=3e^t/wš2 mv߻M@R?*pwˍ{O6Absny~fΨ{]ٟm6Y79#;)HbӮwc+Mg;썎[\w;pݰ[a0ecZ/V\+87j :Z:!4kHjUDCgX[O8qz+YۍXϥrBG8͑eKwi)!K Da)B..~];͕2&#&kt,ua|B\8_X찍֓p;\oTٟm YdlD^_R/zM(xMje)9D$c][C ѩqq|>q-:`8'քdApSxI:ɹ2XIs0~3CuYƾGtt<(N3i̇1≇)j"ѣE ygdŜ9ZšNU6:u[^M0-"9Լ7c;hteE@q)Y\{d׷ x/~@Jdu۽UR@{@F G*aGL['L~lDtERRRD*yE͇w-5ec2K݁Uҷ5^#'-֊w&8sEynspQxL iI0 pw&V20Т}^(IŝYn)r-yp^BF*r#Xxr>)5dn]LUvenm/(q!~Z ې%^"gxv`.Ar@u8|H.sx 07)x>k]nYas\ nWdt8\ƴB9{Y鱇YOb!Kik"=ZZūr-Mn6a>d{!%%ݚ-{|kv& Jjۼe  7 ;݉,sUveDВvD'㜇4Gă?Vnq0#:))))ۊݎ! ދov`پ x66BNgW wq[Ԅ_YFn"ۀRw)PWw6<54X\\g}B<؉]30h\qZ-Jdx-7eiRRQX7 >Oa1qvb7>1;œ"}X9.`0665E<.bDȊMJ_\㠿[Kw?rAȴs-9zq&0'r]ّ;%󥤤$CbIϏھENDIVۨd~|4wȑF.#+*z6Mc[xș? NN1X^ȖTEiA ?ݐz+onf$+(vv<5"v1]Ay L XИߜsgꆝ5f/8|ÁGaz͇yKV} C&e>;zl9L9FrPOh%zTe>:h:LcZhy?JB [h0\­bC31cqEdSdMmBчĽ"z?.x]bZronHIKD~ gD~Qt󤣶A{!%%jqg߈šp40|hB}yn[Ћs6=%Q4 : `02f*kogqظCeǙStZ_sy}+|iVm[ҜCexߑ3{* D{n{gҘ.|xBͶGFtP%)SqcͶaQ_<"PM).jXd}PM}UѡpBGFtݏKߐZF#2*ι1P4-).@"]$B;>W'Ft8ZYY][~q-Bt@Jpޥ&]oǀI\pEo_%W@pĄ/-Y@BN7t7z<՜$Op(E7T#7T16, R}ݤ]͗SxMxkjyHIIIID ;oʻ$떘-5#HIitR&UO wLU\s?d.o> p ltxxOS zlO(.)мͼc>fCt~Uy(Ի@vĘ Aqz %= l,d/`D(<`FX5Nc\LBn~.ODWcePXozuk~ev&t ɵ~QN1A[NB,-G(mD+ iRRѡ{gSR322c+d\z33fh D箢F;@}0y@KEǧxj ~I>XނT"7cb˞AYV Co?Vwwu8}ѭ.MWCy1|*+X[f8ɿRomsC?2솔DtKjP|{Y6q·^y!=apu\T":0;/v)F;{0MNv>Œs&nyE>_"~ȴ"LS<%D>2oD-/))))#!S:9t2ۘNco5üF!$Dt֑s)i<$JiF8NN2!=)v M8tj?,|l7F*qș)zDs܆NDty&n,ͼ}nVfyX}]!ѥ1̧~CU,㑵b+ait@Nn̤DBgGsEi32f2RSȈ {̈${𰈎 kL,(-UhCQua{V LTǓ`iS7B>LNi0'&"g}yD=)a!d w P:qcf!3:6|<-KDILňNۙndr[sG{/z}IXEt|]^ @zC~d513ZňN}?~{d?R9cD,( wn}ٮ/Bo"HiDP?K$V6*"(_+F:onMEDŹ!$"ՌJi)1Ŧn8GN6Ugm'{nd_83"#2"zü5e䆺hAtw3L"2[4 1æ>QrEzip*/BcHcn f Hx^W,PCZ(jo]Ls(,'؋0\g+X&QZobTj+ؑܦ,㝑 wkx{jhFw_ o/_aavfbieJy,r;͕lEAtZDt&&гV9yB8yTzRϐTiA#m7 \E%oqwsu=xR9U<xn:UDt ̎spu%D|-Ux3(v/B2 {r5B5 ʧ<݌nw R=)t";P0Y~C8Qa`Nl&ր (NK09,|VW=p/>C}_ t c+fU5qj|ir{Y62 1Osxx_#ͰN>h_9>$oG)%4V$rs A|8?}=ş^ڀ$ɚL'?UǩrLQ i>} 5ˏ,8Z\NJQ፝>T.N[:1>\.02/8 QݟA;?"gK .zk@OH\;F)%%%%-svN 6P2|[5OD'P{erGN NNB#y{Uf,-PlGx/ĈPɼa9[?'d*޶j+Y8|Z_,'U* =qn*)":-0Joӵh aT.`;oGMftz{vqQxf)H#m<; JVDt^] JDNv}(AP [=ЈN{!% $=lhcGĜ^{"ZC|9Pq!&#D<̥|03+7fݚD,{&wbM}Vw`(gU`JtBvŧUP=<DL9Di17xy{ئ*DtT>Ftxw{ :.\e!5FDyD}M.w3;5f(ڽk^H8KmOq Vug1G)jl4_Pa*3jNM%FtھUv@FiD}'@(>}dEK+6#hbDdO~~w~A 3\z3xʞݔx֢+C =sk!N3z}OfN4@M*@ؐh]zNކ# 0큓{-usaxQKZ:30],6G3p<ö@9`3D:f[걇;^fC IN`~P آĥŻ67\H;/sxQt(:m;r~L al2S5)+CGz#vDG$Dg{!%e|DGLb'QP`N-b-mƂ8*if#OIo`D}?MI18Tv\PI~>V0 WmF#A"'sKN[ڻŤ9T7ȹ ϓY67B"~ڊ$wňٟndODW*7p@sJd]D'u"n!E_X>sE2`>{!:KDZtt[+b p cV%<x58AGOTMU p E/8j*rVҶCxO~(u|uŶ.vhZ~ хSeܦ.uDWnp 㒟 `֐M/>d<ܔͧ~7n~vo>3+#:񽐒$=/LaCp(w|â^-ʏ*_Bt8Ax-L2'ڞ}y.ZI%{wℐzT*#:.$OM{sxb ?i=) nz[onŵm#bFt? ;PzG;Q/:y5[ z5~ O7P[3\))))B Ə}bǡ;ik)MjBȝ !ܠPx1Qf.;ď3]5F`:'0NN*ׁPFއߋ>F hfiluF9Wb'$L}(DQwC Abi[-N~bx?܆d,i bC4Hy:_g=[)#1A bDG#:J”GǨ]6!K@@u޶m]]O7zYqHzRR+g;rÎ Xjo>HH4AOKF$_{x"HPp'N ?V"C&Rյ],^ ~+yވSSHevȡ}zDx*y~oDdiF[|xZYk>)$Ne>%M{(DKS;}g<8D<cԶc>wB,\'Cy*[%[`1ӾF8zi'Nw"(JI<~mﮑ8k -.if+"ŨcbiJx)i?F@t(Vn;aC4 : ,޿(`1>.}vnCT_Etso 0|qWaB]EQ/̞2w;F~tBtGt[AI0H48Ԗ~zD}:~ia'F;~UVfsf*Χ,G57j־$iX;n5 ߋ>"jce_s4cI,eo 7teDD IM"8hS>!# ژcD7A(ˇBfoAʢ?z/ҽ|^K[gK3̧gJӾnFpO {z_!E?$<ఠg\o2ytJuFOlŽPy0W(0;rS6ovV3ڽ2.;{v=*iOQi>w T vOJ] EEOF_d`aOHRNXv}0yK>XƄб(LLU7V'yL ]_+a23n;zR@qxFtΤ3ZQPnpelR%LX0*#x^ZG%WY #:su.9a|o@&2爷I|O?'|b%nTb"$nA&Ҧs(9 ?$FzDq$<7Ϩ_ 0[ǟUsb[)8kSlG~/ ޱ076xrㅓ1X QMODge SQFi3uUt-+! Q7q}Q|蠐]4SU.PUDT?z\P'jI$W(7,42("ń)N aB9`!j mK, ߩV`i竬 '%t CGX[i0ꭥ,"Tlcu bs@ % eP6fd$*&5%RFMl8-Η΀BJʠNR.d֢U^P90DGBQ} 7U6!Y} ag.9OP p&- V5RfHJDn鱊Hט ɭR~Qծ̼#xruu!i$s~+HIDgdD-XI]| :Jp3ox mLv/~B"ϯR]|[k FҔ4H |p!z'Ft1sT":Ҭ(\Uc ELs5}PQ]Q躩a=v `',_%DV ]8+l, 'Fݦ, :_ ))Q{cfcHl#bNvB'݊ ;s;vg@uS;v* 30%Dtj]Bzфz#) xUe>GNxz,?Gg*LuI _#Icyy* #:2DtP=&p}54車]u_ջe_#e(|Н[К0Wش^ D/2"vh1:OvYK}hD^G|^AcCt)mVaP:͞w{9=bdp)<` !4KҘCKQk[Ou4U"˽M:"WtEt%ca9'ckNUvj۾-G9W,ZDtR.T5{ŻD!?ʧu\a_Qq_sԤ_|(GH,ޜU0 r[PDX}@# !'6pEظ8=))))oԨsa^8 fZ_ޖf,- u""j}dD>Cߏ}wn_[oZȊ-Hhl%'*yeZ$DwrvQ~ܠ4܋>-w5.itu[DvNYn H rjsֈgBu::Se.( 2pԘ}[,gK0su*\v )"Ks0**R<-񠵭 xbWιAJR GG@9C)<um?wdc!Xʪ!"bSq5 p#?{jM8kwmްmk[qm۶mi<{':6A0p Jcr Ai; 7g,?hKRDt.F|=w?6ZU@lpָ\+h]=aE:Sӵ;~] !̣;!|N:Y?0bs*s˷j"|۶}[Hν\VЙEք].;X9.b6EW)`û1ߺKʮ8ѿ9h:s zn s9YYZZfPKj _4\W\e.ݛ'"97=zU^V9?u5^zu4n ;xTDt5Ӣ":@D.!W~W־&xQ*D"ǚ¡&lUwYD-O>?g3򭗢0XU"h ,'vaSp-Z' @;Mg\Uu}U qk׷|~ލ~F;"9w+@d_{Ve^p䲮:#7j@DD8  oscjΚrX":6j~gR/:-l|2KP59a+syw_Q%Ds@P29sPQ( PDdɨ$Q̓sN9 NO~U?:ӡk{ܽE[OuBa*>x1ynfaV0͜aaVt 00 ]>RZv~PRѠlGI_o"m/0 bfMBY ӊ90 0$en>w.>48q-w[2U2Stb3]z̕GO]x= 2ij?@ȉ^$doJΦ Ǵߓ) .}4L#-v4*GtU9vCp 9vؓ0+:aaE3ga6St8Q>p1SyTpob/H[1yΎ Fdn5-Z-dž}0~CKo6Puߓ avы謽Kjnm5*,|Gpu*݉A;I~ade3XMaVt +:f0 0 +%^$ >稉3/> zKY7l}r? 'q!J,:/]{k3B6%N\_x,N>g?,t]@(@xS4}ObpELKSG9V-vXbURG 9k#4p'a4aENJn0 ðcX7saaXA [H@Uw%5 z#30{;S~~Kՙ(juƒrExV3iQߓ%k[qU)bpڔSi|"*wZ()P^R}r|OaĝHpL[m4 e+}j+ ߿ݿ}ҍ}Y./` ??Ӟ_>€M5[m6"bㅑO_iZ#eeɋ\\-ƻzywo/ms /m@$.n;nWo>{_EQ̠0{Ê.:1k?oo|G/9}X+:a?-s]:[?lїKO yA;V8ﭣօn.zKGO}:ƧWcw+}$`Sg9mܙwPC_aXH75?p>4&i|E~'y&l(4qyȿ[_/q}KP=L9K:Y1 0 ~oRگ,a8J}hbdNhE;7L[O~}0~@B1C PbI dlZ肔vySԥ'+sG:9NNWSɵ$jPp?7ZC֛O :O>-΁.l˄6`m+eEGEX |ywrkoUE{_8WafÊn֢'$WZdmhdrhwr|6$Rowuy[{ 42d’*L2aSքT)I5i{]^8۪msbt,f;.H8@LZ3iX1 0+]K.U2HusKSڀ"9G= TDҒ7i;.w=QqOB>32H a:-ܖqVTt;UyEeؓx#/4O{}5c?ڣ|b#yQS*/ex}k<i8P8]rwڜo/g? Vt 0VtUsȾgGh9y')MH;% h(HeH[aDEp'ܶ)/sZ"FP,$hS$^caI`=C7|2\Gi1qԊ{ sGD0<"Q0sn7 N>`?*ʢN+gD[EJzʣ腗%(W!NܪmQف{cOaַMc}B3JO h Fw~|vI[ON~.{k^ܜû0N}n^j_֢maEggُSzPa/PXy=j*֝&{iG@[5- 'IZEwxD]E&ϙ$eveԤy; 6]FP,$]ߨ]xaa(:%}#GFq ["m>QDU8InSE;@~bT^ŇD|zLL= wjT Doؖ^>~8NU>낒 V^r?)6%eaX;(uG%viOaĂ.ydh)h+EWYI]$<FM[2)1X{d1&&Wt?yW5N8.!>HܬVstvv@3W+&8{xhyE?߱-'jb;kb$ x7sW"x<6k*Vl'a[PJ \i滬) /3)hR2&gvZ ä)kN)s\~OSvA\+8=ͥbNp26sgUnÉzkWeK)I"*Zۋqh=D/nb4J|h$Y׍_l?p!h0> :aVtvaaXEgгB͢Md'1}nѸP ^p 7:1g}0~X ]"uJP2I${j1"ant~ `"lK/k;.^t@Tĭ ⽠ /& F 7dh1 ǧ I6й]3ގl2CH£qPҼ$A ǰVb;ϑblc8m鏶o=q7s2}Lӎj.K~y,4q~\}U8zԎ% 04_2|Bcf:xicd5k{sk{d>SƮc,Vt{ J#R]n|n)ʝ>{]h6? #JFf0 0 +:'!}#3x3H-=ԘS4nmEgFcg:+:`$3Q 6TlZ$ߓXfYuv:>:9{;s]=g 1IYzM^~=m#rW=i](Z"MHq&5-'vBXALH%D[g1}ƪ aO]9H1VSt̏4W*=hqˏD1Oyڇ- -_//i0 Ê LZs,VtG~Du3ì}G4WtK|BD0mOqP:,6[HMѩIʓR{eި s>g!WtŵH~0 0Pt+6 J<>v-E'HeG'QdOq7HIj؈kIKUt 5@ZhIigRG= o|50c#(>8 \Uc:y!5>{;:Oߢ8{(QT]Fz$W/O! 7o=_&QNG3핟rv]}2\E$'KY嫖&]@Za8rb{^A?H,YR@v#+:;+G8y~y/: J0*i0 Ên$)u,'[FIit-JbZ YrE1mP{ʎ^Ѹ|QrZ) K)I=U gF*5l#E9yaaEl}0=nZD;zlCQN4"FqroyT0(6%qJ+@^ =Vl0 [SI*6t(>aJv!!ؘ]ݘ*A/%(IrE'0dWIJvR<}o:*?,ZhzhU&vEƛSt8bX.Epo퍪s@eOA*]D na}Yu!$YqHI%p{"=lF %Eg1uj_5=K)Dcaх:yºj(Er'頸#m6J 6? +~f0 0 +:d>ѳoَ7Nxi_~$-D$lA=#ЊN>B}O&ZQʢeo`]$js{+`|MOg L+ "N+"g] /@ rX|OaDd/0JGŀE: ./xr3*0%xNRVv\ :{Ҁ#)/M̅?0m,{IHӎÇ(ܞ.7+%Tts0?Z==8>gm[= _tY;|j0 0%p6X/r8- g,ĤXQ3p;z;ƧvhEx'ledŊ"S 6? baےK 6Ŗgߨ#)qeVeAm@1욀H#*:``ˆ|E}@}$)F:PXCp1oF[=7sdT"aщf\髮{Ұ#]K{:wdh,KYOZ(*ʗFC W[n*'W7أ;g9cڬrⵗڟdYE]~%}aanwEG}gO*iK%wunl6w࿕Evnw7$Ӷ#A[ꟅDEcaފNr@kz$Ήrp[.laQ [ob4w#DYѩ;a|cԶ4dC",:qV4<rN UtvUGBoO,hGʪEdJ.B[%oTv#/(:):9ة'0b{0=/N2} ?g{ch>{UFkDdRV<K rW{G7\^JB]ѩI= k-W-wUt ;rcD1sxbvT5ʍЪKcVȊΘH"= 0 s+:dJ?1ŵ,iFuBʝ7B.kvvEVuE%׸ psrXH$tA0caݡz]|*'`˫Š 0?NogpTtv{Ha['/s%"D$HO;d>/z %a꫐#?N}OaAͣ5jWY H}zx;a^˼E;1wђ/r8O;o(8HMA#+)):-i1m}cUt_+:a]Z,<#b>yܗooqbt-FTt Ar輴c#2$~8EoHL$ph ݬ.j;.zQb+6Ҕd@E' 0Fd>~Ѥ.J;qδ[Et;1*n|EkUkAyO`Pss}K)hXɉK:_yGX;< ڍ Vt 0 +:(yL|ç Te'>]>{Lz,G{\~ '?36rEu7]s@VQ{͚9mSЗ"pҞw><GoWIbwNt7p>56T> *:aaXэ_~ԅ$ts8>:)9y2J9.C;*m, e=µ iO8 s`&[hPEp$״1\}O6v ^$m1+rKU"NgTE' 0⎙ st3鍏kǧ=(i9g|EG B}ב/>-`E'gך&&o~oP ^SaVt|"d|LU;u}d" *.?׮_>Ę+]сaj'pVs=󬯖1I_3imgz]Qppcs1l#|Ut 0 ð4AbWQC@PщS7ZwRtA;a{A N=Wߓf#[> 4tSeHy!JE G?@ac+;T6V7mǨV4cZjMvfK0R|VtSឹg$bEYM$ >r`E0 ðïoh6t} hTDGz&L/lCH|.1Sĝs#NrJYėK+:aaW [{sp^v)j)tn/v{cQ1lA5*xtJncE7WU>/_?BQ~?OΔ/WoρYߑU8𷏻kLGoɮXVtSb㵏f.>~'nr`E0 ðs (]`Z̟4]_ȯ)FXಶڔsHBq4kmlV[GAP4eqQxP"GP,X1 0 Jdijʆ!hUSnTisk"wD$xC"]uK&@eu;Y5\C9=62QJe'!V6oI3*ܖ%C0V ]lO*0I;ԫfU@šr&1~FW9KϿ`CY,2yiym}O?'_xm%{8'kL=Zڶmnomӵm۶7ML;oOO9ܩg#\S~U@W\һ݈33F ˼!FDb*#މLL#37h;4v4WDMYV \P+> ͼ17xqj+\ZȘ99b{7yUQG6Myvh;:L~շX`9Dteqqҍ{cbW"9ѳsӵ晁Rf0lHgqcGwrxI!cng-Q—@b(TnjvIBV*M_P^s׼3Й":Fek?iDwnotF;Y:fSBƏ?oEIY.]^`t@S991sw:s@DըP>8+MJ9'xϺeMKNz~9AԜ3Dt …EO>P^f&t@Q˦3GӮ4כ}":FݦNq>wlǗ3Dtu*GLfj+;dȭ/ {z1w7g޿aEF”A;6Q}g<:3/tWHոIoӻ瑹wD;/߯z Yf Dt, DtWҍ{J31|~8jʼF@W Йv N3kSteIͩ]Jv@4L0ð'ͰS0lUrZbہj6WtfG ߺKR]1@gzN΍ڶjPVZ_kImܿg2 bݮHCkbrURD^P0I1#j@@͕Զ׶!vC] vsb-kh8!tu{\"k#:.gúKOQwoͮM/^|I#;3=TBde}PB@P6:s75@X-鋸L}ZuH DtudWJMy`b;#1<^8T%dQ7JOJE5-_iDG v _cGt렪 G"|p}S"̰g}JɀjpNLA+ U8 ҇9oNJ>Bu&85(?Qt?Y'n.w%u!2}\")!bU 6 ܽ"mN(1| (1RHXZ!*D0 8d$OC£^q R`X$HyBtRo9x2ؔ֝W,Zyprk %PDSw"FP3..~Gt9Gt Z+K֝'.)Nc`W*27Nk 6T:z7);-'jgKuoWdHa&m!:o_Jp/e5bDiF^HbXd"HGX/_lٵ{!nB&"1B~g!FtgtD2tMZ ZHB*r]c ETtђeKfb!Os+`_]q1wѨ&̚<Et ΃oy=".bI9 $Ց (.=G6TVvHG;-*Ѡ@쳐WYR3ˈGC"{ξ~峚6:E]ᤂS9ZOeU/'~ne A&m7Vq};ޫ踙QV7KRYT"Vc}_U4W[uۗ?=GI{z~K>{/n9d9km`Q+Ͼ߼>og(: _AvyÆ-c:s?{>˭xГl\87E;;~ݚ/Pu/}Vt:i)py^Uv՝AIC%%<=^l t' ߪcDj*ˡ{r1H#:zkʬ}U'Mt2-9 e_&~na޸f53_f[WKׇ=ߝ03ڔgA B:JX@nQ BnhEt[xآ zpٟدp%11RZy/lveKR6rCh,`|w`%vn iз9  #/LkWv~$?pu[N ѱ$0꾄6H= *lYjam%C ŕ9rI9o~RFwʍaY#Ro׶."Juw.L<Y;rΧ$j&/9IzA^S3%tԵX[ Fd% wKi hrvDGn`0R.R~t} \l=w8;MwhDt'RNKrj2NkZCcط#iW _{XR֔,ZbGt렞;ư]1bݨęA~T3TapKOinK͎U5^ig?꺌1B= q䆰i֧5hyt5m1x>~6OU` *GtQFW]vQ_o_wTN֞q :......Rn㋞:]@ !:(̨S!G`_yLJWӢ\f[*DǾT'x\4[| &D@{뀟'qIYQCVQNxk3>2\WTTJ={KYi+jnO2eR>ˈ0AC['}ƎP8"e8o|=n:ۮ$=F A`m=٥hXRby<9} |.3iMfpihZG~f@t 렂?<.X<0I3 !fbJ}ϑ),;Rb§;L&AEtgR]OfC+:rJJ/.u[ȍy|S~n1zq}R#an.w3ԉG< *m"qPf,Hǰbç6[aGtH,)W? r*]|&O+ÄV%'GDi>!<}wa*y"C~)șVDǾT.BȖTv!`c2yl4Wȥ(J.1yjWlxG3ugz`4boMd8 ":6 ژ@ε9OW/>Tecbވ8)kTLVRn # ~@sʐAk{J(U.]1,wU(ъnzŰAȘIwOlOhAB=QBMJ1tUi#Vb{Jٛ W9lR4Wg֤{4 ѡF,Ѥq G HX-16֞!":|mB:7\ǚذi߱$k1DWVOĝQL2MsGN9zm4+^kP6y'W$\8Y?B =ᰟ]Hq)Om㘅Fco-`uBԫc:R\NIM}ЌQ6WgѽdZw(A}1wxT^ Mo#Yh䈎K%wq!Rr1n8%_l>;%1Ԕ;cʑ Od5ft;ZB g<1 'yi[zCenvhQwSRYuͧ6vb{-:`93G-bSAu>/4DCLF $G+/qHcVAdD+x)B.wޣTA=k-yg+W1H3`3EĆO<C gyU# P\f^-T?.Ftj/4BgHB{c@#Gt\\\\\*Gt0[*uD>==,|h'*a"Ft i1FG+I{T}ZGȖ&ѱ$QzsB>v=Q{` 1:Xέr(Ux|"$hJ#ΡeatՊ"e۶GuБKicM!nO^Gw8j؟ kF0ۙKl$Yzx1QXCN;(:1P=;];+kf:1/ SΔP?/m?WF_Uj@Q;踸T $:dy!D(Ua0o { 6ňcb1۷/~mX>GQ`DǾDHOJ ߒ =iy[ =&~^"T3r!rk@~9D68F)_3Mrv6w@rB(ޱ,H5hq9F ]!oRoMAi7\XA;=[P]JkӎĦ64h#2F$c B29̫wZ!hI!DǾ`CUK'B낶+n:;vؔԶR$aE̍G]@_!ۼQ煐5Сtbbf>E\ Eﳎ[$D^SDx&U{1/,2+OPnZ䙫L֯oq)̎D_wT(PsBHfAtbnG1 ٗ,~ƦNɚ]qqqqqAi=ORKtx@4)1Óo#N65 6#(i *.\>=p"*v&'.[F,'v d0V,ɣV\B4*#GNȈgR͗K|]U,JjCشfFcf@I ;N Y(} jGt}"^'e%}8^ϟOK7mh$NrJ$S̗5pDťqD* pBBHY;3"'e.Bh>$AtRWu_[hi 16mBtEeDǾPܯVKy@R1`c.i4KDgA2}x a%c1\S!w;(#Ng4V;h="Rj]C'< ӣu"ĊV;S>`A죀ȘdҰB%$P54(Vy?.(:D>{7dŬ!8M`Bq 3Jm kѱ,ǀ$IeQt(Qư IDsم SOv?Y`$;{1[aC1 BVD'w&U~Lvz-u[{3ݯ;JY1KֱAUcuS'?LyD7~D1:9+zOsDťqD-)]|oWLΦUVBwMBkS" E]1t*At+ ٹ(`{` yD(8D sʊ%ΑNS~lFE%.D0czvܼ/_?L ցJ:YAt#)k5<` 2彚L||8s%)oI,\RgŃ#n|Et!&}7]@Z꿌ݙ!:<:%13zpr6;EIbi*eb^1Ms'^Y^kS#~i-ӚV -)JG:\׺HaG`2+3jDf#oGtUwhZbDǾn}~dEΉƽGtHv:ϙ?ݮvX f=NV`CLM룃5dv [=8%>BAR@_JѰy" 4Śo$(_MFVħaBn@v]p:A4hd܃OE5 ޷ѝ*yfFXHDǸVO؎GBG*ŀ^ i-ۺ9MyY_)2*Z cчd߭ݷ?s[%`:ڨ9Fdj$ؔYYy'Dm֤v(:hb'%C1zPTFnq^":0 ՝qWfs\Md,kۺ=G0sմvjFtʭy`65Ǡ_Մ踸8:A%Vu}2J$U|wZWE+.јk3vF ^k0sZDǾRn ǒO$*T.(DZH-&NmDWg'6ث^窚Ϥ.#.Ȧyk!:~8\(ev8xᾛ9[xzɻ1_P3hKAyDAot6y=1Gt/mgm1eA}(1杖 ZO5 zDw466|5JUwFϵ+Ā^,0DӢ)V]L4q&cfgΥ1Te194U1' thN:uN TDgd*=fƌGJ4 4ŭkSb-1У AϊEX81EP}V6!?y@8x/}0oxNDǾRn ADǰsϤs i9MZbX% &ҳ .e;c?jȍ<&R~>-kcLZˑtjCt/4юUuKaWݗsD ݳ/ݣ2nf~zyDу {E'hy4Gt!P*zsb]WGy GzblX'@wGt; Wo0B"2ՅVR(qw4R!`c ȕ ݲMĹQK:IECb 1vDf~}6>,x!e2%N* Dt8j^ :[ej}|FG9Lc*Y/ׇX`f쳐|-:3ˈ a?+G 6{ m*' ' GXui!-HD>pUȽO۸W##&?ݞI#hT x֠VmhTp4|/|{fTo[ER? i]#51B? W,q|Bhۨ6DDzDQjO~F}/&!e%OCtK>(Q9Dg#{;Xg8jNo\RºIQkz5x*!3X8u܁sr/Naq;EGӁBYVp Ɨ%a~!:]ZP'%V'j :q.ͥRp/;[xPZٺVƕ[%+@ Y )RJVXOTe2{`5hL xe5ڵ>TUN㮮f}-Vua$Gt щ3X~eVi +S֨oЦk#OOʍH#Z7>RHVBo>0deG2y[ym?1 lb$,%(fF6( u5#F?]V#,}?N_81ٵ6νlsBt]j4̎#:yDW'6y#)qͧ6nvk GtG] H󫎶>?%6 xPDњS. _vS*4‘gO']#ב ոT 툤۲h1x/ns°'4w߇z{K}V,rp[[bq|ljc$R[ܼ1N[FވN#%dK:MxՈ=AN'_jkc.y=ߍtIDSDY ܊̡r#dH!W;#+} 8*\ <(" 1խn#:5}{ J :*] . ,s֫1CBF5޴cB~&#Z< l!i"J,̫.ߦfCW +3M&թD~&U\\WXsj8*BtСedĻT Q}?CeJɼ03 Ԁ踸8[d%Ь{8A.D$:rmBtUݟZ9-4IEe%:#NRYn6v{65ʠךAhĘn *!c%Ǖ&i<1`c AR`TO8MPA]RT^9DѹY)b;DH~y|Ī{f Ƈb!NCqrkZ~++ŽsD7n&f2OUplœOJ'b \~R7EPyj<((z/nDdV6Զ!EgO2 O.DS CN[+mh@9Ӟ# )g7p+m*e""NeVn!Q綉G?齓SW8uzOI8ᤂwJ. K-?0~E >UIӚ2;Gn}?Ͽ}rVwYjNKk/ѠjeWݩTql3u_ky'ܭup6!"*>22d"!x^/0 ZY`y"GNu:"9$Y|~m nS!_(78`/o6t}{Q M/ o:K]b1,v;øۃh6 L_i/bNѶ}d`3:yoasޢ|i1nDnZۜ\,l"U1P1`*}v%cU/~ٜ>A8ZPZUWݝ@{[ 3xٶ=Hqu{m۶mݳm۶mv{Ne3FIzyy_zDvȨlF˨TC\U":o|%7uҹWyzݱo2̄9uyS/so}wU1MKٮr:;ͿFHv=Xvng;DUr=!4_䩀2G[￰𹈞$h3"*Đcś0IfۣF7Ck|5f bUm+oE!񧼘&a!-hJ%{W.SՎf5fXvw}ӯ.RY])``ULmcǭՍ4/^q'?iY1zA*:OF9x;+KfgLBJG0r!<m.i/㷠Q"px.t CsІy*R^»_Fw@H~W^Ǩ};  9猋7v& v 3t^}'/+sgyryˬːt:DtDtDt Dt  .Kt wwsO w>qClFe`1'4sR)pBEXjQf]zwK.6uFW6P ~Rw'3?U1XѰ?; .c{fضm۶϶mF_]}=ٜ$}t=紧݋wX=v7=!Q_*#G4tmP'oFE fXc\6 0SM>tRz&?|[7/S AAS.qgt]@ZAڭVH7:SZ3:0yn1tHTz upƥ״LO9uDޱqz7df1umpX]'*{ 䴳a]03#sEA2C bfTtXQXӎ.<1oE2y1  ~*FE'sTtxH.TgTɑ_d :d *:n AbAEkKȚw"+V! =)=[ZSY 1%9_)mab{b|K*:B!Q碉}֧DbYZ.a\ PeWHj'׹jNkNk;:F87iC&| .|WųpxXj3\[VWNMCi#"<&=  !w]+6:,-JoqmHm![-d "&Y*L VGfܝtBnw]{;G:<XrH̢wb P8*#蠐`I:f~E'|*@ȑVH\iUf!|,br-apë$Su-Srg<,]~!.QcDML<&ΡBTt;K/Ds&}{BvU  *:$6*:ǃap QPj{p<ӢfEo}{>M:YF𠮷n6@qi4v >Q9vXz -0x+<&>Sv"4ƈU\-[džQEY8A< uh`)I` k5$)nqYp]r򣁫;`hNd=Rl"SXt.WѱWt Tt"5LBAAPѡCPѡ3JP)[i_Q49M拗MEk;uo@E7Mhz9n=Dž(:$UBB"+3zR;Am O$YxVd19p"BEx]C"uedķ\z ~rmXTt3 d Udz*qZt @:cB3}*"Αd5I T1|NKISS({!=H$Ma vmӲ64>ҙz;$Z8&׷> ~kZiJF1~QJ-f禉ƾ|y26/b. Ȗx/\ g2k䏍c_U싾'e$y(:c`>V]R$9XwJ;a=⇊^*|1nh_Ew$:6! tδ'J;߯~̚g& [rHBaY-5,}"ʾ_,x/ⶣj납?<#N ~]=EJ~c_H󳺫D1+(A0P "Q$$Aݼ&ɛs7ڷN4#EC93SuWuuu;^'(-m[ӥﱼe7Vr;05gޏH]gݗ-fs>}Dǿ{+W>ͫ1:1WTkʟTCt_mۤowy2@xq1n =azBC.o7̌Z>’ i e笯 :lZu'k@m###@Y`2;hAH Kmml`I#="ۼb{5X(w[s"%*m*kLCvk8blXQaj4JbTT;%S ( mWlo!icKO҈3x'gfh{AXZx)Yl e>[&ԚQk&e' کc5N&S :D"_?Q,baDſؐChT+}\s?ĺPy\)X PX̩f~'kl=HxdSw9^uOT+( (- $yQ5aq).@ *dԟZ*G@y>h$ vDcR[5ꂅ_EWe |*ϹM;Nέ}EMox8Ε(&Dy8Jt oө!s"gM*[u^BU{kgZQDDZ$`Ԥp6v.Viݞ34̡H8L:ܙ47?=37*d][}盒ĺ;"{γ<ۋ5dg3=Ԃ09KZ`P혱EΘqXN].]ݐc+hY]>}gׂQXñ{,8pkHXF IAIxt `Cp8pAϤGgjT}"T)6ZNvYGx9`C0Zl0?-7ԅVa{&=Qktsq OW-M_BHI_' pt\[_T[Q>;נ 8+Z)Z{/(d<b9_"SAeT`p 5||6 d@ta^D E"Ae$ո'abk`Sͻ.=x@_pKųٲ'х'A;' D{R[#w/@4eDC[ʍx1nnsDN^:XhEE'v]o DG󶞫R;SM9-R2TotND;#G :!!!!I$XR=/S?mRk+ַM%r$:NaIRD7j-XG8RCvș9]-DDZ$?"BȖTv9`㟁Z]I_"2@=o2@]v%acH)Gti/N7S]~RwVu9.#ޘQ#ɿ2[-%n#!ٳ\_=j2?vM!!QCDY⽉P0[Pָ'Ɏ5"%BURcAyq/x^1]NÚɾ|U\z9aᰭڡ;fNh?YJd׷.>MFzl[IIn'% A&_p|V5{ ;6WKu/:}.dInqlэP=GnGr\aQI-<|d נmS{J8n@t!lQ=s_9H_lUqO"'%/PVb&~Y[>7Ǟ-^'ayue;.iDw4H6ݭvQPm_LaW3O*#:] {~M/S쪭 N7C3⒤B5رoi~ufIOX; 'f Kg0sV[oPso5ًV#'+hLx13CHSح,NHHHHHH RHϲXk^Qp4&:]bFԔk+1\ZEa+֐#:,-X0 o3R#RU}ٚC)u-mIe73.v k!Gt=Yxj${쑜H[ףT7`^˩ f;o{&B=`TcjwnEy6q֛is  {1Mh\vG szXo)zɗtxR찪 4 ;-x;ݠҌqIg8|">ԵPcI]+k5nK9sKjy" P=Dǿڹ`2cMqG:aޓ8%۩So0mEbsPڤ=ۓc퀻*ݓZx;?Jkdsð0FDtfWPؔAtj${kv2]#ۜt%qٟ͗؁Ѳd:+9t.X ?S]i$ғR{QN6)l3_ eDwiƿ{[ Lj+>}{1R\Vu͈`(EtWbOtXQ)6[B#ye4t_=VvPT%92¡\ \)Gt]X;2I7b sÕʜ+l~LދԑwOJ_tDu< 3p<0PåGnhABRe%;RYDtC[-7'i?N.dgU@ISDf@tsFŞ}W4t2r\ԶtS#<ajD=ֈ${k# 67cGLK!:}< 5T7wZ|R`cߒOvQ#ZĒ0۱߂!ϩecywyTѩ/ C5!7|6Iu) eOp, xs-vdxJ)&pT%yy5/ #,c#pE I5Yy?eqΠ,6J,@8r OG<v?!=nlS(ɦq6`{ysl56pFsgߵ=n~fgWobaS#<i9X#4kPVUG1C$y *TWl` SЮY!:}< 5t@GHXXx5iLU{z*tB`9)!C6LuԶ`,cl~$FND''0"肔 S'.y&3(H :e%$hW` `,b-? D* pseWpcGajb5[PjKZ#M8ς O$YQrѡDeUDNԾa>STkwnkD$Zz+D2`)ݓQe LOØ {$S{'ω D7jEN$lkJ&Zyuvi60d!-H D1c]rL_=6uBWZ#:T e!.xϝ45w0I㷝r6C9`㟁Z(#:@IBj_s.#>oiz3ޢ^2ew9!5n jIH@2HG03:B*KdrDwpZE;FtlϢui>tQ<`3 CP0 벺\NnQA`'\ l>Y;Ԩq'J`vBo"ZWQ $=nՅωFtR>g{zqp%mBݓёt["v 9"Q`LQKt]T @D'sWlo ̋'l 8=$Gq*g3(hZJ_ L6#ē.NS Dg` F]@iZQRj>cl%H}D۰=8h+ϾGcr5]rV%;:5 ٴ Wkѩ7,2VԴ$#j#` A9:2oZ;ߢ02UkY ,]yNÈyv׏ K]iJ":Uwr~ _7™8dQ4p_ !э|;VM˃+%uW'6ٰ脄#J:g9%m~02,]i ch.~:;RQ#'!잴G0vU B-[sxr}3w߼=$ضoJൾ"%A5`pݬU-+l:zbÍK[W.N*]װ|Y%GtL[^bXvuʈ.\%`|rESu25 lN[W@. v #:!D9HvR-cW'D=ޛA :0 6Am^v̉F u=,,aQ_-lZDtljo6Խ'3)PXyٲKkg4&` {29Pt$rv(-5Skg!Mt9;@+°o-*!:U\qOjaġm' KCLD'My` kPe' 7Jg0c6-nDbzjZ[z0)LD%脄S T]gݤbIN6J]9;w *#:ݚey,sW;bzcLQ복waEш.9`㟁ZL1Do/ƣQ~c ,R >fǪesDgPz=lq8.O=𡋈.Qx8eʑ}Q&I~˔CңX sveDGϾ]Gtqn oc3!&sv].w]ysD#pPJɵ=PHtvAjtQT, GyVY Н{E纐ߕ-e+ٿlb )’e+hTXHϾe ņiv4T>c |hY4[N} eӖԶq̦=JuSD'HHS^Hjt5;{Q+0qkqܓZa2JVl]t4𗶡D Nye2ϿTfbc7~X{^ 6) _>ޝzs%Tvo &|14gld%m`eqPt":!!!!!!R]sxð(Ү>Ŝ*eDt{ݼy렄ҪTiLb :dژR/%ؽhTl3_)3whgJ^mΡ6q@,YQu@`{Ԯ`!Y )F%hn- % :{R)}^+n|;)2ߣ1v.5GjQ#>";eg툗]фN͗i 黦5ddiCk1aᪿUC;6k7(s@CtZK%fT)}+kOפEtBBBBBBIgj lFY}2iɪ.\irCm_}9QFtW~tXEC o[WH7;$7@GyiNBWܩvpըK- =f/ugnk^V1 wuDHS=dY U0hw}` rfʇ+9xNvD!U RMNX& pdc1ɺ1GtET06f[*mmHK e !, \9tu D碐U:. !P"[p}6'V-j\5@tܸpc#*f69bt_zt9bh}iZg3kYc0a.<ڞP k=6>h~K+cs">x^$=YzaD[ATXpKؑ~^:c~'iߓ@tkvמCՙAy{CcUc T ,ꎆ' uR#ޝ*4g!i;&ͩ;sv*`[iBnNV%$S'A&RFqm=|3_KI(#:*y`8+mUtK%ߺ'IT NAش䀍k1]>Ơ艷׾`4ܷS:z_!wNuљo>\Grݡaω3XQ7 q"2%"kl3ABHv ttJ橀]E~sPGWl9jdj <.$;@wsu}1]kF#:w7+hu&]>8k\ej2?S >L5UmdK:kyk*\W^QEXCtY^Үs8KgkpÀN I(U[gaͽpQ XQUvAi0\Ett7ݭ.zmiASkzG{h@t zտI[-i=IA#FNq@tBBBBBBS7d$ȅ}yDG ѵ|G]RkN$G&N NaRYF' b!:(FzL~Yjl#[nZ>K p OrHu.v)#E]Dt~@- ^ԞmlV}xfA-Q>Sݷ/ݢ )/\O"u}ёǑw_Vos $NDɺ[oO|^{{wM^D'$$$$$$e ϫQb6;Μ$ _)GC2ED4y5l׌` !:)d=YMo(XѲ rRgSY)H]\ u(Kf.#6qFtf[\y TC%d0"m`i.C!%r2@8ÔOT`@tCx 8v e<$+ 2л:B^1M訚#h(ՙcˈ,(x{Fa&^H- U5ֵowQC1)UB+sACNW 4&ͻ59W:@`Ck⦂/v`,*nv{? 5|[j&^Ƈbq8:0!E^?V/.zǖ+xqesAC[,>W,w*#:%$$$$$$$OQ 5r|T>6ϋ,~8a{W\ǂV8_CL3k _Vt{FbZ>H t":"9$8ZMa1ZJ)BA|-7sXc_+xi}Cӭ!f&Ɇ%6“/ | 3gŮT,BC`<ՠP 1a]7bV"q"0Dpx; ]Ft)GW>}E~Lڧmph}֗`_KחQ\LayRz51'b|KMjޢœ @H2=Q@WID\KUj[ٻɖ=qr}ڶmLٶ֢|®s6VUĽ¸>}}ʍňlrOKw2Fp{^U-vaJZ'z ~02jÓڹH]ET;?yǨ>&TX)?݋ Wl2;FŐӢS9ѫDNon۴m7ڿ~]6Cb.Jvփ-Qw =Ra'w4ټjc"#bc_\UbyD&mL'e} I#nɅIZ<GhG2S͐$fȁ/{,/ʭre|z}5N[h\hUU-nV_aɼG3SPv_`KFrϼ@NP/; R cI5ٽםy%KCv;^<KHS7P"1&>'OٯsI7 ": nT~󇍣ly_MceNW/?\цSEtW :nLMvEmm @D @Do2L ;[Oab/ ܴ7]ޯ|YvPt|L^PhOdv ZQ1b/ۥ; ,F,:VmeF L r󽈔"7;S6_@D5-bt}l.5=zx5{ª&sGF?1O ~kd݌F=Ն[n<̩hh>)]b`C7ƦrFeϪj=ȃ |gtd{Sa! [;ЧEkXT 7w"\>ibKDt^)4`TLDGDRD'3r'dY pS* lݧ^fW>z؍;-$&>bW6B":7GtxP":,Nv =;>Av2=Z6> K-m[v<-PJ "o NeU˵a*3xNN>ݾ/:rzOnkEtZzw>ފ;G4WrŝѝG>b?WzDnpLZr_ĝ9"Y~Wl=x F @܉I$#A!!!x N NwpqנFݽ*6k:sCj\e^9 D" V27jfQSlϸ7L-Q#0Rb>'e R,:,r`O gPňN~s8.VP.dfZ@S; %fUl{:,ceU0ٺ,8X"Tp4kZe-@@$ DG_I$D"S1!:Bt$~ H$ T~B*w)իG'ynE|:9 xׯL֨D5aJȱg,tpRBX2N#/;ɔ]te&`:%T%/W4TcAu/KYѝˈǦ$Ddnpgׅ띇owO\jF5܊M! ӰF eEC|3%uuv?|R3Y3;05,> s>v- q9.sk!@f> =vyU`}tqmML?+}؁[]:kVTKvWh9v z濾O~ oW68-Y#8uSzaѓ箝Wxҭ4v;\kA,d| t?o쵿oU{5ù Vy˷xKOVڋwŹ3Ww'y=9t2">gpA`o 7l%("H$!:^Q-%u/Xۡ+]~;CqK貝in1O5뚸1vBW“.5sݟ7 ^B 4̡eg̡9aK">IUb؍k 籊vKfe󓦁X7T묲(e뱺)U=wdvɟ<sA o\t)WH$DFtQ`?ZY&oxR1C%*9ɔ!'@e @> >> IB2C ڐtJ Wlv5v-~sJ1-`!tI}p_C}k "fQPkL+GfsiڈLa?[^g]xoY>So=G@LN  ;WՁ0+3׶Cb?ܓRTe)j3i ا2/}S26ֶ-.֪fl/ Hּw37,3@#g@'{ LZ3IVվa}x<=jC&I$']Xb]ş`.gҴ7Q&P!Fba#L|~2-2*w gZt !gO%Z埮v-@Y&wfC":WoXη<:^ރOD"H:Bti[PlEhW$쪦nl݆ 9h :,h;|Jf,ZGt}}Xy'a]>=|tw aQPe-~*QMrΆNeTFv菉^F,t\(+sO"b qm< Yo9/nm?'C | /k  *8OGta30]&Ftv)"~V8"3f#־x0Dt/Z,j2~S}ؘᷗ1sl CbR@D"YXxęi+H ?X%3r4Emㆩln_x>e&QL[;QuSвyEN4L|H$<4 ёH$Dǂzƕ(d“ň(y[ s%u18Rl^9/uZN}Xt-K\vVIu*03t!z@_pKU1I|;IP8~wR_/#.3jY"Y WN6e`Vqu<9 %oÄ̊qtUNugBN=OGtSf_Dw+ﺐT(=#:^3\5vAcbl4HiG${#>;!?X݇"_3`H 1D"H!ctZG~EP:AW՞/p cj=<6̌7`Lž"Y.MʰMN6U?o2pȎh^LBW+~ivt巋 2q {iёH$Dw߈A8Z9.0|hB}OŲp{Ճ8 oYEIInbW}8)Vgŝ;D6N YӲGi;upKULsaվ)0+n.ioJVQYxl ZmUdn*Ne<>! GM9W˔9oohgPZc?5)X|w;XrE^o5CaGX#n#;y5ɯB@֭ R<`69lKz/ͮqfy}3D~E! uGtHqi>p.|Q:8rYVv BVY}#:dP MUi6?Ē6 * :n8q1oAQAxT Ft6Oo}#]DBv/˷cJ{$k?S$T8 wKyϽ)?6<. ]$D"Yс,ŗr^KfM["[j_]?esW J\7H)6HhQ7ǐRxAq$l*voT>>:^1rXoZ2{=;*i.[^aoIUJLɟfHB >^ҋ-E('MƒNSD"H$Btщ]|BzG@!Jqc&qsn9*xlۅ*\xnhLպEt`?x-b Pw^v5"`nxi/ra:\E`dҕUT۶?yߍ|< jB>N{ˈ4uLckc̦=e쥭\/Ȭ7}+GƐҤ> xvq|؏ }|*</CENf9S~|D7mv6Jujk+mɚIQψqZ56+hͺ'ޞIk&n2&w?+=SG'js(n&s"6TmU-JƬb޾v>4H$dyD{s" Tbr0)s&]BeL-ƎiRJyG2tRLAQ 7^CTjI`CB>1c3_uOE=Q$<4uH$D"Dr\`ʻoܘT4ƪI4nj@ A?AfJGhg Mqiz'UM1о6.LߺS? PXՉX}CJ;FwR/#veߓXĕ ʤ\ :lږ0s2"ͪg0DŽ6U 㛪Mrx47Dvy;<4ȳ7^{@{j!>פJ[GFNh ]+Ͽ_NAl.X%ᮈn .K":â# %ߞ AN־GihbyD"H$#:ߨ@X;Q>I>S5l*>R#AVjB7֘4: A ]bST[[#:Ӭ;kDA&AoDG"H$!enR}׵{|I3Ŧ54GbȰ4"ɞ1 $c*-#:_FsheXhZJAQ'N+6=2T; nx5v|ޡ#?s_3*0Gn~a3#d p(SRWCa*[%% :#>Mͬ66Glc?dPt(e^&͐eg'*TCyxu{33CH!:tR%D>׮UCtHjD~%*Y~f|*Ʊg<ĥ֒ FtHȕW֞]_x ^qPi<0z5"Kp+ji@D"ijb6[qڗݯ5 ]Фs>Kdo.|]n ZsZjX=h]Z 3CUB[[v#:do'dRJ &!:D"@薬cロz[Ik:Ŭu kD449FAn~GC:*W6p2wf&PRvW&tU5ucbV;jH%NeɢU QS&zfvkB;YG{K f|.7RAeZa]!: ,8y8P+]B&5K$idfN|2UTF6f{#ӯѲ07Y{LJI乻Wi=kDtIM>&3;w̧E"H$BtNQnbϱ.5U{{?<.GmWENC3D7~`* B4I׽̺&nQN4Y6Q=2QCdJ!`$DG"H$".c;:{GYOp4N!5 )5sW_#M׈NbW!?|Pf$I-(C2cN"'>`щY,,<.U$aty]ڍO)fW?rLC C Ix~KdUItwvvs_$m~j$ ѡ\8_(r* H хǩD6bfnUCiM Dtm n;u}}IdƈoFiAH$ݼacG!ID7z=f: T9 ѱrz#֘/TC`$#x([ȷRPeؤ=4 ёH$I"DoҕwFOl=@1;!DJ=?^F:E': 0!l`ԡaT:J)I/ئ8D,ܴEe5J"Ԇ݉;"|z[.նSܚ2g>?%*ԙGيW&#wƩ\ iؤ=4 ёH$I"D8h9ムGR@qZYU$?r*nWyI9fNG`+ $#h(,MSPMoxxS8 < b`W( v{H :ѝˈn=y92@ؗF$K\"N̽4]Qt::Þ!)c!k3h2iy۫[<a8yBD1OGtnXbYb1H$"?bصǥA3얂X *_ĐQ;DLG~xDrT] m~x=ቻUy$6i8MBt$Dҁ1&b=3ث"(k~,d}CM}gh":A>;נe+m$'P]1 I*yE&O,02͡w@@d<-wR/#b_ }rG,,%j%N* ]O`Vnk!lސf-=CbDVfg(_O{<DщX;_C D")BtXߨ :|fw r˫8ź~c=LN4Ʒo,UXgؤ=4 ёH$I"D[w6wX̍98͢gNes:8GF": 琒YDH @Ue|ZV'||(e8<)q'u2@UzaqI3 -?݋L33mWU0955|W,UXAWGx^FpgJѕT4Y9?=sYW߄?#=g3WI$7Cf/3YᓬFզ?h{9;ˎY%7 {X<I{i#H$DEP@~{p(el\j+#۸>X~0Y~+ֲ#N̒p\i8-~Цj26%Wl%zwl !{XQU#:;ӗ أS eƥ+ _{$k9VXer`*~pqk(keBJU :䙜m-}ns=س {YiW3yNbD(.fU=á_}/X'?ZFg"WyGtg~Vy{lmhH$Dn}fm4Et;>`] }r\fY\23CQ12984>w/'CtiXnkp42vu=4 ёH$I"DWQ}%νz@,e.5d$ <#iLc۪n# oI Y8qH$5SbA`L:pһǜ0^@ZtwR/#|%I,t\ʇp}@ ؒЍc6ihÝ|*ݭyMܱ$ I/;[GO %.~{n8745vu\>Ĉ8/q36+,͐7rKg]<<-fï1yGtg|DĿB2y4ZX$D"D.G.nHI8pASDm6$h'l3Wئf:(]6aU`W;U~iMtFÇOyC? b ?D{4 ёH$I"DWۺWy235bUT0~R7q(Oa'ѢKD':`APC$^"+8?yDH-~)H1;~t$^_Fz b5g33^qi@GlבG\'vƠdۇ;Gbk3<9GJ|*K.UDt,%b\m I`L7:!1( b~PZπ_lP`gmNaꏽ̇ ѱ$rPBx.m6d O^͕ s(Sf;EmIƒ4aہY;qr_#D~v*$[X$D"D6n' %,fߡ]SDgoak.Vz3LidLoB?&*]@M<DdT\pعk]P\%Jc:OMc+>^U{u ]&!:D"@N0rٻ* "ޘgT>Txx*vdsoRz4Xd Y|Jpt $XZiu{X`dCWҋ@4H Pa> %VؐQ:c1D;D''u2/&Z}QRL!a;Xrr>?׵D1u}̸L~DPEP -*:xIHdL{,lyB)%cp '\qaG(Źk&-y$KtGtPQ"|~uA篳OJD"ѡi"Ftrjdѫ48t% I&jA!\z1[Cҧ7DGU9kOӸ@ [6y::MBt$D"љ(ObSģXC- :9 #OZ9ɟCvf&IpDˁCYfHRni [e#GafGW6tn/)7mXN":;D7{3wR8Pŭ,q"FQ>2ͲVyn2w*&PiD2UfaDm 3,qbÍXM8w(6ۈup.=!Ys~fCf1D"=2ͶS^i҇|~KPV&t.`;D(C265juB~y@ DǷ/]eI$D>/g u$Cv%Hl6 oM,\IQi[ƪ|:+B&n! YR2§*f;w|tUduaEtN0x៺&n#Oic=BC=4 ёH$$FtRŧ]e}Gus]mWcz6*P !*-u?Bbȉ7x)U5`, DZͨɦ;3 asHw9|\*e""NmL*ƧcȞqRxrzG7to @FtJ5*ۗlHn@JW4ziKƂU]-0<?v跞#'UmKDe5U]O$nH onoN JcUr; LRvϔ^Tiŧ "4W<{fジç ?{o˷?3W4t_ᩁPK k7>@wsW~#r?}$DOsdv؊L䟰rF4RV]o/g[%3;f n603-2-euey${"V}j+hzu{𷺀+uϯ,|nr^[[mvԒf iPu*fxSxn.X{NTM":<!>ߴ=x̝XPOc?ƈV T6\HyڽoʨD} ":pkR| bh)à=A4]6iN9Tƣ7hJ- FV W6fN˭yT̲/Oj"V_Qf/o_R":@D":~g_\݃~ѷ6cى Dt[ӓ폾y:vx}1ȊҒ/3J䯒%չ˴5@D Dt c-˃f{GíBU/!#s=bⶅ̮t*~k^n].`~׌0 e)*>uƘ˛z`˖Pcu/6It|ѣDt ;M[_He>~vS,k JTaFD[gDtGW!p-iw+4'DtaayDt/֞pպG/EMa;q7"mvnob|'ݣhwDbm#r"WfzmVߋvlws[Xd--xSdq"[cyP"{4-R|Dt;"4^ 2@D@DGD ѽ@fI^PM":" L!]W:W잽i>yj1EE5t]D|%fГ ##u(ܢvdNv'Dt&!ջԔIxҷ~Ç߹n}p]\ֳ|/{?3]MխLWVқ_ZOq{n<t8}\dkzOkus}}E eE.r-{'o6]tG]w*>{:6H݌v>v_v 5+,vx[NQ\mI 6!짱 w]os{ib*m4/\t<#zxuY^d^:R+JpDb:J}Pc| 7_vjG ųu FWVO]2FuIMM M=9{:]_ln T{20zGK.]Iq9EQW6+U2{lMp.65h70gvZlaRDeiΧVL6 E*ѻLj~$+/۞' ={&jL{pG2~Z8nR&iP^*,:x#0Рs L,YX)&̚ӉNSʥT@X8xDtJ)W8gShdcՎkP?F_2_rԍweO '!3d=aZ}1":"ꑫfKڈnn4ЖKSKwLKUq U-tU)rToX[~HѕԩXYer2dѩQoM+xw5Auu L"0` MƤBu**?H9w;QֵhS jGfved\݃֗ؿgT| w45PcW)ݼ?(:x-RN*Y6Dt*SoW-FJ={7xGnTov ȉ =GO"_Isz@D@DGs9Dt:tWo}vD4&7 lA\)>.ݜԂRX({[PXDȂu>$uZ?8Gt#Wn~>'­Ɂ#:ă@=`=3P,r23J~!PgͦZRQXXXvODZ52aw3o6r㝩-xO:PTynbWk>^ldUΨWUi"Sg) Vq &"-qϮJJEtٓ:ؔmiE}_>f|h'' R+\@DNk}G?w;=pM3tEP,IR`%ɐ$Iњ.o|W*>}>׏^OW 쏠 '1W'k%9g;Gty_L˭~ev{FPMJ~pTou|Δe)B_Gt psGí)'N& ZsϤ*1QjU4@Є@z~D=1CN+R:0]0YTSIsHG6Dt.մwIynIsiQ_y ;ѹzK$1s蚃՛wQl`Y&knLs0kW]3.i땷1*Jnn[S~os{^7 Va]QQ>mU>'->ګ2.jLIݬWzh|DlOE, Vdh:h8>_":Ѹg7=?k1ۛ>-J3e6Dt/ zE-%7-dLj,VWXG]؛_\czxف1;X6`S@2)P7& `":CVKyn!q{rE":=GOlxQZ6%H3"-EmyDtJqZjڇMf,Ӕ_~=g_*:F%H%i0]`Ft\h5q.4;0R#:=4 Ձ9(ߓ^|Œ)xkrVFM5:i˹{neI[ipN.hŪsΜ=Oz jv iIs=MY_٬9ێizxkэq|;;E0>5P.t̔w$&[jc R"w0)MrQB:.rυ N/d[^bxɄYԞ9(z9]0a$f<Ș (k"jY83balnT)ӳ[+&--+(,_=ə{zok3!6ONt0{M7]r4Mk}Eoj"N"Ѫyl֖ڞW*+sљ55J5e5D~.0NtעcՑ:Uy閙  $T2aRL[Kݥt #:OM=&`In4ծfBQ`iI^d66l4b ݷkpߓ~ڡ <ir{+ar^knIF켪)X\FZ{)/w}V~HWwS]Wj{V`@0"!Ft%Ԩƛ#.Ϥwi{OWKONobdL Te hFoFt23Xc_E~0l'*LqoiՈ]e@5y7du"65 6Q~8K̔Iܷkpߓ^~¨ج@D":Ft2s7|S4ӣ )ѤJWo}d¬QSg:90K;Gtp~pӗ&U-4G۳G!'#dVv]#:רz {8XXvW2|di)iVT¥f_Kw}O:DIi*EDt'=Qx=/L5*T;>:m^̍cřYߥ]o) T?hʟshZ(h^J4äSj3M8fbֿW_eMs.(Ӣn*%#"mZG(Wzw*Ex=wntk#V}?v?-vyVs&Ҧ:4#dVJ$xJ_3ܤ@MrUSQשoTHL'mOʗ jFu{nMFݩ_k{_/BʶVƛ I?a|b}.0/F`~b%c)}9@{pL& L Ԙ9̬7{g6сnD4ی@pHUt?FZZ뱔@DRс.сDt @DRe) EK{'Vہ{' ݒY>o^»,VW\wAO‹NWVwtcEt8YPi}cTTEke/ep7Dt +(];7Xޢ-ضo򹣽b{<7' ښuR{vdNZxh+^l={o_9v+s[xk{[,0ჟODI>+KO,v#Z2=Lv  كǵ2":ѵ&do޽DtƴPJ G.;%vPӱ;Gc=cZҟ[(vjӔ'5 Uv0DtIKDe{w.?UǦB مIsvq΋svMlDt.@&DtJ8⃕Kg;6]}+1E :E#:":B.i9.Ծ"1m©}ۛNg[+p߂̑qؔn)Cعi"Q% ^1e:3fqN};,k(bTiune-y]PMѾxͳh ŽbDhfanQboT0]];vyܷ'saؽc~Żc&EOZ":54Y7B BDƒ&;rw cuLtIexc{w_5 w@D"_K":O ":.Ž螜ִit'x=8j1mrPs]jWSO/)}{:$N/ɾct߂˞̑ǧDt'B+ƙ?9~pYyMmnZ\>"[M>wo|)\WRZ2).Vč& 󂏿}WODO֔6ą^Ln@Dwy_`eW;CD{aGtLλ]wԳ?yoY|8]9,V=IDGD'-7ev޹1-Ahݾa۵w>k^rO+BgBD":":сo?]|ɣ9#p)uqɠǴ>7ms]^i3cM[pߓ^<| ΛbUvnBjď_~Ɠ[RO KLX^D?]Vvl_zĞ?G}J4jl*[-Z`;e=O.0&0+l؅ƤNGuGWƉ:/8xV{^_]<>˧?}sM[[tu3#<8O"/T^(^nrPEx'Η3 taH*23Tز륇>p^ =]O7d-!KH:?ژU#u/CHMƁt>=hEGrmٛgdnp`1'd~XZ_F].ѡdӞ+&IԒMa6VT]ul'LA& ?|Geɒ%Kd!sfW7`PwP/l )& T^c~7’!: U~6Ӹ=jbP'%@2H@CalJ c ^ț $ձCJ!,Ǵ{;/v̑4dgT\Fەʑj'`>DsW,c#O4dMy>@=n; .kܓHpؚ7/ۇ=yrt,ZUD7.K~s h'nz{ QytG7l8_q7_ߪkսegFr. qy++%_zDg'O^M=jC ㆶ+ּt v׶K[2ll&~&U],Ɵ?QgӞ?Iq^ڋsAs(b' \6kt\uHxUC8<ݒwL {H:%[|L [Ls_ 컱u~:n^8~ҚRai!2^ѹ};&a.hb4_ τs?=gKX-Wgw#iɒ%KYݦu:BtWNUR<`QJ)~E"L"FdL{~.xW$3\"̯΅ѥ;#`ƗA⡞.P2;Tbeį< M9>K2LDGZb% 4q#i[ES :yxĘa4픙g8p]N-S(l"Nݎ- gZ X=4) Yp/?ѷ|?Խ}XaCh |!@ C4~@_QC֖lW zb >C=ܶPE2{P 68$^{#I^zи Ы#ﶝK=t$ a$1oBK{ysX}aa׉|2%ћvlXr,(h(%ڐbsAzs(ح\mלs?=0v&Az+$_,Yd!:K`H:d|6K"wP)co¯c능 \]ïsƤS$FjTn_;g" Y2`6WfC!R{X"dJE*vPCrz0`$; k.I(5|0 mU ]FvN`FyH@]eQwWhUbT/䈎&}S/8i5NG%fu=щs{Ii.>g34-!QpDV;(+?غ\+{+!`60>WO|9QD}CKӣJ%>'Rc KJp=WJA1,Z]tND<꿆[s5 dz|bI ؛4H]-0xd+-WSL uT Z!PS$dmAPH*\HP DG\**p 2ؚs.lr=Oިg48=#dɒ% Y]PTj8؅Z| 8 ʯH.tL+xs!GtTNCU Ӹ\m*B*v!3A=9w`ͦa*XfԾ#LdbXFSO1؃Qkr{`; Čq>(uVRj[#ǁR6Y[=z>4} ZwM"MC;WA(-SjD=󷦈U+wQq~]lC?lNCMB_7R j|5̣>_{U4 Y)O[ 63JoLs d@tP)MkR01st7A՗K.BjjnSa{ț t Qԙuvx*BUB5}_Cx iڐIDt "6gK&RG2Zt0-fq{it w`($Ɵ\!q%A%T'j>KM\L86ID葈R0i_0N'K,Y% VKF]Bn iPrqi, \PC#wW']\$lZwiOP0`e=<߁?rDGQ[.7p_%N[4*UWׄS*HJ|A= w$;45t#`x&Gtf{iڤ G_| kT!/vӛi3(:ME (K(A4WHVgYIo10E<"1ΉMSbˎ ނ ۂ@>=9W)Vb 5 $3!`6Dǟ Bt w Q ?q2P>R9&\kHZdɒ%Bt,DqG}^":bT|O#;C!#^K9P >0Pb rmT uՐDC߁?D7E]` pF]a$%Øhȁ=)tR7`Tv$g|#赣Hv G(:݌TcJ =l,Gt5V Kښ3x٢+ !TK@_4Ƚd2;u:16:<6 ):EQzˬ_:XUI69-.N@+тz Bt-^s=@Ol g?V#/K#:[>*wG?ڐ\krD/٬ۜ&l\ibǬ1D]¡ =4uXzt`yYz6^*nW170m.5"IDt(sGt0@т1,hrG؉>!:dI<T@j.lZQ%qVP:SA/IK,ŸΒ"WKN:J EUK`FUHF` 0W3u}UvYgU DK4x)AtYIPΐ :5$ BrH9 I)GtX *5Ch/M<5ۣ(5۱刎&duPL$At+fO&nS_B6r3`@-EJc%Tk=aRu4#:ԳD$:NnO'c|I12|gz*yl7gS" Z29 W <ۑ2K3;_tqh/>7gU\+9芎@M%%@y!iED3a :@:ڏ@2炿\JTʣ&fmJjBtf)yaݧ L1߃K]أ^E%K,Yr:DgBtH@2At)י.sGr㊥2z%yڔ]IQ2˜ <&:&aWmJ& жCèwϦGI{޼5a DG;E'eTZD."$;pD5و.[c Q9 T Plѱ$ʃ wPP7+j]8l}]tPS.TZDĕZ4BMaK]} 'FtYۣ,.V{kj.5S(WqZ6m3=fϟ_s[VYͻ@AEۣSkdĦ"g23#}lS䂇CVݾpChPȍ :S7}0&9j¿I7D;-o$fK(~̎m~#i*DP#mr"茟 z S4hqT.fw ɣL{>&D֔*yɬۜs?=_f9xX7PKb IK,Y\ΒO` |fi 07YU0v\i2|Zeܯυm5VvM #K$ ~-$%<,R[[(=߃!#Y|4?PJ\gEtT ҎeYpӞ-ĭ &A(LO0׸'m]D qn%%6mZI۷Eį۶n eD&>TAkNFUr;0#v XNzv*_qn* " :=gfA fFtMCnɂ1O ьِwQ3#_G'>脽ҮS]DtZDXm֪'1en? ?&8(YI%XH2&?VLY֜*Pќs?=L2݀dɒ%KN,Y.;yP񈎂ȥ \(.{qQ?2o~.$x,5}fLÒ ٔ :Ş߉N|D#QHܔ 9.WT£ʼn_=Ghq>0ݢd=l^gEtAv%KWu{(Oݾx!:vO@"BYzFn\iܙ+G)~%0@;kRZAd;#z۲Ԗ@mx ڛ34dk漅C~%q:ۛNWʨIS,dc]Cۀy%OsѩIQg#5!R \)9vADJYMU42N>uaIIIEte-ͥhLԃSp(. Dg\w_| ]LCLmHhus!Atq:uI L;G,}P:4FҒ%K,9d!:8ꈷ$h䑫ζu#ff]%9dcD \}ŊْEJ˝! f;fSR [F?h#}Gti1ʽ;T SGjZ2l-jљy.>-w-8OGҒ%K,9?d!:> HQk@̌._G Z|X+saPT( M6<\P6d4p_݊Fb?T gh\Gq 'Hwua jN!y0x(. 3#:}6uUY>ro-}Rٽgɬ>ݦ'يٶlY]QtE>BtK' {i|zDɏ%{_hxI~U]:qG;Wrs_}a:JJ0[3!gI3& 쁏{x#tOP\\u%<l#dɒ% Y/Q@4\\ⲄW1[@*E/,S7M6c|c.Gt(9Hd>אw&A#ċ\3]`D:7*UhnfQk\YwWȟ%?F,br~RJS3eA5AW2اGOϓ,H |.WۋG]BОhw7?.x/TڄE,#+̌r6{ CP~^ `M `YCm3=*< SǕqJu<Kl{Lou[9|BnQ Jwv]Z!%D[gڭپ'YI5J'?Nԙnz6ᣫ$v].|o\_|^ѾB?I_` bGt#YX|:|s?=0fSF[|n H:,YdBt,DG!Y3YCVW{FAr$'HbTf;!%|<+sa<[XΠKg;pfS(:hqN)߸=^C$$q\~4`g& Jb,%D'((" 2ушPk}eHJ0%K,Y% J ~hr$OӶ̂5qʸ%(:jUbӥгl.݀- #7,_WLnt4-#i"kTMԋUKj=Jdu8ߖͣOd.-`*rj< AٽE' 7 oM8EIkMkHWÒWkk,ԔF6^#:Iǟ?T_b\<7o^ jK'Ƹ.P 6WhΊ(eU mb;xlIUdgF`!Y~`8D7kHiޡ7G6՝O ԂA~|D/]ܾȐdE2ÍFtڗ  ,I^E.4pu>a&#4D gߧ?ΆPUVvH!bZ%c.끿/y>̠Ds4s?MMB.":("^Yxq_dFtxQX'U7`$K,Yd!:KBʦ7tnTs(< !xW5zH$m9"B2*3e*4enUwp_ TS];gٔ#:()zaSsaçQXlآ+ΌxP0 mX?F,Rg-?c@k갴9XOT9=#!:N>DYk2&fxA0kK#8Gz":DQpZ߼KR\jyxg>d+6W +Qt#"Rs|\IwtQǒX3XAk.6`.끱/Lx>G,iW|b.i) :lc:l.%T'B7Ƣ]ӛI(ufι)啄:7b$M,Yd!:K#7 *T@ZТuGʽ j) iNƒ_#r3tQ6Á䷥( 0Qn=RQM)#$*eC(P #Si02._{)b$ H$VC; 9<Ӓ*GӪ;-z誠vs_?CƁpcN P`B{+Xz1\z :?w*"NRr̮.pOTLRZ|9uWұlܬMH*qcPR9CoNsюsYvuk7=߹,e"?%,WFtP ̶ׅ@HD-OOW`8C~^!سq@ňLgפS59 Eו'>L[}du/ZI@Ԗ^3&d(ަF+emΕCGv'!ss_iUxD )<(Dy{L{#7>8 D4aSz3&̎mLD7u[emZSf1;k;]љYtVKSK$bbcW!{ko=C~0̄_#=[^S`0r*x rl~jH J}ϵRU[sƂ%? $n(4ԁb(Pwu-[haenPU#jsKV(;w엃W~:t׃A ]ec[s(?ȁcp3>Ei3;rSw!~{׿rMK0`&r3Mt"ך$XkzS=B?7u]'v I'0 # y~L C 7;2hb_@ s_/|)͝G'KMr>8ԈnFEdt`M%w?T ]8yQlm۶m۶mۍQmc4i';{g^<]Y/j($:$$;uXHv[9Z`-Cw5I]ٝ<Ι=ʨf'nPܶ%;X5n-I¢iaH^I}n+kк]^KCΦW .c~tnF]DeqKx_2''' k_Q %9B&9kN\J݋CWщ^Eޕ5Lz7>sOI4\1$$%:J36-f@;,tk3[ڦEѵ3eD'ѝ8Xll,Ib-LRotwd U$ْDs®ц[DWڟ\oqItYLt].?<} GNGZ1 (g-uxhK1 ٹef0T6Ml7 7L@APaGPa$:$:T$:$:T$:&}$:@C@I@C@APaGPa$:$Rʞ@z8lPtXC`azC`aPtEQtEA(:G(:EPtEPtXC[C`aPtE@oaPtEA(:A(:E(:YGl}\Cb=\C  n.,|+TG׼e™^U}Ͻݷ߫qL4{u-\|gZ۲@Y pl儫_5$|GpN¦Lߔu6Z9^tgW4~@o.Kz?LWYS5'oSZցIwS9 =~:O:ess )h|چ:hUsut6QePz2BfֽqrTI[U5$E݆GFylf.K6:הo_=z!=/{3wb &M?jt#خ6ӵc*?eEk GS^T߄Ж i}L 6+D-8\@'GPN6,O~ ?vƨ ,y暋oMAOloM]@o"犪=a,ǿ텕-`a݄q׾ST4uwIC "ظ +i{(:K1s?==|AЛ:,؞z)v7*;9¥޶Pt`ҝ{Jخrl_E.ǥ L,ؒe\^r_Ԫ/cv|.7P+DIEVt|4 jpqN8ٛ_>{sJ?bPtsYl׎ wB9v6>E+t\SߗX:LE8?:uOwaӳtoϝg~imyxEukӦ6^D\#*`M/ f_0ދG2@{ZfG_/p~GˎX=kWTq ={OyUPPt\vQ۵眽Aيv _wCErK}ߨ)*:EWLsj*I\Xvc:cҝs:8[•]|/RL59?ڿԣwmB_Sd2{F_ <=p~G&Q05ɢi׌5n:qBGuQ!bmLJ%;7B8?#}\4!'A%\'zTXvqd/8(:0n\^I-۵Sg=Ai3D7WxJkKv{.i()vδ%Wh6bTW=iL%ݍ|Fi| 2| M߅>Jo!.7B;ik!6vBANJQt7R* /nb|~Dr43H5VϽ[D~/8D#Ptn, Ml"CUrs[r_Mm*(Sn]=9FAɽuզa}j-fվT _FD'w}LCwѶf IKڪKL_Oh9a4/A]m~P>-o Wڹ sKNJ.cʛ@/$7s}+TN|> zI+7?wp)zCɚu- #N m?o#HH:ȋekT4]Uy"v\*::Ȱ2;ߒe\ql׸?=|oԆE(Gv*AG0>Y4k:_LD{ul.#hI[Qt$iϻ5BoYYB>߅:rOuv6qn*%tsRt׻[}!,hs}:/S#Z^ꏿ?j …Mq酷95E tDgsܦ99x,+U-|#X粫.h-䅬PtU竁5 HuNSтao|XCQ~%gC?E|6ov/ W˷ЏҨ6c;oGZ=O;li(#hI):yktlۢ#DY/A]`~ghx^ok:ոݴYnWtyxI߅><-QL.IwPRGڊnEJǭ;ai Wt|땊36rr|O=^`nsdTOsڿ8lW*mah:ɂ-v:G$Wr/jT]誏|WD_I?Nײ\NYGV3+7_G>Qo9|T2+\b}7-\yK_uݒeЦi/A]p~g&imYLvDE'BAN0W1\7mPg/ټVhn EW|eJj͖]8}9E[EZ1.EG O~[Z_*Vh:S2yu[ZFoxߪB\B>߅g_j'ȵ`K.fݮmpS Ttw:ROP|\\lCZ%KX5vY,<\X(:0Q$`b|#yٸ})vΦ\hLf{-)ZTK?W>!t[3s<.rpKF.>kww ZfԙPb:G/hG xzi`ӹ>Ԙ˵|~ЯU3'=Fu?A}!AP9n+mcKw kY$NZn =иK?bȸ_ыI1s|-Al`uڌ_dɅ4]N+s7f#H*.he世}k;k "בǭ% Ko7Swڢ2iƲ=wTqkEqFOВ6/p3:zn\v43hi^/A]j~hr#<{\ *:A_P5՘;\-1}B>?#XV)Z|OmXyTb}Mg8y 7}b|@(:gւ l׎| Q}%،?z_jK9~:SJoEj|˾+ڤtӊN]b*"#Y8w8 }u(* H넳4p%mV X*9pq;s0R kB>߅懭Fݍ6fՅb*k!wE&2&B>?#XV;r4S?scKn֨>E@Y2g-=˒xM.6%~EJljalؙe"9C%eDԙ.1N~N4ݴ_st:?ꂊ`~(:ڏn\˾i۔¡s W<}y3.#6T3 Rt ~w(1})==*W|&I !v0íhDT0%[ҿ  t#I-O'`=r_b(T޽]Mޑ AX/A]c~ ×:ި-~]bY^f~<6!=qØK B[ё?^BSWgѺEUAAЛW/)pR⻗,偢SЉqGϷ:2pOSc#X>-y8ʮ>4>舸c"T~يwBgхײ@ۂB|KB}EiXaEJʌ^y7Y0y{'IitJkA\tZ-ݎrN]˶dH&]2S g>uzĹߺ~TO€R^^#M,S.p xPC|PGJmhXN,;B_ KI_h+:jI7Q_~",)F}!D/ϳv):"arc3۳~yoi;E@Y7о3?ӦϐG8< |cjQmknO:mEGJMv )е^׻T- n}9*lJY}xmT6yB[u0l`u%mVrKDNsy_ǃ@)7Wd@}-r- Y3!jjj$&jrE羐ԠtIIw؋._gZn}{?/ "(:g./koGN{( {gVoBDw\:RCP][ht8ΏJIg5^n;'?.&Ow:6\T^(O?u #j{Y Kpǡ-7# -t%-? p\g:lBwSrazd_ D6r_k=?Ix<>Kis}_p{R#t]8| Ft+:rÓcE$KI&V G'8+Wso׵/3U{.}_]ŵtٻ 8lb)q0o9YH2s$:$:Vnln3gȘ|N1(gXؒ#9 k3iud{*qibեŧ$>j*{\۵o8X{IrCAOExw#zUiW*Hw=Z˸[~%: >fhlҧJ{ {.D5m[I{ }61 !n&N}&s ?oSMm)6Nzlg5}G<k/Awn?ٝ{Wubb"ՠ~2}]Dp[5jƏ_B5s]5wLAwߢ-߰k\rg:H&{{־GjޤMnhزcKt]XL\h6XobcnXL$BcUcnkiRWƏ9zg_ D*u+f"yDWݯWuo{| 6ފS[*]}gȘܮ}[/l:uvރǬٺ/U閭ߑ5VoDWggTuoÖ?дmťh&WlVzF_TkϿœv]]JtFώIXP_r&&aи1OHtHt_b7%OkDK1c?o3DDK# 9ҥƭs*{=v7LR}1k謘4Ttv/mOTԤMn2%* WIj?.]>$:nc;s֔d1Y<gފ6t.S;ׇD'ѵ<-xoO&K9}uڍHtHt4nz]$Z! Y{.Vdyw)%7ܲcojw^ !HtDǀRu}aϪn.Ҭ]cƭj$ k<6saI!3cMuepcen:/HtTiOD7{uv;O~=]?DDDGaq]iVzItHtHt\Xg@hز?D׵P$:$:$:Xg4DDD+/ENItHtHty?s9$:$:j?FN'W`SW)=>=w {e-ã cR A o|eﮟ8.u1033339\3-Ga&C8)33333n**ȺIzgSZw5JHS$:]˩o>C9_gms/]Ɣ{~ j\2jfњ; wZ}-M5~Uy\sKNC3755 fOR5;Tdc^y;h've.Htŵԓ/SGc)yC:1y=,>Ws399|Uj뿐KQΞHn5$Cf<4E#EJb: nvVVg8~K >ӤϖoI#M7B-G6k_FN$:܊ Rv>nO lsdrIS$(љ1<ǯ[[y+6wxMt'n{PIcc|עVÞpZ_vZ9^ :M?v,Yeo9y%@.}nզ|In;&t+d~HS$h0t܌W?No{1e&b9 _th+pp|䱮yUk;rɜ WkQ.ׇWeDcs_u^`۲ \]-1$iM9 6zLPN=D[[SxiK_ޕ,k 9ٚYn- ]\FfĶ4zwLT.6~齆ȗ5L[W[`˭*\T[*uNe<k46s6aϙ;Ն WkC$:ORѼ_^gM+|UWJr~ZAr{KF* Ht6DJo_Ŷ g;9zJ݁WpfQk{ V$MV_xK9;ik0{N#uMt^Y<0Is=o~BS 'Ik˦wY)bE<&=nEMRѦ/yyWV u_=Bη'ue> ]:qrg/ɘpI])C3˅<ԩ`. {zИiT9uÁTJ "{ydp ^Cyu5X=pE4ÀԲbrW^zݏ o ]?zw9XkSv74Eo|C-ׯT^cܯk_ {O>@Ӓi6qwr_kT#+2rLqy[r)h3V[C{Z}.bS4}#+,9 c,*o}ٷ|=5Ht ё#'Δ`r:ўqdE}u[; ;M=ҿȎC#HS$:,Ȓ#KL0kI,:SI&R{P%MN=kgs'=N$+5w X{˕zL=\؛;ѳw@.2zxүs5UxK10#ؖϹeU/kNW3 7̴>CǷD_TP-6frm[rݜ\AB̕{d]bq>Y=Zf._y?$:,XQ7.nme֑Htژ_%GVᡙi] o9n˾_gINMIK\[#.M[[].KK3r}25xon/Kȿb сD\tnJNWjȻ6{蠖'zq'9z"h`3>p>эd+ޜf+#z[j_}zoQ1E$:K`S7] #'͒+|)E.b,]Jao|Gd$V;$Ή[5^ɶK]ג\.= %km BV_XF\ػI8q{{lcm۶mضm۶틝NQ-K~y]yMGWgCzAIo 9!>9vQzӽf8km&N(#>Kӭ7/3\Q:i /)A?Z}EYT8F> >p%Ht ѡM[am2Ù<Ԑbn?(KtΡkcJzwٟ %%vȈ6=,{."QIwtFq8٭s.2qz&ٶpL ijHS筐%M\mP..N|$uWY/#JsvO%v"Un!>bUO[a>`ٷ>_~ONt8ʹ0v}T(z_FHHKv`E2Pa@ӂ.?<|])VZn Wٹz}ٺj Jj/-a!*f!Lt{Rzmwp^Eds\>$$#i1;O w ;i0 i!(z.fYN%wcu-ϫhTڪ{NRXRzC'rwǒ⏜MUg.%Mi}8Q.֐)j뢟tS}$Zh:F3sIw}hpt^^}_aqB]A۷y_Z4q>4{@vY֫$:Jy\i}GnyCI]<N[V?`*1ªuKPҔWCȋ}% o}┫7l0m;pZVyqmθyN r\t+G;X'40!Jx^g@ف[  !խKB3\z].8⦐4>O»_/ie6.fɪۡ=l vba1Qrj6D'䎯Pa@ӂ܎2f]ϤVU\\s[M %u3 79;$qUZ]YCG}su77v>r6aƞ;jgq8kqHm+ٶm^mimnV?䎾JSiy 7b~~ŗo^zM $.Xukw)\j#D!w⽆siagGC;/\ͧ5NrӿQr !!M!MPRO?FD`si }k#DD4;ҍd3\w͏o$~=x΋/n=;te#MYiHtHtHS* Ҕ萦*L#!Mi !Mid=Ҕ萦* Ҕ萦*LZ萦9rנi Vl?|M/_s#ZlcM)V]9ԏ4ٸ%;_ y$:$?wֆO %W6wL8z3J1^z/&I|ov$ͱad˼k=+ ٳ5ΣY*r8#BJCJ} e"]vw`Ykb1'4mkQh0bpIɾ簡F}|B:])=揉Y=3vmiqa1%-OMO6~mg B!BEGL?.I^ԛ: %CM>a{6.C9wyXW$-;2r!,^kqNISt#om*WP׻dp8kuk~Eggnh117DO6΋+!K_i4g&|B!B!Tt"j)ζo"&h>`ؓ?L|W] Ժ5wH֜ރ3V9vw^p#g-G36o|t{_(9zܬ51kQY0'-Wt!W"鋖QM)_D+Ě ;F3B!B#Tt 5QXnAi ͓+']S)*"0dj^aǠwB#58z~ ֓r6+stx-47mV #w5wAE\hRTr!r!c+:P-zwx7I?pw53B!B#Tt&ȉifD6FE8P"H#E,E/nIRVjFjʼlD*?,E({R  V}oav?wF7ͯfzaQTts2]Cb#x ~<,{1 %JYԴ)[+VT}ѰՃo!aܴH!B!P*:$W1Rt\Jv}g⬴'v,gTPM*:dZK-Ttc-u8'}0T+w_2CG osg%xX>%z{QڦV6H|eTt&dp(uJ)gS;ҝ28yEgJ>nV`pɔp M B!B BJۯ:Y+1:.CXmgY)l1TSxH):\k!WHA&m°ϐ TW{<~N)_F" [w31'Ca/آ}|85{1 %JY:'E)be-7?g B!BEG̒HѭnZT~.mFY巨(Wz_11Vƻ6]ڄ`Sṫ{>0xT5hAB!B!Tt-ݍ2e"k"Pq-6US[RM>9qMlZ9E8*0q'v]4|ꅞ;OV=]ѐ9g|OvAı>ύߨŸQ>ILjdW<Fx_}{{gR6Ц]])')q}dbbPd`:C]ţm6sA" unm5g-2rQYk"oؘ'=~2Vmnթ>VM,5]?(?tE_Hh҆]Gxxů8B?.~4gG P>Ggc_3<ð[)#g6394~"|$q L7TJ落NcdYC5>Wt5٧]CvelgֹpI `7K[o|E ZB!B3[l,]zeA꽯7笏,SPW"%Lp -hFC?ԧz۫rpuy*>m6zw-m鎚u9. f=ckF(Qi+613eO# jxz59+4;ewB9vc|vJ=dM#འ|;+}lܝgbnUt8%m%uTglRuIe^wWmދjNwq^5}շs̕ 3*F"'6 %Oљo_B$4ʳp\ܝNG eb>S-@!B!Pљc2p~.Ď)j]UUTJp jTTCE7^`P6QK}Rtᕪ^~n_ i'V͛.xxQQSkCV)O6b, |)5z>YA(+]Z=OE;Xt癚; ;wdJ`J( 8˰ps[ٜqI*W߇ 1ݬ_:Q >~`p2X1 -`B!B!TtZ8eco%K!h8 ӨTtY&&Q-#s G6j=֭aPJ \/(:{CT݆/a Uz-D@=NGv P9ՠџNG*P> UyG.}=\LZ+7Q/FފNMOR*hmR֮.lkDD9tOE:m8/QONUWXT׬ EGGÊKiwH0nPGQ4)Š HNJ $4{ =;ݞɼ1Ʌ ҩ3(9(y#^4 %UљZ8.mt) \ύW $PBčmg2ֲ[x{,RMa 2hAB!B!Tt4b8{\V:cl%_k((Yؐ M2͜ӧ<4*+TS;g6o*6?] r5/(:P`<}6(=܋Й2S*h<[>E'JsQf7'%>EWP+6~9sGޝք-:_O^>W#G{ ^TI<PS <3zlԧڬ`rNbz򣜲+[~k5n0EffS=vh!Q&;ҝF1~LbХ5pEg>؋nTh9=3>^'1[Yo@ 2B!Bja>kgQd~hX_K9j|8ZJvuK3Vayj*`P-Q[zE'=G\TgL3J^zt^D?C6`xH-U6}R&m ,ɫw e:vQђQ6%fmh(G;L)BE~Q/O}' D#:O.ZU:MہĚVd09'MAɚQj[ZBu;}TC7KMX5%B݈e$n`2N'1s^d:U 0Rn;hlu]LkP'a]Vtp=24gxcd|OCaҭ?x#TԻ8+쵹*(8P&i09uχ_կcl56!FEEW]r616Mڷt փc-;2!B!*:BEg7Kw5RtrIZwqUm֌}%uYaZNX%pu0LA5@EfMX9$l5zE'vE躧3]_b= Sv c9 v9[aad{Թo&ڎފUY;rO~OEwCoCOiŶOiT /1΋_evj%n(Sz[̜ k°jWt(?[YPU{6+ΊRT2XwT\+3B!B#Tt&hr .o2>r(s^U˻xxOO@E|HMX%l *E:/YLH4 ~SUP`O=b9R"Ѭ[ѥ\yZ}>ݕ/0?gW{ vg'/^퍝Gy? fwUUe03'O_ V%EƧDq2$}]soY'#TtV:#$x})9k+`=go>TFZB!B3F~nKLQ.I`E!Fk?E ؐνQM*#kZM#aZKhTTETW^[vGN}E@g9b,F+:U+?{n #E PnATڲo=qXSإ/7 WtUtk &ѹQ2lܝWTN='0|HQk6|aVe01'{%00l,eC˅ wC1rTt_ѕ)RE4{֡dƭ_%2B!B#Tt&5 \wyCjڶQb*cXzFI쮪TS45g $)_zWt@)ݻ#5Q+[N V[,J6^pNO2NQw]YJ*Xq5w"1ߺ |ʤrPc5:*񙫷RzEc%6*+2ܭGJ,٨5> Rts(Q8@tl堢 E7f`r~+aYe䶴8 2XՆg?˧Ynsk2B!B#Tt&\6hDP^i;z;5cc)ewD) V+3Nߗj*`Pс61éIh t0Pt}Vd;${<;jE7WdRwG*5nTU=}TD8i]UywK @CH,*_rl#qVj,3Rti7}/^냑*k)/j0Rqi Q jP[GY+Î}ʐɫK3O=e -FBRCVd09'K^b&J`RrA@ڄ]0[)PGNT 65Qt*EMvqk)h3"srp%GYJǖe B!BEGL0^%`S`7װhWPϩeJLhl"?v+7YaV?.㪾j*`PAe{˰q3>?[PKsq\jE~FnjRu?}@By7C蚋/x'X= O',>{CPM^ѵE'{ W<_x9K/^MS$ڢns=xvT#j#n(#[Qt7A\)>]?{"9Y>"޻ǧq"Rt9NU'*ɾ&$mG;[r?)tj'Q|?Ú B!B ,e;lkF԰+:ѭPtz߉AEfʄ֩㍤T/v"E7ތIV+ 0&PwRFNǿb_vUMȌ:*`+D?e^+:ń1,E1odf+7c)l*EP~~n[䜔o0[\NغaƎ`TtjOJM(1(wB4<.*&d|!_A(w>!B!*:BE`؀Z w^J䉫\gEOa?X˾߱XDg9)*+:Xka{6#^ftog@ETMq-Ǐ#~SΰHٴ's*g 5B:s nQ7"XGإ;P9|@Ҝh+v5YspBj<# &z$ .;-ANx! jBP6}P茗{x(_r? reg&r]\]Y;3l9Ԏq+v Efj72ؚ`Tt_V":F^7jװNsagr\8fk3',2ბ9;౦>JW+2B!B#Ttj!+`754Hmy=Tucd 'JݜQrػX8qrmsm6k<۶m۶mQc$t_/;gw_N9tʚ%^Gv!}çt-kƶثζDD4ew;`Ϯ߮s2&juvNlq؉gW?(<跏]~4ZrW WPh-[[@CC2o_W{OZ!iMtÆ}K=.\e4~/׼}ipՖ: V4zJ"[@CC2+a2i<YX0Z- Ҕ9HtZLtcFrծ;⏪@CC28Ws\/DwyWo /Ҕ9Htх٘ʶgLDgn`U}GϞv@^0``O$:$4T$:)O$:$:$:$:$!* HtHtHt2 @;9h0MC$:Rhѡ?naUP4j93qޞi٣T×-fi-3qV$:$HtU+v~_r]Xq1')_.DDu;sg^{M<tM &S}O^C.69wHk:cبc S3HtHt4ocw1]t-|Vzߖ6(U$' 5ƮvU7gj&wyԘ)s%:$!E{MQGDD/?{;Gvs[i#j4rTEt6EVxz^n[֢{p}L=S]'m.[PU˴ ?ߪ}=qJo]G :E`atJjG-;MT3qSPt1ѿI)i"}Vo I,M>3|gڇ~XUC"&y2vnE "~EߥX6l DLN^3S7#ķҸiM0)Bmol7+KT?TH?wbnwYd. 亶IQjC/\\fj+3rX$xXlA*-7Tij(@(YIj6jF,'XzՉ3WE=S[(foGl*>/m:ԯ (nb™y jz+ʙ?si9S_[7yjIX+:ׅkG3T hn^(&4{`RuKG)fr㿿\wEPO|RYWF9C[&&忑=vz@(YKŦD%fzڮ]}B`M)x~(vI%ilLjM:aNtu=BK嚐X-uR̬*&XN*XѱTQyuzf+ M3\5N)By:aNt 拋-x_ҳ%c錴bn6%\ոɉI=/QS4Wt)a TtU>b|K{MV?Δ} _@E7#6::u:Eqq&7(I"4ˤ uqI) UȚD+4G\*BS%9FǹinrHfio(K2kpc~3\I̴*pݛ<1`+_},TJrX`#m'BX[trLGI]A]_U Tttxh3=]5ZVG,Vtͫ4Y-Qa9~Ỡ& b,hiNR$\A N'٭8/t.|c.ϴPt7gѭ5Oqwbck(D4R)ŧTI{sJ`B3_!ugRӍ˫kV!|%=ㆿIʑc.e딼s+ѳ^q7 r,sO4ڊ *:KzB] XϷLM}՚ XvM*])(:Em [ҚO-pbf?*]HRˤܺ74efG9LHz6{:9g,E{O1c|+y>& k[rlw.|ޖcUunsb9-qjyjhT%Z}vْ!IVU_uQZG4"r{\=)xF6t\ji[4|55_~LVϲu EG:nJ/@3*i%=âSo}9t{VzJԼX0BOzcnX9p'HnѼJa %sc w%63dZMƎTVwiؘb7e~u I?wfTKzrE7.3[?즹-{vjzȊџ9/J^L=u JW@C#&$:$:'y$:$$:$:$:$:$:@ACCPaOCCC:(mJ]ƛFu7{ G0 &$:$:si萦T萦T$:)s@Ci@CC2B*w3l]/[f ~W7;9tҕ0HtI!MPFj[e֏t;w~}d1:x?ks3W,YX@AC2\5xlt? { N^}M{OZMxok :RIN 2j3@jCa=qT 6ZLf뾽p;~S7:hu<Zӏ=FIzuⲹ5| e޿@f,d ѽBc[n{268s~։CQ2vs`J:zF0;m4 ѝr?}ոo{ G|DWnMk5(җo gUUrNDITJmu'ItD;™͟-0Hty-3?(.'{F3ž5[%R {Vn-Me$ij8|.ٞoaϖx_$:B;|윿I!|׏'fHjCJ' uAf- ]K5%ɸ f,Kca]lDװeu;,ٶ=W3 H-7fršBzѺOo_P+[-C pp2g-u@cβcK_/:2333̒I`&Y`怙œ0gxmWGKҕG|vSJl=޵sm"նlIuhp"Ƭ~jgzȽx::q-JL@sV+{4K6a=lzioEC]B T?r]'>g?b1&?;8n͢I[0m`Ek62X4~¨϶]p6HYO)x_=x#msF>qNO!c|$;vs \so = x 퓖fz5p-[]w^u%;WZ3ӏhc6sI5Na %K0/vtR 8s𽃱%TxUc ?u{8}p`;:q#V>bVNos?,e!B!B!P&Y,FmkN} Vt(9 WeT5EEGE9E7{k z| 'c$_rE7KF&n-K{flBk%<@b;yRոU Ӽ>1Rؐ+90Ndz5t̨#,XQ9 )bvu[mjlb7jzO^Z!^ 3Fn7q ==u`zyLWRY2)CnúkuVw9`#" -#O6y%f`D䌘8B)*:*:twC n1~ WvWTL;KtTt N_h醌!P St$oWI;"9#&E)E{_& puDA?F놯Qgf,:qs0@#ÍZCv ZA.SMxRh-p '[&s~. c/ v *?u$=Ǝ֣ؖ͢eS*l !c b!RNU4&־߫&YP"{ jmD_@!V1bĞ `pUuDOPB!B!PY%/) C1w앻]Ru?xsY]y8EP 1F;Sk, UBV)vT7j<WQtԍ'hJgźaְ/-0l.\EJ6:*ϔ,_Ԯ)ev=%`5 !G^N]bJM|;}~um]C%`"<58-JL=Nk6h?gQ\lj~ns,0;՛([DPO+?lDϱ &S߃SN V3x#ǁb/WxZ ѼhpB@F&x-!kPU& Yvfs/V=ر[^@X;/Х[nq0kl)A9ķVzSzj B!B!ZBEg-mcgno6r] ᗿ-;^v9ۦK `UrFٹ'/JΩKw {kx :J*[1Kngb7e>Q =Z6qX"پRc%h5`aPT]0*q3C9JeݥI+APtVj:)ySp&⒦PJKDfX"Q=h\H3mӫqGq?x:^Orr)W.^Fro%; :*. qZ&/]?T_Ʈh?2Auo *SzLB!B!PYS&Z8d촁&wӴ8V֜p.s9y'ԷnҶ)*:*:BI[ruWEY?XZ 8ۆwњi3pTU,hQU]`ew2l5!*#M;+f۟d֯:3aDջڸiwU!i7CIJA){ffNoWOUɥ9c|fOi̥dU#olSt'6qtSƸW7gǷ?\~JM)õD}%B!B]+ӷNU^[r*HMQQѡ͘w읤9 m$qnKJ4OYz^i 9lP·̼S43A\f(44ru,#_LJJ٫5o|JM;zqجEkWtVj`WC@h7\QkE:,!RVWkmbФMwE׸!TtڴpI|˂V >c|cJ@cMUe8)9ES) B!B!PQс{9u4zk4\Ug<9;W}c0Q!ڛI2lDC/I?d1>jjLs3 E#D9~. 0K#u$'k;qd.\Y~ӟҹ,m?d,4Yj󀤥ozWaSv(9HJD>}Imfiӟ7_8;]V#hblUth8gJ\%y=⇕Tѵ_ۈ"-]?guI{_hD}=*>_SPȍ4"BMMM^a4Iy]A_Xi;A;~E7nWf̘0]?fi&'#Tٯ 2qI|EWtg$5/,~JK9{8i].$b7B!B##'2qf3?7c~F5$LV$CSpF2X`^N4):s(?w1Rj%wU|OIs ^"l{DlRS8uS'uRVGWtVj:_L2w_ mua[ !cjm[>o7m85;3`ށpGFv@!B!P*:R`tS.)+̫J}_)jvv DdZԕG=Msٰn?DrK J,#=`zu0kPSM䓤7_`uE]6$wgwGe ]AjPHH9m8u !B!BEG-T/-:F\6KYǙiVa+u 0K-}GQ4;V]Ey5UZ!@ӱxhKXwۺ'bxs#.E'5r֥A}!+ODN* 3 *Wq7{L)"s$ i? _H5!x|Zp {)2zr(>w3gd:SU;|*iF&hY4%+u y=tW^JTd R4(yXz4UO3XLt.hgLd}9VK;}3YR2ե{>~)tYeNY&vWiJ.g/Ze~_>ôp [ngajwyjeK{D'MQVg5UCOIjZYGTD7uq_̺P0k]\G}U\mpMt>q\U+5k]ᕷtx^m=yj.Z*7* Ht6s]~/o9edc;*6D!aqv3e"6']Tm<٥Sr/YgX]M+Mg&r]p( VX3os;5k!㴫# `8ڒ *EZ.@>[z]7[%nDa2N.Ǯcc3JY&:FL(ZK֨mJj#ɾwU$JӋzj=Iʕa4ݚQ,c/OLu+c<Ou3-1MNҜdCY_G [R 1C8?ڻ:y0z9Mv+Zy x(L׾l,t96g)&Y\nY W6DHSFO׮NCv|:w+&>^q $:8`UBv#B>K&kWGdZxy z˒1'FnYZPa@iid@{ɶgWg9WD?fTo?rN|V>8̍eODy+~m7* Ht M5q ͍g_u0{m* сD@)Ɓ@G.QmeAˢvx_Տ69p=$:Ht с48]~7Eu2z:Ht @iqdu?<~kYGO <$:Ht с48ZP-c4C]CErHt M1Ht M* Ht @$:* Ht @$:@$:Ht ѡ &:Ͻؽ߈5;g@УmcҋNy$:@C#xht~īu˓*˷߼#St>G>qY$Vݱ]'!>#չ{Ͼ$:l$>CS6wD]j( ^~MJ؃Qgls\31aLO !vTDžXQ1:vg۫ Wzs[8Mao!&qS;Ac7amq|>~#&Թji5 K3HtxMנ+bR *+7Qn{^Ɣܲfx&ѵD|^~1Nɕ߫鏹[}%&iGjҔ%-0oZ s{]TJKL/ߘ @^FDme ٹNSal;l<Φ: ~G.kq \]̰v{F5eP,/?2f?2 hᣯm|NUYr隄G&8ˉNv^Gن]5h^&>,$ёdle]?!&jb 8Qi?}ם|TNN+vPMt!vΔ՞6sZ X+TQ#l Nl2 сDmS.RN{SNVӣ5xLvb] LtO !L6iOGMQU#]<.8Dfs$:>Ѐq˵D7gŮ|A_shKHt_u5Y;(.0AC 85kА%A !nx z Am}Y"m>YRy,[1spʦcn~8~s +FLMQO)ϋ^,2~?ǫb^3G.mGԴ7sHX\R\v=~/йP纔 T޻QI#,:oq]Y vek['SNL.7B2ݹj-ybOޮ'ȍݯ\.15IK0:At_E^M-ںq*F]Pߩ56[&z ˅㎺}b+#ѵta4qHYnG75h|^j,ф8 C5Zag.y5 >O+}'/ 5CE+zu_k N`R/rt:ijA~Kw_gtU+בs7gnoөzCtQSͻ㫭Uy%~9sXmY)ۘ9 >#Ys{ʒnj˜SbX,b1c1#'B-ڹY&emwlÈNψ݆{DWY{GbW[!]|-.|8_'9d!e돿z!UQ맰1\(NM28=wnt* MNLrz3@àMn[}T'J 'Ya#I_iF.{*:blj2l;ADqY|Z:U(O}hO~|jl3([RU:O3ۦf#Mf]#zSĀr'-p]h^Nfrb $ozEA΄n=N$!:^s裫dD+@ڭD"54tm`j6F_BRmѱ:oq8٬ɽ&rRbnşaK ya })WbX,ňňN_[W#n+o߽?O"T,ᓧgDLj.}[I7+/z5$g/NN*6U3“ދwIh/PIF\_< BKIY8smm)lvLvm`CiXk1{6G/#<Ó_V86Ң4qŁ|R[=~jDgs hped7x WJ]ȳPV_EU|L8% {?68E+M*s^.nߵR>?),WH/12#rٷj5Et`!4 ^u9:L\GIph(.kOǭED.FPny#ke%%_M' _Y,bX,#:#:}% T$KO΋ [DLjuǞb/kW]"uGMQTil- n;%ODRM*MQ0b X[0ks᩽'.k:r\jj2zpkil0mv5z)Itz-IN.]@ttЙcp̆Y`=ӀuDh{fj$P0B ^a=~rDj";7¬vё5V5 kT@p1H|sYFJF1ƪUDy<.wT>ipt :Uă4zmj#ssem`IO] "*# R6^_!jz8WO|3VA~qXf[zlO ay4Ua-?|vkn#_!:bXDO5 ; ,lI_u?Gdil1{bX,b1c1ӋJ$[p;&[3K#Kd|aD[D fpqˏ#?QO4^ا=^Gt0oTg\H\=~CuNY'ŒBx,Q,Wl 6ڍDD˵ Q;szv5N&4J_Q>/LrUOcԧ!:h|Wtze%Smb@ݼfÏlӜ*$r. ᥊KٔMD(6mdsX3[dS!ə`cl 2 xnkHr^p#&>)ϰ%X׹OFt$&mC)4jk7BtR„͊9+]DUZ#6]R-<|Zuz5ʇ4t bX,bѱE%{E[f%fF 5ٺy;jR\yD̠ƈN3>>uMAE([#/BH=)#:1nTָQ"I|ZͅZF,8ȁհzDw}Id&ԒZf/Sc[*!!t* #:? y͑-dXZ8Qml bN&B-}E (%JwZԫ, -[i[K}8Zw+ѕ8HJ=+K^ [`xc#D)2q3^u 53jc3|NZH6s 4t`bX,b1c1S+)kC]OYn|?~(ۇtK# <,刎2CL5E_V_CjE V3٤}|bX,b1c1ӋB;2۾Ԏ# 6z-]/(C9`}H/s{9K/2X]=)Rye^GoKD'<\#;q%Yrd$ѹiiɲcY8]w1VYZa2+U PT3xDUSN-E*({+٥sPh#hgx(R-2 &T[ FN wWS?sCVڵCtoKEᇰ;vBW +|8?:>pDWDsS?S[yu!AtɅy27bX,;c1c!15mpeS}ѱ][ƾ׷%QFNfD\e1qvІGW- ^˦8{ё"z&.zV,\_Zn6k,"jFjRHqP-E |mʞ#g}7%HOMsM<@ts||QNfa]ާT_kwEtzN -$J#rW,bX,Kň0YlgRjXd1[bl-e0٨)'̢򋷟wi=j%  ;ԁ,WGtk^#(?$/} pzs*,WDg˼lh?Ge*!`&>wh8(J0cv}>sGiȡJN_/AtTBtPv o>ơ?rv/Iry=#~cV9tѭLJ]=RMbX,kDbD"O8.K_41qJtA>{݆f$Y!=+º#o RnRua/Pk̴dE| 68Kh@ : -wpKQU@VH|_?^;TEz?D7/z-N"io9Zk?)v׆\tb*ᰩ)lɕ q^* ű4GbX,bѱѱwFjjog+Xn/K&P&#:FtE[|4l>M-<.\vCV؍BC X.l>ju%c UɄں]ȻM&d $1z(Mr"-k K { dU>D'N56xYQu"d0 >|RQTrcP(SZ C cpPx Vne#>y5q Nqrck+B,yKexWz=ZA<8j cn^kN8}KFʦeIg}xmJO[X,bX,0,Ft 盱H P[tTDLj..1[2:ۨ ׀ B >>74W +#:BKw^}Dk7q%#7:T)3L-8 Q۫Yv**ASÕ27CV+K ( !em"L(dSᅈt`ZxܮŌWqsm|)oa׉.*Xm/=s(=pd=dH3]^t*Iϡkw-@tC&'Qļ40 :Z 6js+l;[ZWzdotʅ[iY,bX,#:#`Eh dmގhY;`@7}?}b+&>=0hѥm-XlRk𨚻l Rc]ε |Zo6RC9l+RpG {сlMem j#f!$S߻En2w=b0C^CEd:E c"-hr&{*j+k( v4)Q³c8yI (QE#)D*;#FG+pХp{KӥPAvŎo=nҜxVэv&wX8Mgn/ 3~.8*9!2'Bh%x΃5Etk~#]k]XIM]Rx 9[[JdVMA ߪg,Q! NT.%q4_Y,bX,.#:!ZRtKq>+2OzaD-$\W[iFƉKw޽XVcKj rkг1P|υZ LچPD.$e仩f>Y8՝z*h+rCj׭:Nh; ♏@ܡ]<ޏAR4dzE!N3φZLݵ F˺V_|?D:%AtPQ/ɽS'Q>}.N elC bX,bDbD?U=ZgIVcCu{y?beLrql"h܍_!g?ާ?roE|>s ~ [ |.NN,5f.EJ\B(‹.;7sD5B,J-FP>h,Gr\7˻A-ڊkP m.FpjVO6$ qFpKz38\DܱKm,H<ԭ/_˜a_SDvSQ1Vڊ}HS+s׏ὦ8\蠡S6V6yƲW: |ΈBbX,ňň/de0o٪yDaa8ҭMɝ U~v5rlbQ":6 YRns^On&4;h>\F%]!+^Bu{B@.qF),<?' t]DPPz:1̶!V'.7X'_%$CȾwK!ۦUt'h':3b?): (;pЯG6V:3\ЈolMrsk \ۿ&fwY_=/z-h 1[ pȱZ 1 '#s/!}S9EAQ\~ X2N}|7}[V>ݵR!7#zwև;_fmNL!]sYٍf.kOHq@|l$?Ʈ- pg۶g۶m۶U۶m۶]ؼ3_hgOyw^ !!_w>l>4j_,rK-ITݵ?XOaG] !!ُ9$"%{_4-jOh>Mp03"}E' x(RjOf6 !Q'qSf}GšO;Y:Ql.4_MQº%>/1Z9"y:;? {;9o;YD(IsluAP{QHt !C?i7Oho9tNZ=:cd3{1K]쓮f_2Zd?IrCwTX=S!pkx@vɟ̙T JtǷkr[vuBWyCUC9FծиboQj šgo!ϋsmn4Wp*Ȝ ȑ~I&Ge>e{#LwKtYfM Xg3;6E&+z%C5OYs8Y%I)?~l@4cT]ޟ$:~@X}*XvSy jؘB}[o=6\f|~C.no2sX8=}GDvS'=HtrP=+anl]Sw?׽j̅=ItGI)^WybzDTb{H8CKwOxvbgŬlY!wVjkv<:c}8bApHǶGz$:ǶG2RIt4Tڱ& O6D7﫽㘃:cOoc%ҁȯVvHt yNvTe:T%:9j}dymp~4եSsjntYٖ{A9UfyJ3#jt:51FkLHJ@}ҘcMItOW^E\B놙SK}I:1pxKgbv/faں%!K: сD.Tt,p9Y^=H_=Ot.nҐ,쳟Նe!k: $ήWN"Cc?OAnպ7gD5cVC]}g sO^Rz@. wr̴/NTnj)Tc^G6nsqѽ9W2%z.4ѽ_^ԫ Tr~V̷4}jWmD7[ёqvo$՘i-A>AU77V5kn9\vꦚ)ȑjşkzgEgn $:mW;%Kl!Dwrg>+;$ՙZ' Y}M~$^is_'[WFwOhI' Ntu{-`V.T-RVy Ht ǑaYaxs\]1/#eN Hܲ?jWb٬?~IپsI;Yw̴훫Ds89,$JqY!D$:YaÜiJ9ŏik~^bؐo"Ӯw6٘Fr'E6+?EGL2Q#LXNb2{x!Kx_?YTn}U£X:W!DHt}5ڤ&MXmdp> OV4 YB+3+JHtH`6C${Nf|xFvRABϊ_khk&<1o/3=XyCROd}dZ{iȏ{4־Kf|02~)2Ht yHt H?@@]iq 9ޒ d![ ( mhyT@'HtHt@$:HD !)$:$TDD*@RHt]@CK HtHt)$:.}D !ѥ$:$ѥn^zU}7o_EWMspNb??᤯jk+}[~}qN߳EA[^aU>N]jx_nW5. .-,xP\$:NK)W,%mS4-=3*ТN G;@NKɡ=٢ʄ[_~/Ϻ .e{(|ΪKxڌJCf\}Gfg-^ݿ|qS9p~-+vp~b1$ ~9ʵP<}k7}wyE ܌?;]Y>^CX=>Ht!It)8.܄I9v;}HAdSgtΘCY08krmuoآ_ژ}xz~x.;$:.%{}1ɴ?W&l:=ORppQ7KϾᆌۦsVcaewq=8.;$:.= #6a'gUvoy#i'9{畄3퍹rdhp΂ڰ DD I6atBN.kw-}1eMZZx\~qmm08717%c&^BssȸV|{R޵vᕷ,}>uo?)\ D7a,x&plveENv)}m $ƔMƌo]~HaW%mYaL;e ݜr]< y^k.G7qݶ^N`Ի 3:$97O.op^YrKe2~M{3nJn+״mW} &prx^&$3O^}o2r~b?^[U_}_ɖy=˾ ,Nq:ʈu,NV}%W?~ۙz,w٧Gq1MwT'O\7cGnnѺ{畄{&NJf}cc~W,bK^؉6D !!&owMν7|S>D׹ }%few{3y ]9[6f w'-myǍ=6Ծܪڝݥ;JW˟J{OWp\mmaq߼6ϮJϝZܶDҘwѝw哱ϝ:yax] ě8%$:${}gIxxֳ/<1%*^Ηyou&RV:q'($^Jƌ-rpa߁CX]]]qӼ-{acEcNǿ2^[U7٢͝T& 3oV7iy_uYEaJt̅cy;Җ%mKtHtHt׺Ú/u-pwly/b?19[ _zɥwrPo蜪d}7Tբ %F9h|߻ǃЫI]HtHtg/ DWݱkPPQmị6?͏;TK-ѽG ;Qk.55\wc/[>ϟGx֒_:y\j]TK];߯ei|ٹvMA"e !ѥCz{.jCԼwY/@CKT~FNۦGDD:F5Itog@CKU]Μ0u?S|y'{}g9 |ct?>eOzۢG&_qS4DD@CK HtHt$:.UD !ѥ $:$TDD*@RHt]@CK HtHt$:.UD !ѥ $:$TDD*@RHt]@CK HtHt.ؽ_Ht]>ʸ \aL !u\/fTms хkEn= ?&޸:^* @C˙gW><2lM.^޼:HtHt7N*:*xewiE.'E .#pS.].g]̨:ti6SǕ>-DDҍt1eZ !RxU* !^>7N*K@CKCmma.<.J* s}y$:$:T$:_APa$:$:rRaPa$:$:rRaPa$:$:rRaPa$:$:rRaPa$:$:rRaPa$:$:rRaPa$:$:rRaPa$:$:rRaPa$:$:~N@CW˱ؽ_qqS7'sK0[n[˚88*Htl.EQ\AqA1商-Ht=[?] byzЖn,ute^GbWkHt jT(O_$u=PquI\%5|#910KۮpeWT-tIt ת}|%ƒq)5Vm~e.t n][W*qItD D DHt DHt Ht@ DHt {wǑpblffl Lefffffo匷&w,y2yyR}NK!NU@D@D@D4cD`{k|.˜":"_x^GZo]e;}a@a=f^}Q^߸$Bѕ&X5aN Zx; 񎢷 ׮VDЧR nC}c#ѽzR92gEOTe*Xh3"¹ߟšǫy-;s)}ɞǘn_'鵧"|}u\,]_`X6N\խmJ ]?tx}ݯO}:y~ٶ'=[,9ZumO.ߞ>:g]gxpcGr왰ͫ~w):뺶< y*. .$ma% Px/]>ցBe!m1xBz/ĚmKL06mш<+)BVB:%c}SBvn[Z4 y,-KcNTf_<̕}W*{y "y&+*ФKU=+gvM?aX,nju,bYrmW4kDOw?Wr"Ś̞gs3̐]쳴[ˊ&/^d|cێxmmA%@DW[-g/94sp7?f}yC.N>d^(ƚprfXlֈ.6%{frЪ]=^~W 㨒TzX{ i_P GÓ cWoT5ab3""'}._vݍzC[㕩n*rȁn{a;N6e)[vO몡/O w_ū!JݙcO=sk ]fz".zd;ڦE2!/e{QYI]r/N<U3wlC,,JADAXв]8I2K,=/١Qw}eȟFFWul2ՐB%w=x]Xlmk*&aOfoIa\eYg(H}w܆KSWG:M\Xyݻ55Uwݱ,. 5-"yiHAF2],{jestYb{;*?|b,  Ȓ X}zǙ/y(%;q՞XB]U833>;._H˓`7[&{MU^a\csW6U9ǐ>Cjq/e~PIM:NK:gE׻NWpb}Q;sX[Z7E:UvO ?^އ7e薍QQk vMkޡU+o\Te>鯬}}kE*빫"^64>buх#&ŕƸ=3tx$7fޛZ]wE"տs,",mɨ-֡ÝUL^Te{L}Bնwq*p퍭>\Uj<8gAxK\benmsnY/Z w?VZ,[CƚTW4$rE>/VYs"}h7%Lzʝ,.aj~BυO8WvL]R]x^#<4CNVR/ˠTDhejbfYjeys% ;~pɰuWuק^QS.ɗ5zA]s˷7U6wXC, |]>T#0^]v˅@S7qe×OZPG"`L8{1G+Lyd.k;=U=iO }Mlg6N\#[>d;o,[KkT@DlJ$d\ ]I$k^X-^90ަ湣mϏ?+;B}DRTeolN;oīgb/O>-2sdˁV.bG,~|pdP-EO.m^EE5`XO$mACFzlX|%uGtٸMi^EtnQbvF}c遨.;Mlm/+Q"*X|~׹y(.!l 7Ӎx[۔dil޶f,(ԈMp ߣdD)ˤE?S{Ey:- zN ޼zo|Ŏ{艰ge)t~|o$yגQS > Y +/MFt!]<1ܕɂ~*g<<{y}yCߞ>߮CeGtQs/$ŸQD|eiCi]c`ưg/~򃛎Ho~Y׹_2"fB..>lv?N exώ?m;3RUT>ڛB%#')OEt}}iσ1 no[cCTDW4K">&=d} ]˧\W^q)ȱ!Vz4]S.w艛[ZuTDaHDt8ļrlGqi|@DwɈnP96>*S] mX0?e97^{+ ph*  dOqӯז' 0;>|gοMquDtTt;`dCO3} MO}Oʙ'.^I0?ʍ{nlUԢ3=Nr_dTì.5oӞlڂe??N=v>^s '?sx_a@D'uWY|}ꃄ.*.LKo=iv?O@DW*:=rO|ɇ0k}/q#̽;N-3G 讯ο%ڮ,,~g+_9pw~ Iؗb{|3E_%_u(Bc'.Gt;][]?+B+?mJW,/VSd˿Æ]G3nٟޯaS"ϊ训Fgqs*ݷPw grہӡI)..[H?'~HDwC/\.щZU]Vu Dt7..ʾ۶gDtPYwmKZ~?Et":]B`ٺ/U[?w$eu#{g#Gtq?03Ӆ/fffffffffˆ6bĬIu)3ٱ2=2vz[1 /Wm#^/͜g؄`?ɐK?{/pt"&܅__f]Zt gdչU}Σ0~s8x$e`ɯ?ʭ{/!⼕[j6jDWN3LZӤE0#nNPɊY;&5hEsԵFX ,Aʫ?}eYP!GA\`QnL3=|5Rʘ0籚O~Rb$>u!@:'Xn- |Z'.1 dw=j7u5-iFQ' 1^!}UH!B!B%:((ұ2.߷ ߄ alf5fKpv00<{?9`f,})5 kM-|cƵ5b\t>ˬW&] F^ny-N\QdTNsp+W5`w\jcw9yT O }մEޒ.0^)EB!B!B7XNZMCԩ3㜹Kp{C \Kh (b܃/#W8Bzuf24Dx;d B(=wpS"A [vvf1;Zf-N'|oMK{+"x!;1eVq*X<#(]1f*J/hȹ[JtmݐT'GޅWioI3%]>a-+L.B!B!Jt*L1Mw(}데¥+>~Aj'> 6@Xy0T}9Z * ߚwAb!M/5orNN>s7c6Li U8$1`y{JtB!B!$P[i<jn7Y+mD( :}#ѐ |Ya@*.j\v,yՄN,a)l?$Ph4m蠡HD 9 i9I-]tZ<4Gey5EwЙS{R4`#Qrw,7wJt`U*[\M{KڟQ- { ?@B!B!DJt ԃrÞ)qp3\| FVFjg *DJx4Y&vw5;%:QK7tjpM{Kt'_JBz_>o9BĤ|%UN3"LYK7I#Vr,储cw 0ezioI3%?a.ݗp=kp6mSPsqoԵݼËh՘ _CѼ[4Z6PMn,U?Iuz{ 04JoC69C>7\KULSH:gi$:Ynn݆lq%ioI3%?aAB!B!DJtZpv^NIsK-<鑍!!؉{A3_ۣp)vKt3U}E(Sl.S0_|ZKP"O'͚FD;b<%:m*wCFs7Wޒg%:{K \UFѰuTB!B!JtC่͌Bѻ,Є0bpV_Ujq[do}>,/ioC2odifN/ѭvpU~>І8%:DyU'zLNʍ9&RӖ`usKt~gU3)SYH~j[DgoIOB!B!DJtO y<-݃b|0ZSt8dIu?J]e؍=zQr—,-/_Aiѩ_W^g~iJK$fYh UT*C͚Y.nFO[WEޒ0o] J!B!BHD3^Oo:_0nq}Wk^2> ӌIt9t=TTk ϷA.>3 Kt&× ,j[uYDmJ^L%F',vraw1ǟ/[DgoIOq]!B!BDWAKpYDg\#iiI3 ȼ QT-TK챟%?a %\{JtB!B!PBMC4%Dcbսxy+)8֓_pnMC@߽rs[cnkh46HtSY|,\$g`tsWD/W>y^Ht SSU#wJ΂ y_#{/Q4n-U@Dg\#iiI3j= UG~6tɚ9q2JtB!B!i()sWlFG?AF4UE :dj% [/iPv9 6Ǩbc̹^Dg;igI3%?a :^XcV£DG!B!B(ѹRqmOûے =f/]YoVo?W#2AC_={F8=CoHeI_GpɵH׸md s&*ւ!x{ա3ב Iܷ*Y2rM\Wl\ "#"0IKPVmXP,D³_/*oX߃M\z+pSQ>A̟K7_A6jZX"t?8hvm۶mg۶m{ZlS=~ȖpVn=,fh.":H=b˷&XBpʒTr.N=@D&/׫V~xpwRX+6;}ql Iqns{=)[MA":":":r*}p,9 )^o$IDtdS4W2dYXASx*,ߴW5s/ TVpYJ TdWSt-\qc^wZ],An/p< Dtvja|sne ׶m۶m۶m۶mZDd[ˉ[;'w9SٽUYyvwDj7U1bܸf蝿iѱ&b#k90j<+cAߓuǤc5ɜ7 _(=}=0t@2: o+9`&\p4 !(ѥΒJv=ɓ7X쫟X X{'>x)s> ݲ d-:-\̍h1gߨ ~q-xaXr~D =>N]{hăPDGBjH̜P hB(/nɤCƃf&m/{6{IVe# 82oi\զhy(A&10,ڛc(7y hr4rg.5I_| Cr5Z|_úm}"}D2.`'3{Tޱ?릅Hi^:w6T؂ vQ1u߈I*m#E:frrEV| i 2(ej?~ξ~#&igd>C#X9SCq&z<_)I?P3LړHcX|k}~ `^.ݕ5&_{_5o1J$yAx MB%nF9<"{h0=w_~m %},}9x!Z)6qOOVPak1`׊a4H8pY[tO2OM͚o}ҫx>9k\oI{-GndfN(!B(Q=-վw8 QK_Bg/@H%Ƃe_;},<堺IeEE>,]Gw+D-_qlв&z4~|Dg?:WDe)[tO2OMܪAg:C`4wHJt: XB%:o>qv#2U_'(d 퇚w鞔idϱQW;*boѱn7=!g,Z+Jty3}s7L`Y?uNYq;k\ f.s@喙1M!` b|ZOYE7^]xSԯȒ?LI5?FOOA {_1Zl~ a<(ߞr.#'eq:'/y[Ɨ}4y9:h$QgIB>YǝpVW:z:YY>?x>_VO=-·Č.mo—^-c0xؓya>Ų+UT! / {2&/|Dޤh/?,TR|Btv\zDtW=bNKgrҼk/i^Fwxr[aqQ^`ewSii~ϋ#o8n-#PzU++*Qy6Er4XHqTK.;b3٥@y {S=#Ybf=6>DO@D/iPn<[#my)ߺQYb=;.W)AtdHn:!Ҙu'_66zqH!]۷'+D *@@TGG1 !CBu.>|C>,_5YpȶR/1yhZ SN:e`^1  ^`̋@}VP`>/|{2/|`ԒMk..J*U.Oˬf$wF`!AxK*ΣOd {<)~Xy JCtl:|9Xr]Rq[JT2G\~c`6Sy4R.]bqp͐7D;q)FržM=ItL pK@X͎u~OQrҳ=l%%՞[UiK@Ӄ1gwSs؟'4He|P#R_Ji(fU6ǡк2!2@dӡW,uKU Q8e+JgхI_W#:,1zQPOr'`tE|yqCqA0At#ztH4v5@kcO4D/XA9Ń#Q&ʃW^Vɤ Koi8vҺ@'`6 KyyaXbH,Dxaȃ1/Ö3Yq^ ߃M9?wLH+UT!:8>ݦ!;Φ۷fRKН@$ `=.2҂Cl^!:|ֲ=NԆh9rխ$sg[" -fJ ' 2%Ψ߯pBw8k[{>7ox`%>t{huEQ-wϷu%,x9H8BCc$,a4^>D'<54CJX7z~5Yj苯 &g(.LItT+_']$I|DƯ#0WįEgBt7MӫB9+svE+] @4}ǧ%r)ͺ@'S'1FNE˃ׁ :6 wכ + E+Cؾeh^1}^ϋ8}Vd>/|{2/|Bx׆&͋R]Rh](X1t1><ߨ fn{)@hU, #.:"WY&k%$AkZEf%4pw< Y~ԆZ>7;nx(ae9󢴖Ss.)ߒm[jI>ցRUg%CHm8<=#,N*<:c4&DGfjOٳ~m6jmG[(t+p} 2jg$!" !݌6JV'X^'QĮBt$'5zAqC䟢 NztJZq<(Pkyi^W $-| r2o qƷ 3Fr CyG?ϊoOᅣ|n G3(i&<਩!RJ9ՏL[tWkCyUL3 / iiQn"ofgZ:_"`_LJ\}w{NQ~#4sw `z Dv&/Jk9;(溣D qD:uw3zF~H3 4e#WI+oe2*nѩH22l HSP!+ DS&}]팤dLAr;IJOA'脌MkO ezr˔HQ|m^EG3#$ -@·Č8.ms^-ggyۓqxa-eT)O/B+UT!<넅R1GRwg] NN9^PkleMmZ2G:sEӯ om?~Wa}:#dh>7Wia9oɳręfTJ(HO=4m ?m| Jwb4DGY2p;sX:G7ubfŔISW#[ O@w"]4d|ŠBtZ"?ۧ ŵCt Lʌe.09[~ UpE2v'%<._'eVkv};c Lles^?!ж CyG? oOBtLoR]Rt]^yԞ55ӌpP,.D M];S #*NWYu`SjCI~m9SH9BˏP|njÓdy1 ZN{B@uTۀQ*@B&mH3ƛ蔉$c!Bzo*YSU!ֻ'.LzII>wD~pp]:ƥrخY'+4L35ۧ E k,SdNB|!<}Oޥ/ yE`r>+~p =~{̈́%Iz *TW*DWkQ݉e6OoΗ DǞpW$ "eXq\y^/EY6KGG%Z!mRw x%6:r_uKzn츿bcٽSs%=#ϬД| Ui|KҼ:vyZ]Lt?*~v^Y*Bzv1]}AW *DɄ#cjFBtDr-%hj[ń |MlT^JAt_ukڛljAtK0խ@@,·ŷ}^`ϋ}B/L{2/ `p3o,D$xi^!@q2E16Jm8KCuw0 R ɶQ;;n{͔0IvVIf2S1]\uڼ>u"3onM.L:zIUV_wsEKLwi0S)}  tL#Q_}]d+4 ^B)~8R|r!D 3RNHFZ(ClN*aA?ױc G2>D߶y˃=/ߖY~^80ɰ߃m@R$AqJiRJ-9K&o.Q{N ,*,Xm>/|s O}6{/˥a@aw0Ov'缕KzFRz^ZHf :,ut{G̊ q+ёQ;)q]#mVDAQW|_-vڧ ISO#އ6{&(EK.C#^E2#N?5u_R/={e*Mr67"o) qfqDn_'E f es^?! _y@?2CiO煩Lo3㐞ZoUq %$+|TBt j7Lc:AviIl{u3)KP~C/|s GM= Uҕ{;/)__ME煯8jjz*Y&IU3.hH3+8D'\$if @-:fZjWKr x4!2iI$^yt} j?eJtb!:dqQC;Uΰ֮y{nn5ъ]UG+DG}guIΩ~~N`oǜ%d|.m—k^-g xxۓya|i{K>DdJ*DW!:mM~ZԞO݆[w9NM9 7G0xQgWZFβc C"۟Sߓp,uUyuGtrPOKD>gVd. g#?[~-ʚvv1EISO# (4 a{ʕ\!?6+#io*Dsv{n!{3/iqu#G+DܾbS 29/|K]|/ּL[g!za·'~KRk|$|HVTBtgS,J(bkD??mEVx9ER >"ZdNR9ŋ#$HiYT27<.f/Rn^ZnAXR'l:|:o{{GjBq~P2a7`y|Qz/|yE`r> =~{(m{TK,7?5J*UBt'sRk13/@.~D>q׆ݻ`=wIGw+,^pp$h6uw0f[:R _˩|i\ݯ {!V@R{x3+S } ==x7gtiS}5Oˬêknn6cCuSl. ޞ|.ZgσM.LzIhSRt|>leӞv)OWćXtRk7o>QE>45[WQRĹsZ{ӪZZ*D}Yw/H2g 29/|K]|/޼|[g!z´'~͂ZlAWu@*U]討.Sc f% 6YEfi5nMG7ߗDmn}Ԇ!_! "ztHڀ[o_a#)ލH[f~ã[HOR ^Z[3R*y6 kM^W)%d%Ҷ$~M9 Ee :6Du5pzPpݜyEJFZ= cܒlxl5,.LzIO//ᱥsP>_1tФmPP'Bta 5;3N5BĊFM*D6ut\u&+Zfyy[bHƇ>/|yEr> =~{(mRa94*́GJ*U;q&ɂuq{|9=mOE7S+Q8>,<VSǮZ}>Ke}&絉gؽ 5 EjI#bO7GyQp-Rdf~ãˋZn1C KÄ7/4(6Jޱ-QtfV*o|{z,oC~H|beg<0'22U4$௛:ƩAJ,Mo-}us| &G^kĠCK Ct@#tˮvٗ){L>w8, 08q*k&"'M5d.樽vPvS)Q<蔃 r4Wq5 26/|KI_=Ķyaʃ1/ߖY~^8pɘ߃ wY<(zRJ=݅8_tVێRJ-Atߓs2/ces"WZ Q܂#A Pk:G|^6/Bt!W nv2I(%"%;OžȰʤG0ͨxIsfuەC7/$e0akwD'J$iݗ|jRI~B1 W ҈;LS|y m#rr2)"p9oi@cK 0-h·߃OAl{<"~pm9e臀 Ǟ K?=Җ??TWTBt# tOG )dCJd:&?,9)) y85: 88e!f RDfD:_Q[Z 9N'V~T.7](CteL_qdN3&/CGM=rař~̪kf^#Y8\ƜbN (>],4dP䞻T/+5b!:ؤ(].MQ a"r%>D/"XA>z YJWI !0/ {2./|`?l]~J*U>Κ# opǽUeత2]yDK0j''~&|oA` z Vx0EpQ[a3D>gQ%b26eN&{IZdyLN>;^ v{/ϫ+#p 9p[zڸ{'$#uw=J?xQeRGSsfq(D9Ņ87s D_&#~O!t9f(( rg-v#GW݂UdDYk>?u:!ךL r^$ǛR/,^U~,đ <1ii"c8pyW-1$S|/>U[՞ C?=*Օ*UR%*?{j8׳mV-ԏ/iс+RtRtcd QŊHy1 (ZD9(M'IލV\.t L'SG= cH-DR@Ʀ-*qURX-5hΐK ho@˜%h &tM.2wExµMqz\i_ѯb-\3r/EI=>u9z,D[&iVFn7ƽU/#]ר+gN9οB]HּS੺<QDm!RtRtL?Hy]׿>)NEn+ZvًZwȳwDFHH]o1xu z P? ERtHRt ERtsʹ?/u;rO^v{ewz/C 5l-:?+{YJF$b?\03333333333333f̼L^1OwVWwN۹95~w묨Z)_!B(ѽǤʜ'cB qhx)]ᚄ6PbmPT g=PC= >PBݴ'.J>!B!!B(ё;㴅kYqe^T"wapM'IN^NkmFjw_|# VoA{!6%:T?ͩ:v!z,\.!B!BDG(ѥ^@kAVl_n3)=8a/^'__18nbؕ48)YkW~ُ E5i,yQs&KtfA.$:c/D])~;yȜ~HiH !B!BDG(}:Kv=,,۸G"!Goާf-݀Fb>Ju٦xX6~֒p39t&%:L] lepc1%ѹ|f}u{],|u<r26d B!B!n/&Ib)`6[~7cPAۡ^H~{P9uA`MnGG)3Q3ɤ/5hY<@ys<Ϣo/D=B!B!!!iqFm%H9D + >C5_q$'3a,LC(٭(D( ߲w?Y!$:4_pi=G6!B!B!\bD1WaՄgș`|j y׿ {*h&\[%౔,dtHts? =l~;_bV>;x fn3XIfk>ū=z Bđ`S Z=JtS@LBTa/ 1p᝿p#L"_NJuǑs#]'›8}I Re#F^ܰYkmis/Y\ڶW>@!B!B|%:zz4}IidH$C?x?j29OQu8 _f8eJ} NG$dho6juVLCg$Iu ,Yz(`AKU_˨VM Ez%:-:'O۬ݤ uuʡ;ϿiҶ;wz4GR Rlp^|+cXեG2Ct"C. *(RA @A< .$d@ 6]hWgZÒ*-:IL!`K ZCKtРUu 0uPAB ~\zݔ>!B!BJt2&PC%%wmdW7{LU~ItBz`:T/UШOmm2خI5+MŜ$a>]ܭAƒ.ԉks+9C/:7_9 N_uB!B! %:Jt AQIfN@I@>H}(fziDP{P@$^RO h*E31PJnYx%WQkN&گ.%.[{׼ڝGJ6.xpG,G/Z*( nU s2خɂ%+:Tg%ѹPD)Z>X.W>{`KB!B!(ѭuԼ+"`$%:g[PAsIHٷ: {DE>)h8H4TBC$$y]fd i[\e8=*\Fb>=*@FmKtI E&UD; ]kvKБ)I\nBZ6M?HlK &ӭd%wMBo0 3{hغkDC5[w/ ?t>а-1~=zrZIY҈xG5mπUksfJH&}>T[G)G%:JtV_9 X.d B!B!>@]a aZp=Nv~ItI̵%DcBa?K6mN=꼷 jy%:ȱ%+Ֆq(R¡^2{@O)"vMIEpQwgRUQh`m<765 1X7=a+S~җ*/ŽKPK`5x%:JtV_9 9q9NB!B!%:Jt]0Ģ5AS/nyIDӆ@$SQB7;s6HJt3 .Uc{^J+ FϽ ;)Kڄa#Ͼ!QPm38XLזF_/сQS˥'^8ޠegLDG+g=pyjH_2B!B!DGcJئ}$:mD;Kt6W[?z/TN9k%:%:-S9t{Pz.D=4yqp-۴wpe$:5oj{? \΀Mkr5lTHtKTK'_`XMUbDG+g=TC}B!B!Pe0%l#"as'g>q#u@gEe #.ǿgo2ATjcK\ăW˖oYQhj40z*D|rd]GN%8r\Rf2QL1&7I.)Kt_9 Nru(և @!B!B(QC<&^tGۀQLBzo[@;ChdB!B!B%} 7#%:Kd7|M$$masddСϾ3 P,o:aֻM<+M Fe5>/<-:8j"#. 홗sXd]k5ẍ́ +@苟r2c.JtI\gpJ>p/!B!B%:Jtwnh#a-:y$ 3h'+~30Edg5t|8E0j6ǞYS&riEaHb:PAuz\~&NACBA5 U0șQ$ &7b~w]BQ$%ϐ'*0ƼDgeEC B!B!(|>2ȒXKt@%Z۾#XT2`mf~dἕ[+N%:Q3{:Li":x憎$JDխbk J ;zvH1)U<ؙ6LʮFRf](fgArvMsW\ (PPQϩ9qQ Qgp|{^|+-X|B!B!PD=kbWΒw6ct!M_@/.Ž:ԵHJG/{x+}$|"+ 'ր9 !KׁAؖU8mQA=-h@aҹA%kAZ:QB&Wb߈I)ʔ$,U%d]@r*Pzz"сEv= 2$rD]A1/3srG!B!B%:Jt؞}EBF1#mpb@kO7`d:zJ#OT/{퐇C!B!B!(6 4 "]s!AyO2aR]ڬ!BXJt9E-sc$dDt.o+b'F~=.޳ι, =^9DtĶ]s[:\mƲXC䙬]:1;263"hܘt`-C3 Nlŏ#t߾8tY:5->ЋճDtҚ5 n/TDt":SƯx+K*/FtGzy}c^_y|DV,6]sۨɳ~. @]ȉcKw=L @D@D":@D":@D":@D":":Zu8lpg `3e^RaÖbboW[u8:] G4mՍ \v۫?JU~'=~spýv솻Ӿf4wƮǟ2km: `e-|0s~?F%kk6cOK'yΞ":VG-;ᴁ}k>nڶkGTuώ~Y+זi'ttɳn,2} ;9b̳Ͻ<B0nkџz^]6@D›(./qnX:.Ҕyo7^z]0տ3/L·s"זqv9~RʕYnC<;zvU%Mv-U]4+jAPs辧gk.|9ʛ?|}KJ>mTsg{Dv[)eW%8iY"G6jriA?)љu[,-Kkҷ5)jaDwyWT{5ҞkJEeKs!eEi?Ƹí8.x̬fSGGM5㡨:uѳ<*躝pjZD^di.%||)gGiK[/jgD׫xy,j͟!?.Eki;2|vPpJؓmHþl]4g[ƊQɕa}8ݟ2Ҩ8#qcZ}ؠE܆t _^_=Y_9ΈCi_k^U9ѥu}a~19gW8۾NmHkԬ:n^ԓg^Y:7*i[կ]oҶsS D +!^G0s~cwӶq*yaZ43W 8?Hҹnylg aSWqW3t_?Gu;P*<@DWEo/j)ʆO 7U2/;hݴWWSn2gvݪLEsu[Dtq"ʒx֪9Bʽ]PM[<3gL^Mխ)?zޤeU_nx1GHh%D8 -9}xB(YtP˩0[yG_:q62yv\T^;E8] #xLɳF7ߛɬ%\=unu?_nCZE}Wz/jW_xsqG۾mزCnon [Q-]!yn܆5TU4|P)%IF;MmI6BDc̋g?~:y|Lݖ/n]C?T3;-VQ_Lm,2V"~w6us]po$|]ݖӌ@%( KEgШ@(]cO4%miSG Dҁv<@(ݯjt[*GE( ;ԁ, =9Ty|DTONzBT@Dhi.҈338vs uT dtI۾J>g֨^!РI}bûy6z׼gz͏/vW%qrm۶m۶uc^϶_l[ūds+&}f6%魮U}^{Z5&{Uqe_+-?oɛ0{ZJU@dGCg:qQY'.݉Y+iEd( 3;# OY}O: Jю'&0x=Gϥ״;){F'/C :e/I)ktw#TPm#;S,՞욜p[ y .@ׁ6[}E5w[sb69J q%Zlg-"?|BE9kפf`|E~oCLO%И8JXLT34u_O 's@QwR\g.\%8BI$吤01FL[q!9<ȉd IHWv:=[y5IXp2*lNqmۘ)s%=/b t5yEu@#uޘ8u^E+6v)IIhNQ{ r#HzlAUs~EceŦ Rl%C&#*:2Y_Kۯ4@ V3 Xi [&/A;?x(Fk@E2nƊƏPa->.%ܗn'NHIxz=GG2f*;@`޲]Lյ̂*lE 4@Ir-{uaF \0TsQN_]$ ;y2 v'RN]e/ѓHI(:_+yv/Gӏ}KY"膏5lq9l#@сۙ8Std(:qۉYjx}mSd[v?HciHtM40#aE'^h;k{wa 1$ĕB@\c1HHc}`|pw֪^5-EԹ;gzZׇj>Uu[kDR wq?m$"*Io6U;͹ Y?`JܡWD;#3i·O=>egܝJLBBz+ZjªEWїNsM] i&IQIikMj]$lPYSB5>Xr* vo v좔9&y1dXTmJ:iK=sO5_.ZV%`#0]RgPzf]{Ηn{aSTi8ϘSR{{x;ڏ|ww5~ #[ egp|_~۟-Tt{u-hT<`'z^/GQ=l7~xO%oE;#:U(L8Kpu^~8`s, m?ʙ w<{G_ys~\WW`x_|{'/Nɞg?7sbѱX,#:;}OxƦ̊J.Vwޫ?s_k]QlzL<3D#-ۼfm _V컁2l j]Brb8ç9nUD;#Z4;gyxZw:怅TU#~+1I#:tbMmze-Wp71rͬdq {1zڢ઱ƞ_>po:Ft$b9O[Fʲ= M L 1v@y\Ե<qN NpSd/c(eoPape;+zm0o"l|DGǃBs&M0broa65{ c[)#+=6tfy5AD[Zpnz`o;Ixػel+ 򈮿޲ v3-Fz'pY` %PZ73nZ5i6b'SyGi wwHXݨy&i!^ R@p˨w֐QEu@Tt-DNJH3针s\SxChbd9SO|g6u#:0ǡNMD`/>I-(+GX WX~^.Ac Dh9dDkJ:hH7wrw1a 6ۛ|'%^N`ÌX,_mG7kE&:j%dvCtK"KWtๅ8-r5d'¦F $)$c7P6 ZBʉM%oR #:bX([Az}Xֵt^PC[ZJ~go]cʳhD#1]D?kC61ro\F܋h'k<&UĪ;ZkD1HHKKEJGfKav Pj᫲l|QtN{&3sbmxU1\~=}p׸3.2ҦY;9dS{FoaІEhTz^"'³.l8y4X^#:0ulft6!+{r0{"IlZ#acAnXG~W$,wx#`f6DBHOJyug](5`/Ԉ.1#:bzGY@(컸z7սмE+MEC 5R}FdHgGt9FO AKNFt}_s(o&,4'кR`}N Fҗgjt0]?sdlU`_T]xꇻt\B\uMEkA7[۪ޙt}D7M#)dc\/h=iK=bDG9ivH?Aש7z^z:2X-my&5٬4.{Jff6Dg*0s{!9M3#*wz^w!/`I5CT©!/c~ )wWW8J7Y,ňm)i|z#P)"%=3ϠuU٤M]gq TBkw5Y=Ԉ˅َ]<i9D#i9Q ~n@b[LqEIf E%9)Ä́%:-@ƭ{:ȽqƗ={^pH[_ \ʯh,*-HGNjT#:3lG+@6{Ε3''ؽ5\Lf.">dLF]҄xeS=/KfWI*ԈP|N#S3xl8s7ݎ;#q=$N'v?BY6} BtbXAtaı@Sy<ՙs(*2198(ng ΃#Q7$!Fh,óEf(fU^#T_U Bnة^jDg#>yEu|#:1$N t=!\Q(ޢvDžaB"B)[ vػZ،E47ɣ|LGoˡDJFX,ň(et7(hGȽK9?EU8K)8INB=Qv aDZ6}B%3cX,8%5l(ȃ:<@1i͝rB%-3L`Deh{fʖQ zbD.Cf Yl~.H,/IIAw?BL|uEJDtp DGx?*ʖ!IһgD#-"z-`uȄ.bL&Ftj\7nO+4%|^/!rVԈn֓X;o&Ihr{f{"[{bX,oVFϾKkR̍#j;tȧQei^X8GF :[sYIDBҍ&!aP|al]*C[ͣ(:VVg#:ғll^yz]O2WȌ}T[ـc]!1S!O;QWMSz\#:ҹۿ(%u52QƩAui!:bX&AtW6J&> ;Qڜc Hi*5z8NOk!:*oF]րM߃`DbX,,EP}֊\: PblN698EP$@rnAHP?c}N=&~"@XYĒAdWzGN_HGWL`D8/3[+NݐR7SX{\!W>\cBX|fZ/:E@~CSKƲX,Ft$DJ4]WdU-+Bȝ !A#fnQxg@[ ܥ&At;iܸˬQ!A^0cX,eD}f Xbz{6HH4O0 s><yӁm~Eqs6L6SD' KHSOHNxg=pDN9בӉ0^BZtf~_06ȕ8rdj|P hw11{S<- E{ KD6[:zA:j-8|l*ŔN†@NdR:SDq3SUMp=D(m?u {5ʊ^D</xLj]219ٺ'ȦrWBfaNZeGz*5qD77R}z36! 5At;),JPlDgx1 M߃`DbX,h.;3ρ)~3G6rփ&159<;tsԡ<5{%tNLp;@v?(oN8xXTwUöo>2U["ŨS;ˢfDb^T d{Wf7|'PN6_R]l8∢~,fdҖF8{j0*jo Y}-)o&_ y(f!y  aKQkԥl#:bX&Bt]'^8c4ԚgnNZ@*<=ڮWYϳ~mJK]RP= uEj>èPF8Cy7fW׬ uZ!//s>Tw 6lUC:7@frꞗALFtfňnj($_=O.Y2F2jD@8SQ; g~*mNZS5`s/љQ,b1s\­,^E !:Btokvo5 ΃2hӶ]LZ Gx00TZD!Չ(*GO8"5u+۔W[ nZQN3":/ё9>1UňŻj~4 |Е5i|dƫ7S֚*c[s܃*\ A+9~S|'ۀ)Acz13X,FtM*&^aEItT?Wz9,ق!At;IzrP)F!傐,`q/ѹX,bDksvQ=c2mN]NBLj.*),WǮwkI rd;q !JPZNň#.ܫQ9i66yr]U p*i[!:Q!oK#.pyg#{dAn'(Qً!}@e?~oWQDn{_!:Jwy"P] :robV ;Ftň4crV]2?%i)5F܈=v+alD7sYLZֺ3U2[pzW({bX, 13/4v=T6"E7fz-EvgCSkICN}G ]~e#&@i&֊$&)Y39; asHw~E*e""Ne|CFMۀ!;ۺ<~r;5]3߸s$}i桍 _Yc_;T^Cve1FM_w>Cv>Tyg?䣯 >&Be;~"p P CE5{z }sH惧9{'Oyŷ5;b[uP3,풦@D2cj ۞_?`UA}2<;:7]L-|%$m JitDrHEhYٻ*?H S/)$/ΙoCM&\Gػ;w3lxkMLU6dɨfс闶tocADo@*: =Ϳ1&*Fd+PN{ي&{Zvl6aÌi?":`2"P~#b 2.ZW?VUff:9z3ɧ9Zҽd mvƴvkAx{&}a'aV <--'s@DW4Ӟ]ØO]uoqVz=3":Bj:(4͞-ƇMLS[07)wo*}٨Cv 0f K,2v/wEǹE*כ20Dt":@Dz53o^^m*Ԭ|w.>I'kqg/{QwU}!mA|4\A~2~撗cT~&K[OjkLL* guhO.ʃo>/upi%+E}l̴*]eYk,pdzoR_]Sl䤹/vB"eֲvøZ0m~%3C}4^:iR{˾{ )^tnd:! BDt}pWIT+T`<еDth;Ǡ1:<(0af{ ),Z;?~6`T SU]DX eTd C'~v .-ZiqJi{Z%B."WekBM":"7?qOAS^L)x{ʃ͙j' ,Eۙs #{şEvK^ۡw`8׬g<}f(oe;_{/پ?b'xWS毊jyP=g-۸>T#lzc 7@J/QDWɗ~69ܨϰZ_6-:^<g/S{O[G#9i't߾tOmY^שFSg˧I%vk4?P*ՊUoE)uy+bǡES~kӤw L/Xr?QM"e?Ȟ_V{hJk7+~?szG׺GԈ {O{ou4ѥ ;Y>LF;>$`NS.EBSj![g3_>qv` JWMkDrŖS\T8lsѩMsӪ)QmPOݺk)d $;=Ԙi f8T8UsV^I>F|4liS N]ЍُKD*R#'3&z<4@-bO6NSzf-}NO=؈ mP5= \Jp4{EDޓ(P\2e":.u*VsZL i]8n44Ij'^AK G̵GBQLyVgot'*?ȌT?fGKOSEPiD-Z&faXuщ454w)jڍKkY]GhB96غUNc{BJA{BݟDt0aQz2[9Vk*6l>`iGS=S~z*ZKpĤ9TtIw{O c% Ki:}嫎}(gZӔiG5:J4E􄈼lɎtM U-J=q6oe|9V!f[E5T^D^{|ǗoMv}žC0{{.t6;VA'*4I=z$ҴꝔIY@T߃GD'C'JzB೤]9="sIa<0o3DtkUG89!&[fF_?<uyDKi~\SJ #:QS˩ق*m櫵^]d( 99J񁣦]k5_b.OGO]R Z4d(SeDω:\%<~s?)1JI5݂˟ޓ(":@%Ӛ]*TͰx.EXpg#:woJi2s_qS[6԰GDxr%v ;0p|{傏Tɔl0ovliݩNy͖^fJҢs=່N3xb9EʚsKxO=؈Nmh;ν'oUDt*Tv!pqњ~J̜F'tȸS% э4s np!t֏r(|D k8Z|5e=M{W&ϜVŷ6B@ͧkc0aBAVmyݟj{s 9'h;ν'Qi'MADtR~{-\:m=tT3H ?{3ec71ie9ͧǻGtm8z]ɗi٬TuNGt <^r9Yd'T'>Qruv;Vm{؇A}5hFϽ]tB iZT,UU3vy/Qќsm{O{oG<@D,Mwj2[%#'{Nӝrk)N*P4v==Sՠ۱ƝHՍGtc/ <^JdFK*XnuZNӜ3~^DSeUdfUc7tUudS~6;<{EDޓb\(+ic3p_ɐجj -;9c# <&m]TѦ@N۶-k|4ҥLSAF#:-t|D- Am5u37g5$*Rw:ͲC=~kg{"n.w ꍌ~A?{y#:tGѹ0w]܅Z>C'dtg:U>Q IvIz$FA ܋NKzڄL ~Y/üzsu}lOPEW(*:ͧ{T,z뀑K79']bFMn.ܲT)s!\#:tGѹ0ڮUô:?{agetg1*UW4dnI[$4&C":șkj!jOwDWF#:;|- =N{yLf취5K)*2sIw{O cR 1y 佬y]3~N, t-1i;t,i 3*"et{O{o 5뛦bOMdDt 0S \Wv<2.lNK4Xe%Yݒ=?ogJYpFtRR6f3B6 ?kkw}4k|D'WkXw}Rwh^r^Ve^Lj.+z\0?s4{EDޓ>|ٱijֲiChtgDt6I{8a۠&RU{XV}1i[^ ;M&@yUP ޴8zGt6R{Og 0k @^{/"}])sQSϤ7j,TL5s髶reDbtSh82vޓ(":F0t3,Z:3":Q =5T` ܻK3g~ܠU`vsɄYK~M}Tx=ii wI6sHGZEej6yf]4_Zf*G5JSDgV@Mv8oT2sKۊ%5] x{cD!Af V\ sTT߃{DtIw{O cein\JDy\kI1;?x\K?sN5/ZVm{̙'/iMJiWeUHMi6?%CM̺J2XhW.El%~Ftm(M>W Jŋj7NSDe= G~ Jnsw=Ag,t:*T2'[E(FtC~QT7dnQ}T߃{DtIw^ * F-{ '}S @DT$ve0m݉soh{{L~6l"eXހ¡m4jZ\)`8o6HU*w~U05 l;f`KUma6iNlWWI4 UYy]kUSU׺gD'_xu Rc'@H&]ݟj{p9=p> z5[p~=7L..DtAy+l4a/us܌)]OUE(!lOM͐v=EtZsͶ>VEȚmɗ{5IӴFt**Ju"^yt3*qagd'4FG'8oco#:{O:{ҟoG\3'TGDѥAJQڋnGrN$?wwU0e&_vx [WJi֖}'迵NԅkTRF6j_F)|_%L+ǚ|\ߕB~?fBMthufqJVz߫Rz Q7RǕVz_QTDi|7:aVQd'ϴSg׺mDULm2<ծޓ(O0fcVa5>3ZE FϼҦM!L4 M\]2 O+ fW@Dp>k}-.j3u:~ ^yӑP@D Dt":@D 2.{:r̹u23$x&@D"f,b92[F#=Kd,r(URd,ꇻIL܅˴>(fl}t˾S愦~QNBٮ4cԔ_퓦:3Yr-[w7$?\{hN2ݍbکQ̧ǿ}U9̥]A Г៛zqqн":"WއJ>LW%yu7|XA?Ü0xZ|сDt/wr2l'33šafN aM\}ReUW\yVjM\~C[g_xb;"-޽udz8$.\sw G ?K>D?3"(A鰷Ӿ%:{[$册 qTs^y#因%k>ž]s[/RM{t..3/|{Ƕ;$%I+sKtv\ysPt䏀݁zZ]p8Kti[d_qcWawP+}.hfv/>D0ӾV.}[ROǿc*&64% m|m;>۾sL"xx.Kd0fBwiKtϹ,T,'Zu<+q z0X\QZ$|[ؒJG3D/Kt r8pDiequ,Z.>k]$PҟNH6qvJ3|b(aO)1D簷Ӿvg9C:!džK? <{}BJvD‡<g5Y|]S=89eHtK*Od}ظۤx&Is™7\s8pDjWl⅄'/!w8y%ǡ=DHi_ ma切q .r[tvilpEJ/gS64{ t%gl%:JU}౗ 59~H6yF 筼Z.!Ϭ"9.9p8\s.w+<[|Ӄ!N},7?O8%:h[foKtr/Z[-WެxLSHN=*wn3O*{ X`~p.%%OR}PĖ; ]jle_\u懟!]#0g:M*p8]oAc*fc۝:a҇{"+GϿs]~c*ch1f?M(v{#'S'{}GLbs2[?Cy>uYӶmp 8 ky[r9S[E=v-xyz5 k!GNO0<̠>rim~SidxU5=b+OUP{x{MG]J??/=Sa+|LlBgxq&3w  `Q VE4,au/eήOjCM&VKs2 /u8BZf2c/"8\%:"UPr;_::' qG^[8f7\tȸw?'i97^Cx%:&`ZNQp8pD t7+b%u[d;]KP<:DAgo.&B2F(8n˿-O%ο@}x?l_9oao8틄@L@9=]j<N=$i]4t攞yƴ&Yv5'2 Nz^(J,C@} Hp8.ѹDwsW.0$D (&Bjkwv/UX?G2;>8'G, $uq ^Qtl\;C8>'i0ly6iG'sq^rQ`E!"Ϡc)Q63fDSKq6\κ:s[[h9JQ^p R^qKH^ Ҕa~HM q\DNƛ7C&1-B5_yː")bicG]}>W[jkn 0ť51}J11tPQ`|${M:4Av֓.e JE"dH[2VD{%:CH<9Хb$Itq\pޠE 719:S X=-8O9r,Kpʓp8NT8LWG)ѱ`n5 ©b=Z8j^`E"8y|ٗ^BAǐ(:De%{[)V(36ʆ0EtG.+TtD'>&4 úĎ$xx~K1(|OkW,g2H?%/ܻg91 9`J8Z%/^ƑE߃ RQ)bdih0qa)!wτD^ R!,uI%Tw=).(g6@2RRW[jR}ITBAIO~_BfhlNh\ēmKt҃ ! +>ݩ(uf?f`i# p>sS3YDWӑ ӱ5l [ȓ:OsKꓻO;ʦow8pDNi'5-+d%"aB_s[](DECJtػDɃ܊ٿvD*UQà}Ne1,xj+N ͐8"PLljTt).En?3?Q5qo*p8]XtZN=/<-AyItY)Ż P6fպ>m! *w-"D ,D!atj)3oMItx)'Vqaf/ jF"tWCKפ}d٥)`' DQNR0xNĐY!f֒y^/-"s?d.\KaAɆ|ڿvD*u z\$ݹ݀>f:Аr%?uٗZ$6)*.? 3?1[1tp8%ƗٿF\P-GpL؛%@9q>t¬Ma.6"'-&y=<=p?\E+dn`ߢ%}G2>dn;\yd\^efE} x%:VAKt^_%|ӃJ Wb#{p-GN/R,2}p8.5D_DhKti?\=6*hTl#.ePZ$ly"FPˉs4?e}%:/qH^87nDGX%:l!N= +9դƉup8Kt,A7w7tW]˳W 3Ov HJr>rcob>C58)~ǐHxKF{[g989*+?T(8 ck5iYJtq7!&[Za7gt1Ѹt 7Da4ΓTD&f` 媫O[Os݅/SgN;/iAD-"ӷ-R.2|84,aI ִKtK?t 7*XTdƇ߆l], ߒ!=\&ӏ,ע9?M%v0G!newsBt(j%H̡OI{M@c^UWqH"0 3a%:, `)c.owpξ Ac/f0~G؟%:#0bHKo} |ak-.%ϼ蚐^>J8fQX?h/^wqOaMp8.ѹD7nbWS[Dcջts 7N_Zj 8_jr"fN!窻8NI [3.щk`Kmatb$-6 ybK5~dZ\/S7nƙKGRCjt}=b P3)p M(B~yi'5 H`4( (IKt|tB BnzK`|Scσ.V.k͈EG,У\R\j!]y\..%#I_:ʾ˓='NJC!ǫ$p8Kt]~6NPq4`ssFw9.Í?pV1y~JW!Uz<N,*iVշL [3.钎u6JY[#PǙ< 賫u}%{ 08:#߉p8pv8(ݾTEmWv3LB|A |pzθmQ!yzYmToIQ5PuHטABqW~Jpō]v D'z_}ؚqNʑ-r:- 5J4; /j#|'ERmA8%k8"mc|~)#Dw|9q*f L͕7wKt5RRta*\e)OSjPΐWWL*zK1r$'d*[dz/)])-;o3 5)0 p7ٿup`a 'rF{Mx :Kt|)CbH AC&rtGm,jR1Bk;>sn8M2y2IMO?e*ҳƶhx^w$(d۝Q X"Dg_d j.KqnhZK=E:*Jp8#g [<]+d& qg2tC 5cIHOoi ܈,A VيOYƷ0Ez`dG9f OAElF 1}B8xݽwa/!}M>FĿ<C*ʖxCӨ8:U{!8ï骻N|sOmbxN:0Ex6x3ԯFN#Qd'I"-][SLSn& |@--B_1RԘBKBX*QaY lϊʄ^4&yэ"9 }I%?K;;@H _}(@DU@Kvm)_Նhq GEt">?Y(@^0@>sќ7JW @ʽ+̍y$,hlްp.(dH>":]ʟ [wzUy(@DPBhNՇZH_)^((uIMx|(P.4Pq"5&^M 3]k#{2nݮ-=O׿Cn[no|C+%#:nhMA/OF@g.YTԳDtDt   xZN_awTDkw0{Ymϖ_ }HB_|ћxZN_p͓JV4.v/:껟e@DG*t3,J(Wa<[I Xio|sT4QIm]KdUO*\VLl YzѴ}hv/ZCD@̞׿Zn~W (S.q30*ע! CYQc-5?SyGBS_5ܛ|^tw~qFQDF{\qRoW<@|_-_Ea_/>:אFQDFѬ |O )7{}:w'PÀQS!] CDND8xYM'V{.&!xɉDt^E_ЗrL[G1@DgFuin}Opw9ʲa8Nm۶m۶m۶m۶mۊ{ޙ9fsNOuoԿ=|ҜIҢ=q,t՝_wcXb'NӪK|Og-P^HY<9 2ULe l?~ 1ynRЗ蒤֥U뇧G~~sÞ}ODBY/c_(PwnN_f6udW*Tf]$2^ go\˶9ؖbk-Bf/tS W̐q3fXh`UWknۡs fl6B!B!Xɣ6waQ&rt@lU}-9b$H9)"nt2 KTm;y0UO n9@im=] 4w9 >htOc_>࢙Kֻ{rߩ3uc9OQ\B ]?]8}  hxmcNc# ,d,[V_6m̄<]X]ˤ5lf|! cB!B!BJ[D j L @&*C 7C PpQ. k "PɴnQַI00"'ks)On=l8^ݗV?wT`H/'CSCQ$d ='..BGQuBSBosq&[B,;\"B Wnϡ 37HG(~ЏKȄ҉v]9(hAAgƄB!B!%:/DdZM י/W\RU뷴O>G8,&[0O~Q)I./c6Yӌ9 Cy CB,y+C/Yzh9Rx24%2ꋣz 0ef)2Q *lp7zi.>/?d/PRgn @Lުb32`^C# +^LRi vH5_ (QXD'Tqأ$O.jkwǑᑅjhAu(8a{U-#s>zgrא; $ 7e6̜˄Ĭ)YѸ/qZ/}wmw+IOUB!BB(Q;zVR^Bǵ3Vo?Vm;ҺȡP@~! W(VnNd R<fo'4d|Jt}O.Xn+_dY.mak*5e~UoR8C2?%:-TCht7HרM7laTA둺e¶VLN]G!B!B(х- Vn=: ?kiнmګE""3"]wp1`)Ņ8OTꐻPLP~q&iXPtθZ0` }0펆ꂎxh[T8Z$DgJtv`_bMeÞ/d>|EVԎn1՗r9 6 G!B!B( 贉Q8bjVkٹO*^sogk6nD7y !E*oݵ r?25sE>u '/D#t4Esp -Ԕl$ɴ-;( uı?)}JtfNeye `./ӱLln?,y}ZfB!B!Jt /^f&z8$: }% sz_rL{)jQ?YK7`q0dL:V3$m [wEEHt[?]#s`Z%^C;:Kv`_.P%AZ?6Z^{g #*L!B!B(Qhmxiܶ{8.m|{JI]ourGn=>LQdIƭDX¦S8Cƥ͖ `x/|ZmۡDF,8p]jLsTVTz ЈO[}DPT:~Vxd:P 6Z/^xO^!B!BDGNBo~AOؼ (зxsO"Tiޣ-ݴqpk̏)<)-kQdHtVak(7w_=q3Khl{]ro-cX,hpKtv_]u0ZY vb5( 7,:}ʴyn `-a %a&^ f')Y^[d)d+xB!B!PD7¾'͑qdfߝD'5O_(#7|TjAYy_ojmI/庖"Mc9vn }0=|::첐?x(}Irtl7htE-]kcPqJJM_]UOH%qJq2c` }0돾 0-W IznL8fg kr *8^Rqo$CN,d^Kt[`5ޅ@@֬C/ma,tܲ^uJʂIF {`B!B!Jt*l(싄 GU4AB;KtU&T\CEVq僴~+sd97dHgo=AIƧDX>p37J5vϴ~WaYZh4M榽GeW0bk` R Dd]3[}KtXJ@>yݱ{##m2e2 !B!B%:Jt H%҈UHt)‚BAڠ A$ ͖]![qCH -hI⠐ ,4 :E};}Mvz2(V8DH;ӼԨ{Zt5oq莸3TSѿ0=n|p>Ӥ[h 1hL%:oz} Ȧйp-~i#' hu- c|53Xc?N!B!B(QTioRU4/e?x*B!4szD'Pb܌:1H'o~ABsG/R!]9aлI-4B0nI**jTOr(rNE};stG_~! hфA^T m`/@J = 3  ]??q97 h D/y[@.R^ la t>GG!/{+ҧԲiޓc/H0!B!B%:Jt4 xƕ4 d$ 7(+6e{ l1;4Sju%Au$VH\Sq"Yy@maЌ #vYCl@p-0MI!"d}yj `;˦_21՚AiM jn|"l X@!B!B(\lsݗ_"aZC,6 Ju6# ]"H2#l^B?x7ui @$}p8[ϿGy8=G/xL5 /P`v4|R(F6wk9O±Kw Zc<PNܪ["В1 \=?WULh!\q3+ng@0g,̶m;_mFl۶m+ײ3{~9~(_߈kvye𰉳C#:8xW>9>9 MY.DtmX4C川T1Sb/-:v1^L5*JEN":R9p!Vo;xֳ8_֩Kk vq*^F @DŒz@DU2 ":(\JrS*_Jz@.kȸe@D@D":@DdoDtPzS-W]APIIDt|Rb5~Q\tΌaԋu7k9T(;/PIIzC\r˦}'rx` /tѱv/E$;X( o۶m۶m۶m۶m][ώ&oI3s/vٝ={f{U+n*߇&ƍ{66kG{;mv[/\}ոMρQSaO &#s"ސQ9oA( fb8.$PD%z#O>˖_i`WFJV=eʓW<9?^D_{nنݍvO-:-\̍8cOq)gUaXHKAJD:usT(Q#€s҆ǰ9zδ*t %:3p\/IDO"z4k5i{K+ϰ K*w! qc#_F[n͔5&c/%($Z| +%s3דZl!cB;wWo;)B=yv?!Eo&).mpQSTRӂ I8Xnf/HȬ]FUAo$阙Qs9uIDG. +ӎhϨ%wٳXy< )SAT_kj£¡(7~bP'}5 1xhi3J뀘o +8yV|uI=61/7 \ Pi{YX *' eDDצnO>Ⳏxg /{-`阙QN9-ЛJtxZ+$OS4Xd%,'@ͯFF%dEY=mUI /X55Q}D+R V4vzeĦ 9 bodN\٧ptvX9S4Xߓ~JtIJthcX?F,0:n'űKwN9>p4iݚ>{gbl@) BoB(u0g\S{!Qзc-ڇMeݗ_< K,/L}9Q}1ʩC¸VCqޫoV̜PDG!Jt,%X۷<# [v])QH}T_*r£¡(Ml㞤iѩԵn׼h-U+ Zv`AGOƛGZ(mʔ}I[HJz񁨤*]E&n9DGgB(~O_& *˩Zo-go>A!kԯg 퇚wtOięG{0i qEk]e,|%u/˼ogq'eQOw}6}}[ќT*NۏcOw?2AZ?oCy>̜cNÒ/xV_{c5f+o0a&Q-+|^ݾˮ}]>YM6gkn-' o3d,(7})7u7ݾW3X 6p*h~~H+7}Ppw2y.}X&dY:x{x3.!:sd6W;V9D5,~`x# `> !+sX!`9jc$I 3,S<=sx[_H!#O>#>yr..!'eq>ؕ[R3>6'ߕZi1P೏&o9٧Z =%:[s?ڌ#N~nC# 4IJy6>6_vO=758ČL.nŸƺ |]η ~yx>+UT!<6uwNo#O7SGO.ro͋83.<$ej+X4Dֻqr7Cl#lVw?$foh?SOK+|^dܾS|Mw 8R|6Jg\|]j{6B)5hu帙7xp E=]|ҹ74bhϖ`dfyBW|_Z#\̜}ç-(=ξ|>[/oZafy<’Iǂ 5'}YTS8A%:h `o5xV2CSHNg5uulrw`@t}E *͚~ _m[$@Av@|ѧ;_8)}Xtw]J Ifa]3E ^_䃯vVD`>/|}2/|`Dޤh;-g,WO*UBt3GLJӢ)ؖE\{V^|}yAtx j* ϽكϾaOu UEy!ugƥwciɋ#o8ZGNrs=3:4Z8VT,Er4uhL qJwyvE)wYg-K-Qt |u{Gʺ3ޗ>Da D2fh?s훧]xMzD0pwh^gw5TZidŅX`>ޗAt/~u<J0B:JcE@t C^?oC&vj 1c`N η$=ԙha>.xd{J- &kZ,8d;:sW?dBV3N-GY&@tt{|E `rO> C _>=?5hͤ@k\mRJrN4~BthWrq[tJΣOd 65!6q/u2S~ͻd(/IAm_󢩖(]\jQp͐îtwcϔ(W IRM'a/aA3x;I—r^H^wףfPa\$ ,m=}\GE*ˇARJC4XxtSB&ۄ|$He9lW5{%┭(DjN?0Byfw'Hl3º?vo,n2uJC9N~ Kc2߻}}r@!:߾hnGj[*׏8I4^q3$pȥ^pA~P5C',k1ׅ#9]1uH>ig~'C—~>7%^ .b1#ЬTR|`x?!:BoSWL 6K|'d!E\[u;&~|i^SRG..WKe_"Pݡ1rVNQ("8ǟuin;8<~>n8뢨;ˤ[RI<$WZ)ӛՃ?W0U D?B5{8е,L8cOuͥv˷'090EAt/DV =J<]$I|DF3a^·XԵMӫB9+sv "T]Ehn3zK(|0]:0q[]GuzJ:tՕ+}<κ51g$ 烿._3,_>/|^d@^ܫ ZguJCtj!cb|R5xQAt3nzս)@h. #."WZ&m%$A״Dfqd8;hu,:drjC-n- %4#3g]rJrW[bs:Pdz|-!$|!:D)ȣS8=cBt`=fmw9eVmB([{9I_V#}f#2@fijOzylWx2.LqD.@  9`y Jhp/#'%)`D JI֑u@V(ah^A Y&@tAt{|E rˇ8 S _>=ƏdP>.#ySy3o)iY&@tqt{|0E rˇ8 S _>=Z&YL!fuJ*D'pXjYu$y볿t乙% :fVf>ue 0+|^/ோ@ ;˗q >|{)b+)r&TR|BtϼIjϞiFRt?`f^XH&uNTxy:*ʐ>/qؔPҩWiNtT*R4Њ#->7eC!!)>/ dw%-+539U\Ϫ"dBWiƛ2I2(NݼǷ@At/'sWK{_ WOѧkw?>Ly..BtD7[<Ym_O_ק@mR. jޥdKt>.lA@tqg| *W,i1ׅ#烿.b_3,_>/|^dL^67I@VEՕ*UZtT~q{|Ykísp :lz"iA-ʣJBHK yGqm#]gʽͦA>/|)wEפgO>ʖeQOΕ/C~H+4}Cty+>DW5-Iҵ9n\YyZ]9I$Us_?W_k,!={1]}AW *DɄ#cjFBtDr|9\EA#O457j!:G3]_tmzةKZi$$c _G2>DZya]Ē.Y|_0߃ͼlCG@Y]R E2,am>:D(6>uͨ.'"KBZ>/)qXwԆM )8>7e(uM>!lǽ5E܉\H5S$S'JxIe2S0]\uۼ>vy"3o.t?g\ѸX.\3 &?_x _1ݡSbp-ۧ EւJۿ͑-'稀^{0;a9.2@t0Åd &>݆ HSxu4х}^_䃡v/>/ }2,/|{Q6IPR*|Y]R E^z+9ѨH&o.>Ct"vJNog!~'—~>73y7jUH 0I*0O*UBtO)l4j癖TΘ rWPd PGyQ0iUҔtߓ%f6~IhEy)w9Ydb>|&UEN8=#鯬p DN7(jiյ^V}@t+'\vյ'͟|>G짻.Q"PM]l;fnTD3w;`|G䜒X߼BtTwI:ͤĠ _3.|M]h1uK>ag!~'#—~s3ty"J*UBt2MODډ9ljm]3d36T8p^>/ 8q2w)|^q!MV]s7?~o3)M^RNɺRp:9A@(eWk 2?WVd. 砉3rٟv52[mLGv畁 D`NrIU8@ a8Q aXwb!:fqBt||7,'4Sڠ m3 t<Asfs]?!к c>"|0t92C煡OF/|o^{I@rqxOGlݑTR*D'8X#0QְNIͿfLtr;5P,C(bŊ $exa#W(༼>jI)]fBo@A⧒.JXU,*cIyk#u o {0FjBpS>(q`Me :a7/_hvD~^KLbJd&d9I$eF~\?Eq.I~yI-|8;&{wL]|B 3Ц-v[} 3UNo[}o_fz5х}^_䃡v!>/ }2/|P|ޕo^5i7 k$E*U]:%&i\ŏ}P I^>{=q]~U$_o?M_c^>/ 88VZR4[uw7xӭz\_˗o<{AJZ(oQA!5偧, 8B}Dw%קO,@trwa7fMAd7sYl n܎|.Ӻ>σ.t?Ǥ*߼ў,fOWćtR{7z`S.vd.MD 7+d"qn˧h#ᇿSgTdwx,A-0c _G2>D]yas]Ē.gY|_0XCoV2[5IY]R E討.Ucڀe 6YEfِE a={շ? D6pjÀ NlŠuqܢ / u HnD(j'}yG%VVG&(+y/:fE"ep7=#鯬<,!_ G$)zQ㜕({,_Wc87ȕVg c|lx yDpNrIO/Ͻ桱sP>_1t] 3NSBiA1k# wn-ٳzԋ}ПϨ mrɊց$fp]?!躽 c>"|0t9C煡O/ xyTXI sguJ*Dמ8@S2:nYGц A]5+3،]jQ0 ctHԐg<Ֆ;P }^>/J;.vZ}a)>7zc;/JIFQ$X_&Th,$H8th%`Tns$վ58`1&g 4D1@tLN(VtGl7N Rbm&1/gJwW|>_Ƨ hsҗHk4vt]VT8&+_~ (Ma#242ʷ/hA?w乫a*j{:k4ȧE*]&C\n=4UFd\0̣ _Gҗr~}^_䃡v)>/ }2/|s3C38Ew?RY]R nCx SWU Rg%ĖJ!Spzdg3/ccw\Ja0CfHJa/Tid}:ϋ2x|n i]d4yK9jHIRرlLzQ}s3")^ك?pEis%=gD25i޻?.ѡ| vK srwlC J|q:KY+sr:Օ0WND]9# T1qWlMb!:${3Ͼi|e?cw󩄌 ZщRI5?[)eb3ʟ!:*˩$ 7M %fyukbHR c>"|0t9rC煡O/|nfdcO?+UT!a է%ROkJd:&D) y8^5): _E2Q>ͼhR7xQ0/è-]{\Չ*Iee+J3DQ ZtuG&4)9a24i8+ V_;f͂26[=2hBtA/'K$El8zY1?8&E{wiRR+!:߾9e DdkYNޠGF!8DN  `H3`GY&d|.nŸ& ]ΰ ny듡xaϔt,2*U]%}5!Gopƪ2pXA.DKPj''~R/8x6KTULϺtf /pQG[Dd.1o}]r69'h󯺕U~H~+khv2c`1-!":̽4 E!"Ŋ1/5u%!VI#*_+rNN,oLVI,]b!swuw DR8+œ%ZRD (vlպS+ lcmyJuqi8!+ܽLޱM10gǾz9 ,١OZ|=~蹷+cʗ(ok;!?wuq㥚# sD_P^6>%6W9: j/ձW{U:];cI%`DaҒ -)Ϡ3҇.YZtIh8.7>ᵏOx m#ZVK":s7":eyEոN=5l~ |,XqæϾl䴶Oh76j4vN=2Dtlz,ݱwƚZuN9qpFMq56j[|NFLWՇK5d`]+><щ/BU Dtl^őyef\qsLREI؛TԤpԲ&;{x_W`9s8*n^?֧pܯKx1li}ͼ"}Eat'ν&kˌ]ަY@D'k| _q5F+Co>ͶCDWSI'FԖEt H*2gok_s."vgYxNx}?*lVǚQpԖr ADn(DWrJ ;:$j[Z:{}ܧ ":Ys":|~lq?[{Zܪk5}3^:rJQ]Cѝ3`T*.jGT@Gt{(+KDge[7)|T":DtUס*=W^7Y[W`=Nq}Oflq#/d o6[%WQsI]8»ѝ}T]:R\@Gt%z(+KDg`=z/'"D䇢7̋Pщ 6>ILfF.Qbtzݙ? JDw֥":?+Cv":sw w ;t._@DGcEtyE:tZ1ثvF| J{t]8t|z3/!X/-wYWژzDtkm̀QS'^ ~qG"(co~/Yd8s|;yy?!sI]6Yiqeޭt(+7}SBT {O`kվgD3_Xv_H?_zו_}ܵ0{~ ϐ[/7SKD׺C7.v#ϣ`^-^gߨ^%~oO޼[2óoLۀJtz1yJ. Zg&ޝ'ɭ,ۭ#0C"ѹ.х)5n=wsx\~Oq!|]>_s({;%7a^13.TqHtzwUKNgk/gYn/B!B!$Iһ3M{VMvzZ!-3D_w\?'_ߚyYT[eʞ!vUj HcdOK~mBQO9۔5K6 XlIxhO[p_o>dIaHg0Б 5Z9I9X#f ꪦ,Xqx9ÛzWv.hH3-, Mچ$;wck ݩwZWƒsڬq8"] S !B!$=wD?7?J TN ^r bTuv`*iZLn Lbh"P~C.|Qr/&fi6'X{M3}{sLḋ}zA7ޝl.cͰ)?攬/^+PHC@/c Ö˰TmaXݝzu,N_֮0q Ԃ!۸$,bݮcZ?i."k4d8q|S $o۹oл; /T]{#!B!B$Z-oŖĕr#Zz! UuoO[Xr(^"DWz+ObU| {?A1K؋o[/8]ހʰ^X[cɌ%\^Cn.)Vu_s3_fċPj4l=kW?%:[F܏;^Zֽd Vn=hU5 ~]!7lr\Vxجa;o8n*_}N@!|u3UR.4˝+gZ׌o?v]s~!SH \WޟEA$h{VT$9qrH,` NDi#Ht = zOB!B!$:It|\߰gi4ujfj}lHf>x7E峉0W?/<~«z s:DIbhj&NjlZH\]ZI7可Xp\f8~m'*R2t_$rD5*uBwCPhXͮ 5{#)\mHe7d]'Qx [u!^cYqƕt98Xm/"F;m.}*mGB!B!$:ItvIkM,xT Z*p3lR칙SE}ҙD0E[oWo %:2Z۹t*w5rZs#d"¦ 0>e_,5;Jpfs}|~`Xr-Ify @?2Aj E} c +DDG i1xww*%:NAzL!B!B$='>sdBBUwd| sE7~"";}ZV"Ui -\Jt lڢ5~o$Hr=~5޹6LD $¨EW w+(<)ysB\nU<~ ~5l}CVd">ibݞ}&z$:\}c>z cw7`*Z-N_ _q9O!B!D'R!ё5enL_{WN+/ѽ\m8|8Q/X-ײbƩ/=hDgɨʨp{tItzaIVDXC6!meVFɝc2@j wI' (vM '|5W,=eEM3(J|v]^Ue/B!B!$I3"`d{a9K\FXQIZ$Ktcș=ݝ~6DdփɰB!B!$: rr],G~p\NsE Q|“|Nv9 KU`ystx=ͮcl.+CP^xcZ$0ñMW[I5^kZϣ|5Ֆ\u?䞴l۩&g-@ SػytQE>ɛբĄKt~>[^lNDċ/E|%Yx@j^!B!B#Eh5&EeF>+Dr#!/Xx";g?i\!撹Ht7l-#\3? ޤH Ǒ W<_ɟ}xZĹdLn 5pIq-Q{7P4l_8"||{Ba%wg.A1Kkݩ;mDת}O/]tFxvwƃ!B!BC@.We*X\y+i[SN$:ZG>PQѻ}ӾSEbڢ5i\$s`e8W.|u6N}sCYPqB:³*ep-շ06x e}.I2ڞ+V )AZv}=(zwD݅O~6+4.J'[B!B!NBs벊Y(ہ}wm/=*/,S5)sDi,<}ܵ+ysoNGJDѺi92@!6j5ɨe̤۰U,JU>xkJgZpwEIt᫻\x; z}E 1>}f0[y028 yY+3=Gaw aqjxQ`յ&TքX #j7v^-U++*nJk'o[UwEJt᫛'i;*0\=IdWX| k#|[eOֲe5m3c}bGd0OBhڄ!a$:Hd#ѻ;$:;(`9-( M0skqɌPB!BItvƒ\# $}\ɍtm I\%:\|3_XxhҮ]vJbSXia0Y.ݰLn9¬C $:9&}{De56.C?o E{]\f,Z[b@nsBոTlUk$L9bB}1E> HcHF8$<"@΢Jt*љfŜ$BSz $bx땪T2!B!BHDq{}`x <؃PpDdK1{O~|^$:,8% 4"EG];Pg^c,C\r$::+3`Ye\w^.Pk~w{]~ŷmLp%B 5pCG=^r%-€ YjG"XܛMvI%8{ִt/%[6|&ղ{zu6B e6xH8vnD}qb;ƾ7kiҗ=vEQEPn܆mk۶m۶mVlIflKp_tvxyTޏL?SQFɋ7Sĭ\R,rc$)<Oa[a $:v=f"=SE猷iQxE#*.3OHq?ߦ] خZ`@F(aY>+CҧX1bb%bFO[~wA2ŊoR,'^4:۟|wJɗ^1*˛pqFNI9{ HtHtݬvr`qq3cΣS)N /7>n&J@`^~G.|0Ikm`xïM<ʶ|u-( Gm]:/W7?}-{{]oDK*QRJޛUIȊk1|O@x'rv\xz~xv}jr>OwW7G| @DCKyOo^)1H` yPDj#l$kN* %y4 qxLMY.nO355'-Xb.>R*!j q.8*f沇OjQ)_YY^&"(es9TUex|mY&N\LÛ!3| '2+knk7_~(@{ygQ:RUUFe+.k+ sרWLvks3>~49 e,q):< P>cLO;;&_҂IENDB`structlog-24.4.0/docs/_static/docset-icon@2x.png0000644000000000000000000000417414645734712016445 0ustar00PNG  IHDR [iCCPiccxڕ3ot}lFl ðv")6mcgzAsK5~ֶ|_ޣgZ/nvַ*[<t6OW98L45 F`̖ VwTL%vO*yU><ʶ|u-( Gm]:/W7?}-{{]oDK*QRJޛUIȊk1|O@x'rv\xz~xv}jr>OwW7G| @DCKyOo^Y~kOA1aaTX? c"0މ-JS;-`艸DhJLQce {x"z4j-4M%JU4ERH0דvhEP|aܵ% QHP(mvqm\pߥa%1ы 8˖5U bBt"Qr%50rI%uS"y_N4^ۻ"F.w~4;ŴCMov@'iUGvZu(3?faЋbLz:#c;;?Y?PeOī$X׭w.<6cدo5_qqJNd *p!4m1e"d0|܈(&9?ЭAvMd-ɗϞ[kZ!V!|hd0PAB13fbhАEʱEZDH$qEA "!V(ɴ۝eA:#3 IVx&W*YUp VldipcRqBQBd"q%rQ{D ~8B0Y=6 c{`j* \љvZ=>[pD,Яu1gs#Oz*u @OS NzAr=ׅɁ"΀!@s"!~acm\O|Nw t9Z˄RKKRL>Jgz "8EBd;Tщ>/Ri<0:)°GWϲkkzNOL mvc"Qa4BNsn}?pTڧ?O~l@A#@ʃF(7 BArDIBKfzS ^2/|W&&rTt/"aqMoI]o;vy#&w] t La[Z"j'@M 'rL0E8h c3ʗ:(+6npz٪8K{~3vo+<]x˄ Y%9j[7C@zthOSL+BjL;E>p)S/1,o {HWR4|g4nŇ/*ܐ01l\`xn6|& S-??F_ 3!pBevjx.;/u1ȅ w"j {]R{STLE <@xc`'v½:cn4i\cJMLHD)ѳMKi!%vxVS-]GWT7jy֑怊iK xkGm ~Gj݆1{Y V'Q-!~#JMC҆Q Lj4iT(r]/V6׎\]-LC_x8?rkx D}D ˣVHr;. w⚘?l9ttwl6.W'{ xn"! la!%ZL%ϸsy 쌴wqt5c99dʖO@D[&GK8ⵖrGh^ !zI#YJ6:ZǠj /zh.a#ΝS]ge5Y!V֝J ղ.+:8mӦ%+RQ``@C8%0vԁP\ȕ&/@`V)kV ;bcS!'d Χ' jw`˦^a&OMl;#po&{c>0Ul-̭2g+yKSR}`恀ah8n۲L(TOj0]'t8ƀa ~ėy;lRsyJz Ҙ:@2;MrDs&э/ӈmt$j kplS,`(wM87w;Qw3. Fͩæ[eSpPH-uBm:G,&Lq$Tw?o\UZIa!uD Fw~>#FOq$?8$`(>+Y'r%Z Aq8 lI`Dok|I} A) :r9:!AIhT( ,*kЬtwKиe۝`c8;Q$: rtZ3e};<;)(@Bb܋H6 oqJB60W+:c){u3$v- +#$ץwHpK?mۏqylv*$*- < Muѫ)R{>G$j4UR;AQ.t(S3ovfYAAy+5P guSB%QCTCWu:O,b-}Uw;?J6^5!"A%&vQhu9ٔ8>۠?'҅q"hP(d#f!ѝN h&wCc6g+z,"w xа nÃF? Yg6tYw]PS(E2 ) cn#dAim^ͺIOrF[Y~o3g _60xKrG F< K!_CƾT@5L70@;QzD#i]>9Wn`Zo=c =ZX {M1/mEO":+'FVIG~e wjrqSq>Ƙw 0s%'!u3w7fr~TQmr7F-~>0tG=niUGhC6AkNpuly?;BJw| /؝dukZI5qV3(*mƹ%Qgzz:K_4PtYGPn'6BF١wk ^6G~L@A :H e؛HᨇYWokzc:'ǟThi d)Ӂ"&98n*F\}kUm'ys>ѭ?Ht fi!4iv*+juٗҜ"і$gsYcI 7N&s 3~}nd 1sʓq,YM-/hPkkVUu7bYՋAw=&[+924q7 a͖qy/wޮE|ݭyz!G *%aCWv*XDAA|vrEiyDM\ux-?X?tKr2.f LUv@qJ}7#|:`E3uvrvbL+(ju}pZ,gM ZAK}KR_6b7j9;V6 <פJBMBE}#n.e O ;̽q?[~ Q6 Rћ5 :ɉ]0:5[Pܶ%,w[ռ (a CB4cHw(\%uEAttmݢ`dGL腝܅GvdTm7kg7}{ t1^fQ>gtjrwr"oT97 =[sc&}^26㝁!zwI61_0e7o;qo0=;E>~/02007)MySU`!f9`(x=z l #Ւp zapX T.o#gJ۰>1l' 1qFiz Um-Zh#HsGb41;7?%@t[8-X ڍ_ OLSA('i턜+O)nÔCY,BbeNAYSGTYb\'*bÅ[GYgAK$IzbU6"!2kY~^K:nY>Qϼ$xǽjOH7㚓%µA[ [ Ts۪U{'ՆbG|*C hrcd9hz^+ZWZV<d۳,>a/b0reZBޞXBŸ!vOw "ڣj1/?as.admYA}Rv2Kbm^b`?-׸Fp0 7*e\5b-$U-!b۳?񃬦7 qJp=t㺰?.ϭZqm [-~2U6;v­~-0}=[墂lP*nLkfG;"gzxJ`onjޝ۔I<^G$~J|ûy ð!z"~  ǸڢP) mPo쀖bE!>fE=}L AC9MVc a[F"0O?\DNvYҝDVdCRS(}pn0`H8i^wXF4ޮRTѶS:ߴ*gif)M`@8wAV`$w("=b7E'HFKNR'[ R@;nЈwqaJ.R@4BM+gy0Pp;X?^{jq~).`Vr.!  ;߁ʎv'qD~7a?# ď}t )?^r@٘{.~%ƘvjL@ts[C12jXbn oF|ԇ% "@q8䅌wy.H$`ck 51|~G9P-rǷn/_ጤ_{VŬYבK0- a e ;;+%PZAZJ18Ӑ%F7\g UM4|rJehvH2R$EF(vAZ"]We?P Zo^Z9L9sFNⅬv]G$g%?(Ɂ{D|gEL 2 u'|:Dy- /p)\6[BEԧgA ?sS(0CgqF4t B<ߋyMǁ6z.k{4v Q𶽏B 7 x\U-neHA^BWq`i Dg2(.歟`)NA*&q&uckZ>AзAjnea>oTnM?Oa"V n$>ECjӆJ9jtJ儸a}''3Uj#(ӡJ!| 9TrvI@ ;P&}"3zR mȞJst2`Ha ];vhN¢?&_$C 02; ޖXyaqbb<ދ/Ǥ:?ڱm mW  H7,.9a?TEfqWkۭtbȉw!w#q_Y<z>A`Sx1!nڂoIknqZokKq$=y= a ;ktpσp yB -f9-jڒ?yy|eE~HݪKЋm;{1.8v?It{LpMn­'qѹ^6,.^K今lA]`pW*W9҂+Vý7 ;Roj6u.y|?T݃yUΤwE*`OK[]%Uwߐ1&D%Sf 0A ]鬹F$-.<WT4PKOb5Hr>l܉q(ι7}—9Lo:S=G${Kwٗc뱠!??#}Ç* $6%>a} \{﮾1-4(z:uZ{LAۉuex?q5 9>(??:Be4N0J8vN93ew^/|!yR Lʈt/+ = ߍ:.a 6E:a6.gjƪȫ/ͯV ZFc>p 1aPJguPqP wۼ,av5s ,tz cB+-x "DMT2wOOz1.^Wau>ZF܄Z(E<̹b,b#\/&:paC]9UcXBWuĎA(2= e'=&Cig1#OqĜGgt !|CʍMǍ<@@&DU.m-dmdˑۮ!dͤl0?9exx2&"#Q3d,䭛W\fc94;8Ü{,/e|_j,lE}7_#f\DME;KoaХ;ն0C8R-p6>O`ЛZt5uL\5K㈬ z=h }.傦;]LJj4v̟N6j[#$&,۶W}Qq[4G呼]Aކ홉[T$n*7t;q#r1 r3|%!"F.1p c 8*_/z8+#dQtQGҥoA>x! !Ub jy6f|+7JCގ iNnU3Kz1\L{+P_$/4J3+sa;mޛFa(o˫DN" #P&?k/%{ǗM,Q854gﷁ^{$U)mwj#ۄ91 &1/`2`9 6}pi =(jyv%6QV9 WnbtcSx]7R$bީUS,-ÚnDYD{e(hW7R=20qyp?=P,[;8UH;L 11PHZԓnij qiX=iuXcEOOQL-Wdr sda@أAa&3ǿV4,94C~8٨qF4oN q vAY Qr+ ~~ΥuSmt1hFbnƱX;-v\v*[g^"y[qI {hU3.A忭ώ6V -@C `wI`&vW\# /&pjc~ܚ6`]SNj[g7O,% 7C;KRydvPq=7͆ۤ &gwPyZ1L$,CطpX&=Y>U4Klku@?Vxoo&ʶYH3l!BJuTiY&4]ĺqc[k2TmhKor ~)@ pLTMbkor!e )(0'C2Zd2я)+~Қ ])fa_Tynk ӭMRb'Ojy"#/5vNNA[\6?6 0 =س:XpMUpǒS?ϤU |7p㳯D1AP]Nh?`bD/.Ts=):SGhu&p,ﭰo}A0pQQ/&m d7NdnCpUG(yvEN~Co cg}J ^͕gHT_mLhlOx XA^ sK?%;}LjȖ~HLv EEhx/k6R;Wia#%_1?623G0D/]!;- c/#'1: 4bEWikN92%deԺ` e,fA?9`U `luH ie5mqU PDZv<^071Gaj3\d3E=j!7v~G-mW; LvL\y! 0˱Y~zdpM0-wj뙘}뽢8O|;K]@ 7F}^(|BMkĉїճ_')8dw6&P/Xwg2`cm1u\OJ:ĽK( P)r%t { i`g\?-=D X8*=x/ɓX:*YO\spr͓MaHGpS(qۣ(Y#P( U|/Sni@nd]9|yzHÐBuWTzk#(E.Blv$oJ޷YAo]y4h5[C@2MpjMAK<R4o+pwSDq)<κz5J(PWߋv 3 7^5`( ~^ZĆk ȼ%lUs2w6L1URo3g'/J:'klu#e`vw7x8dü+sz9ϞǙl`[{+50Tre*z.Kk:Ӹn%a08 |<,U8Ȝ`ޑX̮q._A@\+( Ԗ5}ș&}m|6e9nk<+:'lkS$tS;IZT1# AAOE 'NʛpY"Ѷ2,жMƴ@ 7bXR]BMPyїArYW-3W\=1q@2MzIƒK&mA}-BWH@Д뺺AUp:Hp!NV0RY G!"JYGdrˌ>=ې3Rߢ]xc&D^1a.@Y(Ɇa4ߓK?hɹQݑGp"kT{Vm{"ffgŌ3 /Gt'eG[cQoXnB@ׁ.3D )=)08^!b9[mʘG|ISw/ FtԌa\;%.M&Le az;ݽMckZ\seֲts/}㎧Z@E?8;$׀>>*0XD<'{j_7zV !1 KJHNt0,Lp5* U^,@5G#:epM1`|aNc ayfS8IjV0[!CT'I4!7rӓ =0YY]Jxp~lĚ+ }Z|˟a2Qў?׻̟8ۉK\r4W|C`LsY8({^0<>!*_ݖRv{8觭PnW"iw1\I蚽m߹q]0eq%?ˎ3eWZDEEU/jᒬ ^R-K! yA~B{>W H|@1 L[INM󍖧^ޓلkG xopD %;! ý-i^o~)[\6m[2/vGg+ 62=';_mPxbN)ٰ~iP-mM$W̝Ԣ8m}K R+PfDbСzõe%[zHXjްLK #)tQbtxfqM ,OZu%R Ѫ1u#)[ҌrSF; 4 |C$΄2a?qs`͒SG†7"38ڣwI,Bv,2oY^ty޻1(Vr՜?)|=)P +fV/B&RfK: B8_{X'-,6) [`alma(^`4sl`t[:W`-\FdfaP&<_+m/\`[&SX}1F[@ jg1l6%RnG#U88&UV U)7+Gi(Vndq8ҼdG%Ζ"&yc(PD@ue+wt9EakA6 [:gPI,# u 17\ڰmZR}e-5N1iUXH]1,t?ρrD()lÑJ(ojI) a\ɮ\%ߒ-t֎E xiGWg$c& hxa]dcnU-Ԇ1Pn@g+/!aڠ J*JDLO)*h3Fn9W9ƲEE%NwwA[P~fQDAD CQ+11!2HƉu|_"h=?{vBNvâ#+.P8Ai3&hVP 'yY !#!HfsJGOORzXrPĿGCmUĹUa5NBr]D0WුBNe7tNjxL/Sgy i>'!l{Tv/Vu-yS9tKǑ(Y_edm DU1Uմ3WASprzQp^dcDpiˏh>Cxfx, Njba"M_Gנx{3&+2L|fݸu#}Jd}˯J_Ci|$ n.%C8hN"0>E_tzB>y{C4n"$kտynoQT̙dW-{f;0Ob7$ķ ,oq̭q\*ӫ&9٣|X5/".+Up:=&*.#gCb-o.'"r!8"weCnGN2' gaKg Q)$qu|Mmh];!%wE;u:yw&(9%WˉX&i̧xye'$uF<"|aL21 p'>OPrYh%9vbswqjfzZ9cx8ÌkF 93 )\B. 9KQSoLho!E)v9)+`h6tE4&2`gZ}pZ/n!Q<ᶤ9SQL,3KDr1Ho kD)ȔboI7Ň^o3Ют*1gDyH*< xj[Q/1K@zm:Y@Wˊ`4ɥ: CjԌ% 6>eq/l+'mF=ߴKAiL3ͣ+j +ln7dSOY.a&\KfzD/<IYX:ʀ68|([8pF@`W୹NKW۷ Ym9[j{V]K| U( ߂cr k=sπ ? qՁ4q]G\\%u Y`$W'sV*k r Vsˤ<5̐bi xC,Rqc95ZJE2![`yɠg 3eBZƀsUѸsMI [XSGNfHP̓Y?!L T4WOBcl T}c-꧅< ,ϹӅf:sOOqxnBL[P;PvA^.k]졙@ƿ;At ]2uTCaU²qg)pߙ';<p#q蚻9UzrJ9\G;(x*0?kp(ىع ݓ7I;" Rh`/(+ ?gg+0̛Xsra7D<ګbυcKMD-nc Qj/[ kVؗr,۳g;eUahϱʪKy$bRyt.^G)5xx\Xq6$׋ؠ"bF7rxާJ>M. XxZqA}@LKAWDPղ!TE"HH_#bƋ*^^Pn5:8-HA̗hKCߍ/"䗂N8wރ|@`6 f`dk ^rPwqKLknYcGс|vq0dA"2I#+1\[@Tx)%Gͱa u;/IL)FK/?ߵ@<4/׳F\JM*qY "ÓY:>_K;Eq[@,q qK1⛱&o/`y]qMQx ̊6ܲe4v-֐lG,OI6;K"bVHQ`S(=Q1gMjgF[掛!y=hMm@|Jkőbb ]U^ذny(qeRFR%mY $ 2(:e7o\žR5ͤ+^,6[SZa {Fqv흘D}bHs >Đ=aXb{"d+JʄBx&zT$ӡmWPyZ-M!YaTp'Vqg8~ [,I}ks }{%{pTm\bz?`c+Za,v^l+ PqU8Dv vn5^rW_1u Ĕ^N7KClrgp), ܩQ-lop g^-Xc7Y7Ev}my=ah\ܷ]1FۨђR GB=O,Q$A"*0KXy[\/1.#XA<_ (%1A*cN,G 4]@,*g $jѯM?hY?*aG8V jQuq׹g A .8AT"Y{,"7aqE ~1 Ps#]2K`0F)\9¶I[p31v5q? RDteД戨 k096C k7GT=ӐKD~CG*!&Q $[e*E(w +S6pc:{iD1fUWX/%\ER 8d[`EnViXHGp4N. w;ʟ'[Ҋm2̇K'h盕Q_̴Qqd-!W/ZA[a?,|0GI 0yF)c7#W$ZmGKr2\cs 5  +K*H4fܟDn(z ;rjQ<@jp3J"7F% Є<qR )i\&.qJ=/Ť5| \zI7Sk٧x?n@= ш')0%şDbMhU>Sڭ*2H":vE=g)k)F:*W%P.cJ"+ Bjy)_+!1jMmVH9OhvSDVk|LdgYVo {Y)0>_A1Awoʶ6QrUEKwiȶrV`a|aK ~, $2 .q|RH$RCf%ac.Y*ycm|Y1-Q3x < q[. Q&Rkl(= D2m9 (X `S=! 1{${Ӥ=(Iir6H'!í>" W.c;K! ̣x Da'D\ZޖC,b# ]:e[/*3Q\o< H[!$a7޿cnmuf]R*ǴȽd~ b 0W((ld"v b6drKVR)ϰr.~K$ 1 H};yMC1&bKK-b@OPK"Qܖv=#km n˜%p@=$ǫϊ3V2L`uY(ܨ"^ !x8įӢ`PĞГ{ {D!~)Ԡ0N ҠJd`dVjHPj56)j1K%=t/^ |LWZB7kNKTCfMoba]@fYʭk̭ƛ?(4 p_F(.%IE2ɫ@Sܲwd3#rpzI6v>p/#ĵ$1Hܑ[b@Q=Lg0Tf&8;O qW]\ Y}uZۣqXﰓRwL)@M v+`!Eu!`;B~&@7{*^VWwd`()~u5yfl+lG:0bD /"+VFxy`,X˨<ؽ3=#``+1ÎkOJu}j `;牃:I{O,v)vESRvmJCk_8Zm&oE!t&Q߀^{'b1G,Sp/S^j5/>30BaDSvgtEC=v{Udpq6mJNa[]SF c+74v3c.)2xV,3A6ӽy3gW!G5"k3b>m&>>̐  #~ WYF$0o"qT_R8Z`W!FOn@ P'RPͼKJ9@:,b(Be[„;:+.R_uؓ/%NLBҖzD0 ۯ Iwb>M3oùyڡ@[U? >@ 5vO Uo*B 8]Mm h3-O܊\ºCN?ŏ];\HsL-E@Wmyӈ::JuL\Knm4X!ހ0Fxe3پ`mLXӸc]e&3lJhƑ,d8EQl~ }kפwfd }I'/7n7e#%ɲy"ChJ3rI83eiOѐ aSlF "lѥf^u%-nzِDS\$h\3-o_$ lywS ÛFKa7іU> u}88Dj]|Qr"S ΰKZ엞ۀͼPr3jZHZ!#<`NZ=TCbfu_!pHnX )'$ewBvY^{cpO|F19!>bR-ve˶5lӈ<y'98M ~v:^M|+ [F)t SL٘b`}%gː,<+DM J';2.R!#0b#D@&?Xp!'\l n8]/\x~hŀcT`I40c\t1 CrF HA4:aEMD !>JZjA^ # ? ~U]K<=9 1Իmٕ*i裳Zv.G/aa`0/{n19Pf7֥jn:E+|ES\W\MֹC I2g.Ք sS ~]\[Z1x`!b奪R@eaJOS[S黴V@LDqI"DBx 3!iLS% 7E%lj$#EtʇL6ifb_ [ [$ "j & Dw lnEӾI4h ;3iK2mdc* L08_\u bLX(?+U8C OQz@dM*$.+A'"a;reDH"X =_aFQ"sl<]8 ~qmo,:h5ީEicڈxhXH8(wgF6&fOCd챵:, J{\魴:kžz[ūSRQPONML0n(@q$ 0FHAQ"C9BB'LH}=hNc& Dld!AfĀD`!· F8,mH( ](MJFB>:62.SЕA2  "@Bߓ Mؙ73wedcĸ! Q.D2 +QJ)R8?1PBy|褰%g%$vHҎpy*2DgH!beBaF]RY^UjQ~MIEAFF4cޢ"b$b!B0'  ,|vz"t*ze :yI,{&lĵF9yC|b$o5w\]ľ *6FNZrK1sep}3lcqIÂVLٓ2&dJƎ K׌\[ZYXWVUT~S}R(9Idn3J^$/(l4.Ca qH  `$9b!nA#ph%`q ^(0` "L`t`aAH9@DSeei$b [:c *Lߗ^tq-X-{Y^`rÕ)+*}T4\fc^TA'2#!=q3@(At0`D S%NZz}k1}`nQj@?%w~ۇb^ @7[Zb^%>Y٢N>xe½ɝe@` ɒ(05(?M@L >M@LW:ܧԿnÏ,>;_ ӇToRWno'x96wN٘܄MU79 =[ V ݪpo xnw;?jkIZuc+WPF6Ij3v m' jlQq@?=nOlV&7;9S'b?g ?mDgXq_x T]QY9_*偓cã#?yQ'% Ju#$T#EI@䴕NwQx8)-*eeuځ\ /Jk¡“@෵i?8IU~L5E 5D( !rz!):1EG@tM"GHUKP#U*,]oFqb ƒp^ުSE'7w O.j1j,"&(גZ?Y78R_?I}߳a G)Z%er5FQu{OlVXPֶPoֶTҒ!Pג!FW_lrN?OpUEm/jTFm/*5}\S-xu`u:!4 @Vz+xbxia lݱYs>PrtU[+../a}Pjjj a@+ lQ?UU X襸k. %]`O$sP?%VZLi8ʰ3%OiQ)bfg5@?mC?ɧTKC{eI}:ޔWPܩu|F*vBž>6wR YX'8`؉8 yAbZcb=> ɽ3OBe?Q́r~QT;:HyA!Iܙ춏!~ żBUv:1 ~pVUxSE4DI k ex$gYMH~A  A,"1h}9%aU;p|cod6@qjV3xym#@D8fP%XQy Ē +ހ-x˨!>T[NPZI]ܧv&j͂5ndц6NYu֯?[Gdqd Z2sIEX1*l]!B0;``L21K|y ~& :J L_Tz//.=QgGݨ²a`'?de'N"1X9 29=<>!$M^C\kȕqov;hqr?<&q.^RFp @+8l1bdf!wy nCsjt:ƜTQ7dœ4CNOƼoT V܈Ə<pةb|l[NOx]iLdenܨN./Cwl8hMڰ}x+]}Si1K \V b.u^V;4./E_Yob;#z1+?6r:¬xg W>8 V؄T)]b)8iB#rc4+z 27YE$anDĀ;lLSa(fBquܘQ7U~czu|[Іb ky%^)-3#zh!MCV31 d."WrhlG]+E\#cxtyza ;r(GL⨭ܗ j$t)4]CRP\;F{󋟓rx26Ìk?C V@:eLqح= ƘI v*s߫Y[CpܴE@X a!5!#;@s)k<~N gxpэQܺ`0uhZ=OdgB ܪF(2n~"zSj34uhoM Li0? \ uB-O1RB )=hZ88Udt_"m1ocPTQ4X|9!',W6Tx@vm^I.v7ƻք-#3Ľ%Or ,om1yW՗xd ʘZҽE ގn-Ӧ.oxզ`SӃõ8jBZʆ†޸ Ak 3 Z掽mG͈ٕ&)aq0; &moy$8~0#AS#/kF Bʧ. 6Ζ%l#'8)%ǩ#eG1Fn)a}Lf͈;rk-';@.ū =]H%`!|pGct!6 A^5HGz+C˲:Օinnd)]]BBњ|]Gm~'zȕ.hnHrϾ:RB=8C`Qu9w('́3qt3#:VJ qx(b)=O08DZׯ;F >K2@ {V%h܊BiyCQگ *L2!0(ebe/F"8DH˅Fmb؃W${.v D]VNEZCr)J@gS Qo5!z۠$Ki8r-{QXN1#3c F;mV\*v/]P(o]em^g`I| "pȬq#:U=/uQzӣ{OԹ<})"/R.i-hɢvvi'0;= r2|>8% 6Y%-[]JZ\ug̸'-S`'r,}J`򻚤19q?F*̓l4U;h7_J< ބ5x `3$ 77vb3'xQ50k^nțy67~#o̠P ,a84 Qȝ {(G_3C3 |o X~;q:l#E.7UڶI<*Hk8בBe!k?Ck`K4}  e>gYek^w4(=lyS(8R1[!魧g>gݯ UEe ؤw"?qA3Ή tG\5WE"Ib *EhguvN2_RwQ It66]b=GM788ujȳYVAӉXk,盭n>mZ񪖷`\n[kVٰx@*RbByF]loyn 8ցo;Q{[q^H"ys9T @ŐqTzkM>{{*KYw:¹ \BG #9j1 K'tE'¶YKsߘ9/^$&yH=.#t;v^iJUV747iml\ ϋ RsJc)^@I t^>ƒ$a ["׸KB2 |P&3l, 3G9 i|O"X}-0V05d.֑ăD!̵/ӁA6 b즊ŘIE @ dJB#>QGyơcs t ;q%61%kcNe,EN'oXʎy̖wCjNq 1mzk [ɸ \NGO \DR^5B8ˁtiq4ո'|d %3P(d)cÕf*s'pC1hw&>@,9ǩS+)s#y9n]M^<&йZ,=a2nMyiL.<.)>z \CR>Eu/q{9m?ыc6MZ$lzrEVZ|<`79 G_G ^Dc˄|d>PaƀśEU8":ґON0v7CoQQ7[%o8 wݳeP OX eX֥O } Pf4}$߻@S\a ?Gm/PBya"3@Tߒo9F1,_?`qp#>fΰO]F}p3k1ī`>}@'LM'$ >ɏ#,AI:ñ8~0&x^ty"\cܳ1۶qLTDۀAS594h!B^@e&dށf:tb˕la-W~5X`fV[gRpӨ1nA~1;gr:j>HUtƟU / q.\Z+6Mz-sFa 0W@]FX@{FsSvժȽ"64uTA!Á Gtr~Pu ڨ[{X+|Ll@sB@ `RX:+h ARMUubKۋv . dk뒇aDޜv -uYH!?~&(W'Ґ;߫Y ō}~5Ui>ڭwmq2-,sQ 6ykci/G7Hh|8e\ya1S  ?eRowo%{I<n*UMrDrхlՠ܂cvg P2EÅ^`p7"sW=-&"nI6ybu 1oD9=zZJ-v)%\+j%ޒ#Έ< EAyIKXyt\ `Ä mbėR`0TQl4+ ô\ ~w@6,p< *_ PD/{+".E($V2߾3lVAKlbJ#p=uF'<:^Vo?CdE6s$,]ʗ*켸沉\O<1PrZu+kc3EC21 fFXsf0k85+?;n} @Vz=w"dVvF\02YN &Oc̀O +(!}45E·%kFD eTvQcD~)gkm38 =Hl:U)Mds |E^y]*?VZ9 isexjG,sZ3|E-Tc5^һ;~}(ʌU]U1C@bs,dgvgiEHHx+?|X_' pb!E f"!.dR8k ]vevk%:Ey΅~,`@ϲI}ό2p+oP̰ԇx#$ LPL+a%@ޗ&{}|S2}@k9' Ӥ>9@"*3{΃jsuB,{$Ѭu;q' xH/r!%9+yf1jip,ADt2M`pp_.PM04EC~7 r(NbO]f[Orv ZR8761ȦM18>Rg0MVQHH toi-Oܵ2 ƺʔގи(QH>}Ʃmc)☜W2NI>Y:+C*] Xg"ZcI4#yM(L% b m6`\焐p:0:.qS &B.U8 KS&rx' !fH;l#a^u]bz"wYx9r|n{תlA `>c{&I!-m'ӊ!(cR 5C^-S,kZ$3~1ph]R^;]OR50g{hcȣbyCzn ŠǐFOr.n/׊/Qb`#34'R]Fd 0E 6f֋Aw^$TDs7dȤ#=玞Day_Sc]9p2|t:r \)zTl =ĺ?0}h8 K@iܑDkr)ϴ|L6;;ў vO xP"_cx: $Y<U UB6eF`'u Jyh@.k;,dnf[A 6n*,®L "@]g0_y7= Unpn&Wn& xE/ 81g'H2+y{2 wm7нTh&{#mb "aj/Ui*$64 Z y6z + >(R\wt(%vt>$Qqxs˶=C߄ ss p|?XRg( ȃp*]CEIFĢvWNÖI78#A9I7Ljʚ; LgF u]'Z 0FH- a`f*1t 㑑.a(92qmCo&7EyC !,YT홑y&Rw~}-i!/zc|W"PI8B/#+3Af65z^!fsət{2o|䑅Oz0 {ieA34 {B{ۆ5kon/d lk8yi+}ܣ. +[4A!5Zxg~dl`nbݣ\x\̣t-\]2{!'৥Z8<2:! rȤ,wđyHrmwWa8k" @Y=y6dO, eɝYbY8dc%Fs Dum@1;xL8Ïk 1Nb_݈#Q_yjh'yBCmr& P`WzlqhCer_ Qp>A1['<:Ȃٝ@nQ]Ig"h_6qgY$_9Q53LM]$^24 HY1~QfQgCZ$=&=Kbk (IIH=o7MsΒ鴏)6#_r.[oFlA%\d])4_:u *ܫdNKyGF J7/x^'y Y # 7J D+$lZ(  Ӄ}e0l9OҨ}x @Gs|)&̐`dyL6221&NY3i*T$Mi`du}d+2ֈ{ H_jeet鬒@ف7ޑ<#|,<@!qch(2S4|@ê v#L3T"sKn=! w=3)lqt]|=C,dMa" :|OK7'd kZ4Z#SdcAWq*h1&[g؆6."ca$VN#q,\̭)U˭c俶v YCCg9EMlc'|Y~ XK_s!p= t_sin/J/΂-\|aCG^Y0+IA&^W{8.ܖt:7ѼB @<[+FʌXn]b|AOxWQxޖͺ# 5\q4B-bIƓ-#vٺyIT'mO|\%[y2-KХw% ($//j< 1<;RFxFHl:] pdLQip6/OSIRxE&0DwgXIޗxt93˅BXj/XKe7L \{#a e3{]HnЄI&!G0?/{jIH_Ys!ZxwΗQް!|8 }Uc D0'\FU.0v2DPp_%4x ǝ | %8』 EIa7W p "Á ``0_g߃bu80 ?`DZ-p0#( R (fe Y2ƛAFIM1ٿ^)YJ){v="Mcs#1S8:@;T+9bפ..J:^B\N#uA8`jrq܉B8XO75otl >Ϗ}àȭ1ېx`\âZ0h 4fw4֭ϓ##z͆ȍ<(C!D  \?%2 tzPqaK{Vھ::E/Z*CJg k5k_+xum#qq1g7܉bhj[_ju$0S#2XF6(b|J(/nY[bѢٌv ZAjEIu?V $.5,MfَâgwRį !\TƄ*Bq)~4uk#cuAY) cСwxtMX8m7 [a6lD5 _y3e3MZ oi ̓몲;y.xеe'5d.&Jh8Mm:,H k2OAOaf% Px (h֮^(k9HQpQCo;>d]W5T䜎 A}xo]@6)gI_! )t&xڼjt>&4Ly㬼CoE;$ֻKraIm^@x4~jqqf\*-rqr}- c4H D+iEBVM?vMR͖&WSVR  Iȝv&f8 x2Pcf[1`S O,mfOh-H̕ KN7V&e#!aHA Ԃ5 ]_Oа5̐}#ANn@k޾@ٽ6)][Y4+ -;dy֨@AJ^U!Q9QXop)qnjQPlO>7{P(1ݖXxeN;:%Rb|E7fBۉ;a3JgPݤ@kLApq'j8W;CX*x+v`28&4Bs$&& ;Q#ڲOp⯰P$)+6ǑU``9ڱC9괇t[a,ha#[pQ8yz`$ pɥYT@ 43 Lh3%4Y%֘t`٦+RhTGD 0Ю=ZAl+ Q<9Lxܦ8 MjR EWŁ[3VZRC1<kb|9 fR kM[Ͽ20~BEv9i1J60`n. B툫n%m>r_)ln <|xwo Mi!gvf!mnIe6jm ZsN sM6׸9F/ܨqdW?ָ޺ghiel12g6gܛ5#vGyhuw!x7Z`Q9R}`9a 5Rs:ua@v" tMx3g`uùM ]JðvGs+A;n(s: F+12Y|]&W&,Ź|)눓0Q;AׂVM|#|||ſLN6>|9 *cFe!cl|InF.?c4-_PAI&W1z2G9_X]홱]Ē^M݂z)s7(*Q&web/ՄA+#!Xr9jW?PŅldԼբ~.vEq]oHU9#  ,n,CDVJ4_RR\)54`!V%|!+ Y0^rtAM\HKPeM Ǧ-C@s C!M&c1!MT~әR4ZRGˠLcbd/=~ R!0f"F[]4 yM;.KQΊn3s~ǎvQ\'h'ZUZ3TՃ !׹4]&*⫍VpU ]N!I^? >P1k;=<: vRnC*:/M訴{~sVI\h L-5? wo<hH8Eoh8+(ֶq$SJi ntҵ%oMZki'y,Q *%14@ xnY]K fYekLTiJ`^\-${/3N ȶMʵɑsp=S{֝{|B\ukxQFsДt iQP:d֭mױD׍xķN/'*G|PrwVxɌok.CE["Lb터B8IpR9 悁b1[,aj#nR /QSiqVWRt /L@g#O6`;ܻ:)9.K!7GD~+s\hCh8̍8ًpA|p2eo9|1>o72deIX$H :,X&:VIq6JFlBm m#}oY+U!@zSULdU(;U>?=uV+~hu:NQ "VMi6-055ȬOM5.Bx\Ōp]O ba;7*4qAx ]@lSnlzJblU'xAvmK`+BPJB~}~))[Pz{U#:e!dZA\]vYicxtWS"٭Ujl|Cй'zFDyyuBV@ZOqvbMmw"jHP3}@ ײ=; gDΖ%\!YQYٗ1[8??1Z{7 A-n[1N7m%<_:(Vw2 _]T.{ArA8 6tLCm "))JU-43JB憬bx%[NVD {%V=}S5)TVg4-# }j Mh!x76fchڱn.Yr=E 8 Vɺ)n~7TZ}>[=&Ҳ%@'Dݔ2aQD݁llc>RM7h!\a~<;/MW-?.W^[ +ABQn_U{w#mj@R9W O;;v8A(;omona@*Qyi N Cc|NzŠ.u>3 B_9x.kK֤K~_.I`A'&=VW@e!' { kC.z9 ?~ (pDjev;w~b\>@ί"5cu!uBk,]A{ ̱tzaҟNgIJƍMʭ 4$[tXm/MZ LJW'A$3 AG< H0M,f5(bS["V50;=0$vuAsry߻v`hbΊ̠ܯ$1H:fWlbcDG`RS \pk;)qwGt)_4©qYQA"eu)t՝yFRB}~Dž`?RFy=Yk3yz e A\p,B"UWNGb, b1#8;)%q .8#tr~hl3d<1Aaژ'`͜0[No>5Oؗa;Wխ^e7F-_*?Gıƫ'?MXW9ɳ zuPI\hcVZprf@J>? &cf>Ě,i.A:czsX']N>?ǹH WK,: (,zY/hi"hdq&<@&X.j, X뽲 ߧU1u{ վ@WL M p5mZ=4fo}'VS@\AIv餸x/ a¹>ߠ!ˣs`6b9o '8@pYM1MCƭ̭0:BDD=)K ԜZAo<~rYT]3swnBOH]#s{y@?e뾂ؖݸ6g1Br_z %F||颟-X^g @MߗzwͩJB36)HD?DŽ;S^&.D SWl @Z_V`1|8K8F~>$p{~Bwsf;L*Ķ ߀,OA $$N?s`PZlXc<V8( E="!0=@&[U"KBS Q> A1Id"쨔Uҡ0;I3U_ EOZ U{gs"F\736>t5 1_ u \ˎ4 %5a4lK5OgD,su?ducV"йV+Ne=Ԡޥ:zWϦ*i+fY LH<伹@8EWC V ^ἪDneU~hUg&cؠj.׻׃M5.ZǞZot?AOhx0vFy:GeվM9ږ:R%!WKeS?$^|eT~ko -O5ľR _k?d`L[ P; 12+wFtQY9;APwsk zX5JP,c25QsXBwҝaNDkX}\Xq~FABFHמ1I@ 64^>_f#an.HœS ڤݦ.ƅ*];1 3F)5_;gvcC{ۋ }.L`a%UK,^V-HDC{~7#CrJtͷ9*6f[ZԬAYX_ڬ gL18~mΡ ;J^H$TEN C1Wqɐ `&ggӬLU.ݥmJŘDk\E!>pk UTs_aGHt²Nȃ)N knNy`;kRU(u&,Ol5h&')G3 sJ\[㜂':6kN0ax|kruEfz+{A+y,h3OzW{%IZ1FA˱wÏyis6xm?AiIZ`QO yz xzezHJsbaE_ Xy{c…tʄ|2^^S_o*>=p ~;u6źϕ#?%Vc[/&b[PnQY1*?S|~T{]5殸T'vcSnѻzԳLJ Q~о1J\1`ii. M)yA5 ]@ ^>|u$X\ޅd6n8+>}Ќ5A)vq 6.(܂E!a2I316 w 6=k*އ)5;/\ߘ|sk ?V I6; $[9; ?_`@ J+ Qƶ QPpWJAQCA`lZrG:ofon,M.m̌HPyfs ??qy(x?덜w;A8o椻`n-ڵ #H\ YOxEY(禂=Zz=ͣ&wH*ؘpL!ȃDJU87 ߚ$صwgS:!UcAoR4Wg9V?? :gș<t'8 q;eeɶf?"kR_߭$.BL|wxKfJ ;,APoW"Vkr3G&1y`e0[6zdf4Iy.ze.^o:~(te MlsFDM;&o?ydb]~eiL鬠 mA1WHo=ˤCp?#2nC,w8s9Ձhɮ7]xLWp?Ylho,ڨtRaІ@]-Zz+KFer®ϱ pTE?_.M@M;,g%9I6sӳUYj΍Gt#x 8r[Z|&w:u~EWKhdX\V0y귺ë hh`w FB8%\B?_tIJqZXmX8uy`l/lN, gykUނ>#edv>Z;0Y&L48kEVȎͮ.ڇW /16?c/O>#Ǘm_9>a?dw⟡O0,K\w*˰2` 2 ` q_*[E3M r#.XS!Ň-f ,xň#Ԍx㲜.Z0㫟nM>(CIBbPzWyUT g8 5 l8̪n9kQV S1PУ\2fi13Y]> ?|hZ( :)lNY Dw]4$ u^P:Fcȑ:s$i O#X"?Jou(l+\ =$fbXqL~ۜ'tj5 qSUm7 JtfcRK Q]A@T0}GBz/}4eSr"җ KjL%4LZ;ջb+1V R"QN"pwPpPU/,3) ڗ/M<K,/jCޯ O04uW4ᢨư'0]1b-u"ҘI+S1!"vAcJH8YnI>+rdq!&9H1r! rزRfOF|ElKr)cप|"ćhE!>SqcD?S4ucJŶe^97>M^?!I:6τ2~ԃp ,[R\]YHG|3fg!%^NbmIE4"!;2\bxQ6@Mqg|CHwKE01{B[刬ۈz䖸f^«]5_θ*`/Q]Jr[D -x˄uJa]`7Pw4/P !CB56a@Ted%}UjfyJӤt_AQ^Iet@ ޳YKkR3RWt`jR2vd']ã6߲V [x];%wuPR3 ,늃aI II⥶̒eՅf`ap81u* bw#$Ώ,6( ͊C y/./dMm6bsEc{#sl]ţnxӇs<6oQ< ;fx%.qSEϙjD'b@Tw=_,tEh$9(pCo`Z(=Lu^N [ 0= :^.\&ju5MɈ.FM)⍘Ke }u m\ٺ |~bۤ"t. i4n".amIWZΎKS}b7kJ*OO4BHt{wReccS<\wKuZ+T#s6RQ*ǁ݀o"rݞncn9E1C>yQby }b"vU-ءhg20Z)mM/QE1y<-KCD wMfrvĊndsbj-fQoq)[80`;_/^'$/ì\TYNE(M*V袍MqZhoNJuTZB5r ̸n8 U̙7̛3]RYs]*PhU58w'h悪c/gDLblvDT׮DOhkO:p@ Sr&.ݎ9oYqSll7-=-o -d\")V(OP) 9T5 wKgt'ck1ஈHN-RQ=* j.=41͉LR bma쁜 ;Yd暪HT;Q\}H-FJ1;V!fs#r ~}mY24ãy/*%b╘:[  Est}iټ،!qhK"8}M uY!(hbʝa2mCxBuӀD<9W5%qRc#Rv} =1 hj^,UE3\&P4$BT !kV䚃&*i!µV3^[ϭ l i%R+UjVn-,um&AnYWGט 4V֊Xp:"IHZ+CH%6W!.mPn;%楖eKueE$3*On݃,nSpekG4΢YQvz[C$~~LLIXa#U26׀싎U#Nʻmh (u΂zMPI3E*űWG ̬eŭv-S*O~}*l\/K@/&*(5'ZRW8;*# ۶lݰ>xϭl"wY xm,qbL4dnĒ+,6YCh»Νݰ 'L9xcA`KdHah thKDo}XQiUf`0ٔ>0;׵*-.A;EiBq{'! k;vI5s3Utm(]X88>jM`b_.U6ghThDdU< rD,jTδQ*pU1eْc88_buFkvYvW[rGrN:ٹru3:c\VS"LjtonKOmm1cb[ðNVg+]pAv)C<9g}OƶF)?kQBRTb;A]JYPङo:OWj>4]92ak܍[L{Uh2ܧu%{iU+8gNw^ĐOU4rϘg ]yhW_i(NՒQŗ0e>5tdlM/$SX>K_'iHk&' ƙmei55l(>W%8Z\ P~r eۥȺErRƾ1/!<7 L䎹QRC6OuHk f^}1 #5ݦXXw*8ձi"(Qf;rUy۾pNoҮX9׮5!Ò *18k&;gGj% S'%"NFmMkcduSeΨv0?DჸM:WE 3q&nlrI/M_`A9U9I0gTz&J X9"Gh{pn* `kuw!_@VG%Q-ƮZ \ /';]uڀr[ |-o][׺"Tuu{9*;2~1cpBXfbl l5ϛuMFfKqB+w7suWJ D2}FZfÖn')‘ qz29aeE?ՄeQ s:L]w48Z>xP6s'k<kZA=4`75D%Gwz"ь,rxAٌKyX r)Seek~ArlC-(|qQq!Kg=yօ{/t&R-r~Cߣ:-k'\RՋ7769Âk3ҷt"wU L ۲iI 'aY4p1`d33it 35aǫSf{=mTcTCMj(|3yy .S8\v!<\!toyhok$UW[b ǀl[- k9Z U@,W-+E<"ztxA!:8-lU/ M7%`r}/Mp$QlQQZeb--Ubg5eF(N [ތ5c%ki^RQQԚw]09԰rd;3W6<Α0Lc0$gJ)H<^jf(u.ԈXsmYҡnȟvHD kJ{nmXe3 E/(Cwokat8irZp2d>0/ wy[l)>.TUc+׆Hh?Y0qhHH$r5rYIL]LuVTAm64$-Ir@K] +kј7&jbjMNITI\n7.^3Gx]tMolmefEPr\XUQH{؊HO| iE7RY aTwH޵E@E:@%&vs2)^y0L$FKn(֬j1iQNfʔHmbR7 ( P@Sq:]caǽMY@N[3g0ưKJZo0TBHYPOc1t]KG4ٶ^mn)\pdG>Z9wTDŽlӜN@.זƭŔkA$;Y?ٶN;(D KN&:NotV\k֋x G}M-9$k8&pPd3l &ȎunLٜ!֞(2s`YtSlZ; W4)sTe#28 `^܂ )7V7sʤx6䞤lcaDzGgRbAIg~V]e1]P:CH9VΟxdvȕm6!8#yƜ\zX^ #,sI: B1El( ;?)vr C!-GܕDvhb*V9I_E򱛎h (fKQ,KʆDa礳 .6Ch||%W5NQ"Y3}; }C@CLn5}ZMw$HW4JW d>m^tػ4~w}+QnaVj5 "k [%gO fd;ʳd 4M[GXg0ּ 24|<X"mkPnT8mWAeM)gw5'f[f=cS2f-5bg}pT.=2W7+5/4nfw 5 'gaG:CX6mACv~Δn!D<1Ŗ˱&znd3ws5UZ 'PL#y 'X2&S2eF'eK!xpFq1b-<&}c\^_B4YsC fD X̙_"j# )m ]2DH\2K- Z.v+//(2p^:Gn2oU^װ&]OzѹLox:=\ gn,npiՅk*aOdዤb n/0A_vzGյ=$}K+Zq'DT7DէKA \iR!֨?އ7-^yҏ݇=sQFMe}:}%Us,4R6MS7vB\Ej7 tl~1ոM5!F9#"3vv87!6"f@.@6Lwe;32N(n'v;J}mXh!KwAΈ'~Y1 E1l_nh<(!f%wtB+];AͶ Liv!.U<έ l歋F;Ij#YLZ'Yf*6iR<村w k 5M钚1%FJWk+j5'2@&54ZG! +NS;LJJTG&Dۙgv])с8pSYf$T1vy''Z]'Dn,F \:׍R&M{mÌZ Ε'4'Ba*5){|^QI9#$S3F 5%~шb<#lǞ"ceqxckxEn73@4q/J ~k#  Y/1 b I\) N1e 8j ? WOBFʝYd!+c-.@ں;̰0t\/q|SB47)_ynbr#:Aa}9P1j$򷄏BCׁQ.3ԇV(IMROqbufyŇݜr[9:-^;T+`zh+LVz6naAetuhM%4`v5=0EEʵxǾ2 isia"(>a %Pi?hz{FFdĩP1yO[c /F˴3"6(s{qCތz䲱VIь:ɯRñ0YG`0OX9|͔4oxתoKol@ Z|7mwCKzE\>MN)9Nbvo|"nj8AZsFVr5#:l9O|^f)0pȝ Xd9YNxnLh۰zmd\s8ogi3WKY~Y`63 -ڝ8ߙ19JXn؂Ҙp9>Ptem|=q|HI>(j7![</=k5ŞYƄ_oKG~>.l̥{.ߞ36 Y x-ቷSn5Ўڞ,Ɉ[EuDUT,&"MDE,Stb?r_ݚ юFSxp<#Nj\ /]yGxjgy'%)ڤDCn,Q]65#V7׭㮬]zjFO/>r "[cDWaܶ|FZ0mX@VԵF*nz}/Lx;2\ jB7~tlER*mbڝ#jjүXJT |[*+{4tm܊riMMKd3\)j>3@vհ$誱Xy1`bKUǞ+Ҋzcb ~F|(qځfSƒjTMOh-%wiyQuXx^,r]uG̭ƞ^9v5V&ޑ]1$]%RxhNvK`N\Uofӡ"Jde0w N'ge攠e X̚eܲ lHFM`F-CƅN pLJg֝ӽe߶ĕ-~mZ(F n)Y ˶Pv^޲Dl\ih9-Chby\_F/lTЫc`f`aZ[%C&Mvp#UW1|t]K^:X3Ûې|d)Jeь\x :+JGF\Vj츜n<$wNqZof -0L5qyŪ{MA+`!$TNf&rBP1 WoC0bpzХqO`s"m-D2 71IݲgB"ʀv S'F t\µy904 2TȠLys vXR >h`w9jHW t?1Vg m9馚èlR`+ኖd̦;PkGLwmʽ_puj'\ $ieP:b@Pt7썥ARF aU~# Rb!۠4PՄ9B#/Ŋbci&$PUz8Y0BhE(Y|ZKZf`~𡀸0IJ n[5xO X%Lagh%lhaRI4s:`kPLg;,eۀ0vdM `+bE+ꜻ_]{#C[xBTH4"b|)?xR`C I^2$W^QU tZU X4C Pdӎ1b) b@ BS|hIuRM@vڂ0;;%lc; seV3ygr=6hiRݙ`gိ&q!{<_k:wX2(C.GQnOUdl4:I$XeY z˧~\MHkqʫ#8FU^5Jy] "ܡĻ\NX1)w3ɸ[o {vܣ\cᳩ B&:@Ў*A&XjfԳ s{Y;g?sEg0BNxJ/uW$6+^Ha P`%Nouk .:muoX8{bU;?GdW,#M*ab[:e!gƊD TvkY j6Q$iŽ,QG>1P_H.@:aEڃMs"HQɎ%n npgᄊ]<4];j5`z1{ }f +#ni&#b]f d?+s9Yn"SC"^}M6@'=*xqq:C\, yA!WGlu/V~>PwG։c1rǏ,h b,ˡP0 'ɾѴj @Nu6縟5O т,_q9A`t! Fq5Ճqvڎ fݙ18Oµ,-A;\;D9bP&2suMNhD룹($0F8 u.`2iˌe unBtT\<8Lă/"kK]$]a<_\6A0tO]:{D} ^*3v)D;4!:\p ~i_k vq)K!3+ &+iک.9[(ңHlvj%xk(G;ve:-jiV@qrFћ}jP Ьyt̕oE5˴UX||;!,oO]aͻvK{H0w߲ ]݁|&>!1saxyi/|L75}|+ؿe'[, 2%ͧD%SeQ& d_~n _) x-C0 UDD"*CPwb9g?07`#*tp0h{y](C#br{.T\a.y]1S0x7mo#hQ|be3te1.,:wĜcnFA@gɋjZu%Lኙ(>.. в>aΌ\v?>W [%r'je 5}l,!Z%Y1sY7!.6W0"뉴8^yt Mg&&jv-7*rថWH3]{:/u93zH+C覙\>hi@mTM2v*X:²ػ"9o>JJEBn:ʽcZ:؞#=ul||Iu}x˚Zxt py< ѫ.nͲBv3(a6<] Wk6z]e5V]`&ntte lJ|P',zuuD|H ~Y}YI} ma ] 7&a$t프F86nŻ`xs%WzP{}E57}}ֽ`QA 2ͻ&"}P [us"M"T+2C"12I} 2'@-r@ un:h AI[l L[O#b%07G`PY.SluL yKY( h{:]m'3`BNtHO=\d7>b\m( K S@޳A5TXrIY+MgEDIk7u,C IR_lw* >fuxq!5r;wF "%"ukmY $ʥzJ}SoPeDXkݰ)jn}zEF]m ěEKsWpK7' D-(SP.Y"Wǜ8-1k n>Zk,ZyUab&0ҋ>Jπ܍ |t45p<9(|YFvwJ0o,JXfvk|%EW8 \:ԺpT$m9Og# cU;MtȬB OnL !/@ 7#טr΁XF|g ! bfn3jAN)W%0B⚬#1uN1AjQ%x]ft``t8 On%/#jtuW¡%K\`>hF+Ƭ# G-"v29 sNʶD֪(mL4* %UVGIw$s^l![ږD٤O9 ǎtrWK;n iΫEggV}ڰ~Lj;gMchˎ9[D㈦ђ~Cwc=ząSnNmmqQ/ M̹A&':.a-|m(ha֪~9a$~oS>2u OHڎWỶ+|l& b8+$P],اŔF`S8FfUjnd>9a7A(m+I%t *1e }^ܠV-|2J|0q4 ʓ<meZAhgAET#g0jȒAQgz뺽5\sMnߍ!+RizK#X\ͅ,iFPb놗b<^־!)6:yiƣ[v.v5{lx' u/K:O=4C3Ս@b0ln :!z7Ħz5DZFImW׃ֹG\aonel P3S+fumXwա]G{B9uXY xNmyal}Hs ]EJ5(o]; AA\&> v"awBjmQP;nQ]KEVeۜ`#pM-)]n֢+ Rċ,ֱv~![`ZcCاC{he\|PHXYBnNK8N\Y\^ >qwzRRhd t3iR`-`m\AgoT%f+G`9jC%Km-o,h-=m\4 j4=#g:u<:Pq5+n9&q]K\Z̤>Ic{9H6zjxsL].sUwn-+ǽ }v(]75 ,Yl 㔗Ցذc̲3Fs^Vr[P(& QG$:^焄[o{3(_g g MKg ixC7G(@l\ocGj`P? Q-eCmhhnBuA_h%i26 ?02ގFQ>7z2#¶+DijN63Jd*ߛh8(eN[3lD]%vQ۴g<{wg.'. !U)+0n)(g80aWnӈ.c/#A1 ȇ#eٞ1h*m2169dn;,(WS#`f FK9RRWJ*`ӅEiq M|L8/WLa<5kxv~7H-ۺN& Ɩ%v|b P-y0V#5凉-!㕵吮u **-Cdph ari<`tN-=}p A(`\wUt77bZc"}Ko;@^3:H,vS,Ι%.\G}:ARS]?dqjQ|,@(&xt]PqHLx%MA[M:ϊajI{;?9t$ޔDi;D%2n 3֚>JžZݽRU22ҮxZ6q.[-ВfFCfWkB΋T ~Bn/)wG&؛8GFWmZ"lX*ʬN }ly#t- 3Բn\2o -EØk؝h%F7{^*&]NIQ&?;soQ_;B4:7l뇮ed>Z+ҷG'@C`ҶqB?36@A(!V &3- `(X'r;3/C<^vNͶQp!ĭuix*D |r&і$sC-MfBiH q]jKM)'$m"~R/Cg طutA5 %'>rn14rIM5q.Hq5(`@8b Kuk%+ pk5+F'vx0 Lm!xqخ=B+PZ8f56(*0ĔW-2/A >mG,'\PLD1!`7$ EiR. bAGR;()gw#L^71ɜx`}"nKk*9ykw0[/,лfnE55d)A"MxV1[jzZ[K228w[ܭZZ\19Kb3~vg(LC鑗 )y2^Sܷ=E\, " 1ن9Px副Rs A`7Nak8h6C G A%ks7y'MOg;Sp˼le6`4*Epk-@Tv.^:On3Xh>0GG = y2SxCN۰b[V >&Obyɕ+}y[qf '΁LmB4oa;:t6;󮬑4"Zn$'̛$x&˟9m@)OՂKz2 xGR lalgF'+.S:q{VtΎHu?f,1J~&2P#g6Wݺɏ|FTւ%I첬{|ۤ5^[CO˷&k梤Y}ڛR?FCXѨpءADmsYW8O~l[kWp QCj+o ^mM1 /xE{ JfD.0352|=]/sj0&^C~0vaF琏uYcr| ]WyWR6=)>EÛg{7`1HFT lvY]gm6@ig6,sK8:'yf(N^\[6szzzDfEY|mR#YжuL%cxOi!sŀu$p s t$e}[xl4]C7ywd +xqse~|)e*oFn&V,oe@O~ЯKŌA:`{PUl< SvJ^wJwZYuޜ69(H+J%+6;!)Z0514~@Y C|$xS_3T^J d^pn vMB Dyodٲ)LQ"r' Oc": 8v75sR cf V!c.Q`<6Z˞SrKXnRq9%pX!x\t~"TE~uz]7̜E%q6{J@O#WYM. HQY`O̚di`z`$2 NN VkȿxyɈ ea9@@Y0$MW;bE9ϿI7;?c]pɠ q]Dۈc-wCyfy(yeKx <;Ä}+h-/7zϮ+M5zI\ Ab7Ni]q^R@z~j?çkf ^ 7bC"HLd:GZ oKK潆jxTkΣlEWu s&v !%GY |mˢx 6Gy60g^f0Ag 8Cҗqg!E|yf˯|Т5It&UIರV F,s-kRo?'7J>Y G<3[`xE55\1y*~]"J$Z!pZ1 7=j[!D~JxI8ڝ7otd @^a XYFU$j" {w8SHgˮ7-\geމw}4T^(ZUK@F& T6/f/;L(J wlsGZIՄWL-f&yUzA9FK['IFq`y"6qI׻psZkGΠ,DBǸw'n..A7vz,"b;1auK GL8&ӭaA^Z”s_mZ¥iIlc uץ6یj8Vd]#00+EXp[%C;#yl$;+\Cjo&b@ PõSɦw,WѮٰw3}= Sim [bRz bvuN.x; \7:[4=q,ցMG Aͩ(Qx#^9 K1vSz)*ϥᅾnm|oبLl/c@8AJ%:tĬ+y%S\G󎭙πPvSk1=J]>F P0>v"E*G1VϧJ}UzrGXxX][Om(aOGhtpcC1@d\*>c*m X%?6+<#*9N1"ڥ p\ݧ"mf AنsTEPΛ~>A 6}fEV)[CU|BM",f//@9nzIyQ] ZfVI YղB#}B!M-=$2﮿@"z]<) Y/˳Y[CUW}h**bCk% (WQޖȶ)``7`8KAWu(Dt<.Bvt75x(V<آnSy\"}~GPh'(UuJAšyYNj^X4|bSgDkBMճE$* lRŃȰmRCk;ٹl#و[dE> O 2$5{txxeG_,˫؇MA&Ď=G"[?0OJB\39sȮhlb=]Rf#Az!,pQSպ@ZOGPC#u$lL1\Thmr5ʕ0&Wiv`^0Y=WVոgvW_='D{>H{ t}P^Z_?֣3]*UԫE8-F0f?m?}DRb/{#uD$=ӉidKU>j}Y߅Շ`g! K=]ISpM#x2x/g<9SD 3GSfˠ+G M "d{} ~  Ќ eqXe\<.]mTFRߨZ4pKm4i4,1  |p!ǎ4Ј&v:}}u 9l^v]GRZ9"x L&b܃*,\̩bQa &fƐV@>t õoWf2 /ً{*qc}.LXEzuB[6Sw9IIޏ.!w8790 p^(P9c7>r/`ô3aK%X o$ώ"~7Cņ)؞w[y/< vZ540 )LҒ s5bG:X ,)3!h{}/A[JU5BM%S:cwyn>bKUiw`pUj(5}(_|H'0(܂1x/@3s%dXIyKNb֖crwXgW/}ֲ*zjPa`ZabQ>"(e.qP4IA& {<}}^o/]f%XI8LN‡4<93,3˼ ڷ9zNAl?,pM(F'y&Tvj!BjrH&'< Jaa:p{0_5tc%3MǏ\v8_~P1pnN\Xo~Lg2Lx)Q$E-8j^D3Ѯ%YXI=ց};qq$Vg^-vÅP8q{vd)8Mh6bdAJJ{ Ih  |z6~l/D]mbXa&<~Iu.AJjX j7wz?΅ǔlY&Bkn>Io 1#7[PDT^TƂ`g(azLM{&R;I/|7Ƅ~wnT1WuʱrnlYQ29Bt N!*L~QoDL{ 'о ɝI7Ec7Rx7y^RxokY(Òqlᩁ@)9:ۋb T~h68z&='0"1nr%r5rMryYn[aj-6s*u,}w.Qr vGdkZN2%"4DžkoLUD- }oOsN1e1%22ƙ5+u F5m41TpG]ݎG d\5R2񲂣Xё4x)%?@=s@"R(R2S:S`TݠP1eCz&Ȝ_\y]5J:+uFt>cdR> uĸW DnT*m*l,UdD~8S&Zȴ'tkqmSpUtXx[/YR온8i^HhS^ T9op} 5+i4bfn ;"dSfUSQve,K\#ږ7C5.l~l^oascwf~/y!2qUIP K~Q_$ Di1철ըrƃ+dO訄aKeFb`0ļAiK4+&vlo ۭZٻ~N3e{"3okCӷ` F[pi1!H\2'qVªIK \/2o`e4̴IYS$CFhƌ^>;OEz&8j_t}͕) Y{'=im}ܖp[Ȋik%lʛ/-o]`ތxqeŗL3kbhҜe~g}\RY~Z3A{#5kKdiQٞi@H/Ѓ ֻq;ҶeI϶,_Xڪtyi5MHW^gXxQB&Q/3z0=c)\TgI1k_b{="Y?[E(kZeF+̚2k?ΨB+ ZW6 ֛1db Ygsz,2[/z#7lpQ{TӾ]ϠK5._SԶxeQE5&[fzpImEW՛M~e`] 7ZU^른o=T7z̢M״P+0 >fkxo#"ݗ1qBx~烴Px:&8'6C Z=^SDE?r{) \DK!P`{t#Mpk/rz#FGaS3ՀO#UK~P[PoT|a/dcoa?4=/2GE>iH+ M'z(jOYod`%zM}[‰џLៀh!\쉬{oDMHR>)/=y{'k+nZE$֏|z8ORPv4}^h!?`HБ),G0i&/4J^ihhgggtI {R3c4Vf ɫVhTm19c lAo`' `L´& /Mj)8EvyJ3ue Ŋ̼%K컅 eLKN ՚2-sK_ZEY[?R[5J[NlsPb  Z#߀ @_`g/¤Jn甜(TrQXBz?jS?R㟄iI=ybBWUekIbB6UTqȸl{= , XQB*dElAtсձɍmQĕ z+RB3} Cb.RߑbAɡ (jlOCO = = D \221°z2ȬkhR -89j;628ݮ䩱# "H6Z@ ?A%(l`GH`wpSl5xA2fZwٖrUUWTZp^bjЕp XpD"0@Kd2$5~^@pWCatH71;rKv)T)~Tuʵ*DQ0RBʂIF E:zA* p*$Q@{}[ޝ;OO-G>cEp@+- y~Rtv{jp_jRcD[,XDŸQFC/4Ҡ {@7B A7<)1+Q8ê{]b3Z `kSdE\)`LyC#S$DcWS 8:`{wp_dyml\ѽ}l̖dz˭?(!a"X[ced瑗& A0@pr!)ӊ0';zew@{jyo~5v ۯٺ ѭ0r-Q җeF``G<\{wd<py).$|7:lWrjВ ΰ*qFd%#,|h!i#^`߀'A7pVu~qɯ}~zNρ_}3BI8(fܠ'Z bBx*TEVŃRb@7yJ}[H}ÁpxS+Wjފ:; ټ #Y8&O!%2F$&\5bRא$S z=/[HV`o W;>AګLDk"C"{ǝo,N3y57mU{cDbqZt3)H Z1-D]cz{C_HC: tm$j..rט.b'&BA֤Ka~[1y1tj;#j󎏤UMR<}ozN{=#xDOcw#v#t#se$qp1ȝp3Ɣq59_B;[>kTU2>Y٥hD$A\>Ǫއp} q!$I.IRyhht⅄3EKcRARY+l[2KW\ ڻ_"B]~jޏx}!p'2%qa&o/Ll|Qc❡椛b S6JWeG/e=Ŋ1zxё{+tT='78h ܊7^'m6O|p)"9½qve KR-ՑMKЍ/˝5ޘ6zw:KE`'H J^WO`lBY}D,V ,XhYiZk\./v{raud3gjXq)to3e90#lFa\qv vIY+2-fZ)ζP+ѸLպDټ ÉϑaCߝ1J{ǚPk|DFʽMո'U?vwUgZj&V'eSIȲTsm(24"<}L󂗨n-ۘ7y\5u0NlKܗ{N~ҴOSaקNA#2rd sڄʔ .RO.Q2/ʳ/wh`k#gF=ll׃i}^zHg/;@e (}~)hHeOPȨHi1IY Y:41sjcȴ̨Ҕ-^Qq> ۛ7XݾCODQׯt3x}?ct]_#H7j=fXDO:(\z1 @~N~J@C ?R#B1=Q/BGG('U{%"h4D7 O'(g=XoT_7O}O}8zp쑮z$+zTQP{r$TzSNt}[k%^F^V$4x$,^,DF K'hX<4fI, S=MX^ʽTVZ+׺뻪z0) WS努(TAz=@~$_`?R/On`DZ3SF&su-Uv55E6F̫-͙Y604sP\h(pO8!q{)SLa e?! % ^e),TwZZ[]^ܲ^ؤ^輠yq ;B%JT,S䅏C D%OcG= +l)URSSSt)[S/XWU\57)uehS^bSV[TLTU']c"| r ӌ >̄ -$[GFF"`w2lYk-j=Www .-MKlɹjIE(!PNig 0E$$ {ܽ'Xs^7Α6Ά6uec4pF[{C{́'8 ɛ3gDQ'%'+D1 4ؠ6taYIB콢s ]¬]Wvw+xޫх(õx q%dRGH'Kj(Z@ OԴҠd@OPqx0DDKJJo7\ E/blf+$S:sotZc6vhL!ɈDA JӀw3 ߛ>69o'z~(afܜ[)L"E_O.@IajG${G}!zw_ف5v' yy_xons6-S@qu2yPU&Q,’$8FI'dw@ -?@p߇ at1{^/'̋괣Yr* 9;doA"8ARArrj boD{H- @c>\>\ߍmφFo3%;ycM4՛EJ1QwGEJlҖ"+4>Q}_(?!=G/GuFYM䛬2zx{Ɇ9r/a阓ڇ 6R3b5vũ>ʰ"o{J|wD/ uX9TE{EtҎkb p?:v\ _16+Ye_ e AT}_bc0jSSĽ=Qɽx{4RSܭKKp RgXd8Jn(ovs+fnv.\h~c{-_׋`1{A߾o#8}G9q9abcn\K:oGCty~Eew`g4uoAR뙺~gޏp= o_@DǣqWn3 8pB)⌴%咨똜O[%kbr]0jt3al7}{zS׏xn_@||k27-$nG 7&oMԓqNЋsPʄv<_t7jf]c1lv3cp> M?F\jޓrPA\m!s-CfXy#DIzYJyQ.ʸNJJw.e[H.tZ6h ܺZPey5Q^?Dl{"r0Ed@y Ҷ} Es$\bސnϮʖr}SHX/tݮ)o(w}Tװ5Gp0iKb)i{)s?8 o;I|!!Q{2֡9&EM 6%FV{ge+8wg*]OЍbڲ[3ePpG}XB6T~CӶp"]DmqO 5) )kSlTmUoVw+A9,׾:-h;e\Ӷ͞)z9q?fQ{]ަ78e'}>!{#$pt퓌= jT$허z&"i(jNS&z>GҡJ4g#z&; ;}~lB@  RչgMSiCIa(j/*ZBP`G]=U$-,!D> (!$;46G$F%N#VN3WY\~K‚1 gc玤ziStz"O E@/Sp}^TX\dhqpaxMˀݙ;S&w릲)T-RJBB20p@{'Y^'Z]B([\?(\[(^Z-%*2VV)Zaan L Y L 5a*\""<0AɾE2f2c.r/r0|IẌ́KvbBIZ?ٮwSoUҬTȬxϺE9a#:ʢXh,-Xx6hIP#Qb $v$&$KÏH$l᫒8.k+T7nIޖU&()&\)J,a5}Aͼ!FŎ9=/Y=9A54P(Oa!jz@h6TyzM0@kVx;{/,y*qϩ+# XuOOr`(RR)vV3[A#j}[6Bklʶ>K8e]3ּƵ^5,k۱0D3!y눍(%D6(xD-4t0g2'j=!cbb"ܡߢ]a^40xCzdՒ]jq:\0 )+HTB,ըbS cA#q|D~REAAEh" !>/dwq >RJ?o]=Br-텊-5xٹE$M2%"{D="‰֣sZ*bV"[e:cQ$*&,N,HRb %&[j+zӋ-# tڑnLDywz{o 3 `Z8OSTcYJK S TI2DNb_a_o#Yd'؂nd17^z pE=^$ۗW [XmMSlvIJk #((  ^[[5vFGb!xϊ~59~_c\qsG&|I^d՘(*7d0 z DOtSt/zz=*\D.! q>{/܅Xw*%BGdMQX.1QW}Խz*s{K/0ŖPEPv1ۈ4$R }3*k ]c̥V>y[[1+Chf*+Q'{'>Ӽ@ؤ{-F6I<|jޱ*-s1ƍl{MtS3LSe/UBRT_)@"ư.ƨBƠVƐrvF[RJ].NeZk:yluH_7bRw{X 쯸+ߑ"⽅"tW eP# eL/ gDS ΀|awC0. $G] dZ$Mn(-5nnLZ> w달zz/[(:lRRFTE` 5nʠY/N1JX=g^&f @_ʔZa}W_oT{OB9 h v6n҇Y7yGdjd.8E_Mk0L%UgF^d-ze}ռ-{% >w.pA׃{%]G;#zxGI}%y։ݍ.T]3)u%Nmw{\#ҽrDaƞ:qE^3WIwdh$/. 0Ge_(`6,"Ǡ{4y/5#}{w?"[("J{=t79HĝhDŽ]$ ubOV)_J"]1iu4_n 'uObJW(J*rDNTijCMa()k/5=]_4_9YLkYևmfCU=S?OGlz3 =@"@ 53)qp*0oo}[\w}\և]eTd}ZWtE ދӛ=,~`Qz`.`Ž\`dh`ھaضaְbԨcҠdʔASCUm}wUYf>"DaBHFo`*5 ״mk%-_W^,bw,cvV-et-grv]⼸C+#7ezsP>xD) "d+\osm?󦟌qPȶ(f'+E:F2W G5&lJsfC1 T!@A!*lA B_ SKC);Ic%j6>KuǵR}Yօma9=ɳB dBbM 0 \|q "gC; (ˣ8ǁA!mlpȜNlDm{rMua9Ye{H(KfM:0a +Fdr:}fg}9ttw{? v) 6{rvE]I J!+0>8a 6+hЉ/ t47k 6P$&h!gum{DS Xu]؍:{vD~$P-IUͥ)0x`Ȓ;1?0N tEON6Ej/_%h_a cH3z6ލXn<#pcM+,'1AЍ*8@dJE%:q2 21/sQi֔9\_9u?@KQY6FSkɎUK81zg(zI :0:BRn9:!dotϥu] B/\G<gd>hLu2I,DHd*{"ƾƨT~Xꥦpj)hhj#ۈ_Fbndj#TK5ަ"X%MF 4:~(iʺ1eɓ &!$HI;$:rԬs13~ZL¿e0< ,; ׉].][! PKͅ3?t8‚a*Jŗ%&˾cؗP Ԍ13ęzLHz8.3S֏j<űk8h..|ۊji2xɈ 昦HBq,:zf>gb͚ps&"<}צԛA@Bpݮ=|S5w/q)vPBBW疫)2PO=bobɰaL/5L5[rT\/rʽw}|A˷5SI* m Z'0.ZUg&>q;$-7KMs<3ig6. ||@ȼ:7ۄc]9tF2]^!ۢeOE5(N=S_!I7ؙc=13S\0!ŀA?!^qݖ,43vp tNR 'ܘ.1^W=bu؟xs*9y>pߥ&SKx9[&'yGzyr1,L;p_r^;dHe̱KWc,ފ*g2셢({o#9A&WrXȕ,Z>F_9Fk *F%b3Sx&l+LS`#eL-)9HJNld$v3qL2g}9XD};d~q:챰 ~hי;*))**,"-/3 6;b~k}3!G8+QA/pdeʾ {--*'uYGN$dӺqTFcc!6c. ;p&^.ʴKY 0~K싮yEoW\'WX1YT;YPc4vFo0VJq,6Rs ^uny}Bj̓ţWqKs6syPZ~}ߩP-*=/=779'9:<>] }=6Ȼ^ryUs-tlv1if}i(.HT(}x?Lt!}j#O"&T=2qʣCPoĮ%揼%h~ }SɵԽèx{/{oEy=λ8q!CK<RwAk><F^h(a.U b7'FGeK#/s@.a.a w=+| BB}.Fާ@AD[#o03>Bη#cm΃ᦇ&' wԹB"ҳm K$\ݢ"?C4( 'SjH DI,d'455678wu(=ۃ7u َ`M׫)r:-eH\Ntk"A*G(]MSd(!5C ^~.{ ~`?H䔣|LQWIŽҮo2OE4rXnUfg89z6 ^"/%iXNf\I9ad Q ,FB*̜y{( @e$&&&ݦbf&`]fԒY,>~;Éi)<?u\E.ۏ̹#1ƼJI'$O0JE=Qct,ݭ**PHf[r{i K-όZMxaU_=7CcEZabH`iz E 3MHcT0L+(,& ,,{p&zL;{KAh v' >ʂ8 ˪Q , d1 5L} ʽp Igd`׳0˂/PTDo!& vE\JyKQ$DienZd0Ǿ J_2샢&<gH܅cY[L11FI2P#NALMˌRREf*MUco%/E'TT+(uH1gdY\n|'%zb\CqcpdH0mYfĪ{$E؟k &NJdX$g(^$?rϔylMu|".+ChMd-sjXf*Ǣ:왦T}}_ Kp`g%Ťb;0|G޼Ctc(9}?W";%(3E+0.p\eȲ,+þJk8-**\NI.gDo`؅#2o]cQ A( ɓ&A>VݹyɘiYW}U`/dדpLIZMEAJ9bМK2!Q: Rn3sUke NWЕ'd:n^n'7/Z} Hz"\PaCoShZŠy [[_?UI8 &RS]Y}阅qQ9ؕ!7wa^,лr#m\_0i`i'~(k'O0Q5/5YwIB. ȿoF;CqNaFLp#n| nSƇ_/ڢ$,(90(-3(!3&4 578 tȻ_RΣluS\=)92(+3&!3$4"[}t~a㏍gny{KTy ̚/1ʫq{N~Ն f 6#3f7 M̙144n`c=VW?LIXgx" $p 2긊5\el26Z!nEW̠V,gxS,iwxY趴ͽu{KbF&ZuՋQ7 C`@@Hz'3A M&jஓ [FIQ]ҍma-Q_zI$B g&+4⢀@TLV YsD<k$.=2w"+KHoNZOˊxN͈^NO$d--4XdO0F@o}p|iqvx^G|w!}OTvD7$_~8c2O;1f+M$Q-,@`pcCW<3$sotkvxeAq i {ڗ͑Swtw>m!dW5W / 1U:\VE[x.x-2!Fo[~CuDp. 0")693MPdcdg23}yU\or-Ϝ_ʱ ̸u>x~8bxcRc iRNt²*byP L+NBKlg2"nMj3Kfy2bݨm?!7 (J9rBC-I, Odl"Dd$%%=L}$Յ$s>eѦv-śL%81ۂ~8!hn-3"k 2-#MȞn\CBLe-#bVU#6[M9V!Tgv5"*.hPvخ!Gd[֎)kO/-qBAK-1HM24 5\|_q 5czOf1 *\ll7֌+pQ+Ġ5a^LTbD8hi0m$ 53ilw2"HLTbRAWp}ӁV#S˜4_N=Sb54N2z|g|kz~n@=3_ nXm|\RjJ9a^h,邹bRŎ 3 [0ƾ ޣ^`q(75d}?cEo"%cpI}B9ͳIsJVK3 U嗶)3aQ}UaBJMq ǡλA߫a듑7蹶#(BZ3UFi)4?[WW^ܨ͈M)VRT`gc84~AzqDn=GJKƮd"5%fJ(ݮI+,.oVeƨ{)^ qXޛHP$@DT'k;z(U6-ɀ+c>G!ymIdO_UZ\ɐU!WTR_g avDmKew)W9C1fY=`<{. #G% n _-N{\1hon=CqJhY1,t^b̶~)3 -$[KȭN+,R=7hymnFïjWļ&E[6l0Y`/i)+&)VqD1 *mpsXțc{ȱE"ui둻 \~kER皸ςԭj2 P!(:`isht~u}v|;;X^#J]}T?;!\8l yKeCưJDK:|q/kPD4 cz8Q5gX 4_x8Ĝ269G吡%BtB蒃SكYݝvgURy^},:[pyOE \hqKnc BM`,TćP *B9 >Obj3l-fz{yVSbcZMHG#gc]%d|$΄`+,*+ܕεe8Eri,g5ӖbζdHɨlO yAY!'%ǖP%X&dl 왊{!}~OBO. dO[)+Ε˶X3^M1whTOIUr(2#UV*읒!&>DaȕQʈ?~c!/Ғf${2Գr9J[ `4W!QuZRͩ' /dGi"{(&~iq°lkڐ3zk3sdRܦlz. ո#C$HK<'Y:ɠ컖(aQձ^H7e.ȓmDEJ?:-iAvxÌ0ZR}RǤFi2Wc/eq7y!;eE6 8kih\쳻A# HkǓ *(Y.9AzR쥢 !'>Pagq+ ,<u6ΉbGl%E ;:m9f4!_Ni^FQ~ir<OMCO}~]O㐞Qܻ.1N>Bbj+7)bA_{aB9ҊtJҋT0)6{ WX(2/Vbr%W4^5k uc"Dt򽢬26h&lOQ @d?~EHfEZ#ЭIK ʑb0ÞJz C :wޏBuwz"8REpE8䖑Ƣ1aܣdW$^qJq Ub*Þ*GbObayqyCw)"bhu ! 쫶/e8r3e z9a S;Y+T-8p9ʲB)ܟ /A1d]}W_/׃(a&jZۄ-m6ib}OR(q[(sY9)v6*ٕ>%4A`  | qo9W\;%RSWࡣG:%Vލɖ;KRC&9'աgEŅKz` |oϏysH\ӺvG~qO}hwA"HOVSp)*ίxEg!v 3v]0Ԅv4}h9* {K 8W*DXqaz&{^B}Dc7ϙdͲs'*܏VDye6r(%w7W^tX"dF>Z]%ŞIzM^qd0[9ǘLBQN(Fcp1ӤŜ`=;t S54+@q6$c 9<@K=ϓfJqgsBbNϓnBBEL(Br왚 {} wcq<xqd0G]'X)*}MG]5*(О@8N2fGbT!9ދaű 4#ٱ- )QS^bB7l-"nX+UY=r)S d398B18qVD(3i1ʐ~ ӑqͅfW(!LK3hQrd F1 ]~h{:ʊqF2'?yOlKQó޵; ǒs'.h䗵'F4]Q=a3t Bޅd h52GyoГdbRJB;Uɰeȼ '_*'H/lPg 'g'G."DQ}edɽe4!4[!bDBZ^%ny24%(dLhМHdO _P5_S0x3AvxŚuUX+k08q yrG|]y ǜ|S" btq'Xð}n̅u-̗VA1zHQ{A lѥ$T!<\dTz97oƔPg#0gLUӽZu9̋eFy}F8wPr2s2 gď$PFW"9)VN=ΈDx"яFّ0\LIybpJ=$FGM(,5=~IBLct )@Άk{rGy1.C 詝ܼČ<[N!oKLTO^Ҥi {~ lף|QV,qf\!|l̼tRHQRS]Ԫ{%l `8Rs[Bs:R.RF$c .G#))b5B4UP=bׇDx(]Hi۸Ҏ1$"{:reM'~I}BҀ<Ѓ7І2wDOh"ɷ D2{[FWԫC.kXh {"^OzI?'~^OzI?'~^'a5 IF@V@b@9[;Sh=10&3wm))_%~Ra *쁊OzI?'~^OzI?'~^Oz($ =Xym)}l!NLMČ&˜|@$Cܱh|I:䲆5**B?'~^OzI?'~^OzI?{v_4`$` ! 8363c> qޖU' ^OzI?'~^OzI?'~I}BҀ<Ѓ7І2wDOh"ɷ D2{[FWԫC.kXh {"^OzI?'~^OzI?'~^'a5 IF@V@b@9[;Sh=10&3wm))_%~Ra *쁊OzI?'~^OzI?'~^Oz($ =Xym)}l!NLMČ&˜|@$Cܱh|I:䲆5**B?'~^OzI?'~^OzI?{v_4`$` ! 8363c> qޖU' ^OzI?'~^OzI?'~I}BҀ<Ѓ7І2wDOh"ɷ D2{[FWԫC.kXh {"ľO 3g̘)C̘1bĄ /^te-Y`rŊ*TL"3J(O8i҄ɒ%J$Ar䈑"E "$>|CG8nبAc0^lSE LJJGGFĻ! _.3Y nin.m-lL ̫kK 몊j* ɩiI EґQѐЉ @@?>=<;;:9877%ljjH!"D>xЁÆ 2d *P0A zء#G7lԠ1CF /\x`ѩ"f&e%d#bݐPЏNl,{X4W7ֶvV6&յuUE5Դ" ȨhHD 655$F"@y %J 9b!B:ra2bx³*Rpnjfb^ZVRNJFB>:62.*&" =\mZFeẊ_ofWk:Fxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJ(N)aSCb >xa .X@a8h`(@`~4R)@ IH@r$a!@A!e9uDk/{[k6֊N̞ ,КagNĞX XϘNXT՟VV=suB5*Xpͮ"Ke&R4b\B%>0(F͔)t(E!ˁ<<4<9QƘ QㅧC7eBm4 `XO:G>||\V}ē<}rMI0AqՠHx*pGs08s Ai&!0L ӵ´bj-FV_Cd#[~wB.$ӻ& aZm%z=jj![ цbWM􍽗ڌ0PFH۞:pi(:u"TAb屬h Qֈ VbDuDۑXl4(`\ &Rni*p@E2 (aPB(#(AQD!j/Fg_2cF.fH%pJ%mGWdYo; ʄ3*È4Q= ?;ŕ ߻>ӐD{E=4 G2G9B}^<%hZHĊ { H@%8\>l/r-P{lR(Gk: zEHv(H7@%V?닦d{L6o*]W? >lW60/҆H{> 7C63 n}cUqHL a:CÃIL#`"S 4&NtiN.hz6ݭ.FU]veݧʺکks.H\ # mȃGgh@c&G'9[GwngݭMqt#vnIVj"2]ժA]^QQY]եCll7a [2BwEn"t;ֆ[Bw[Bn]6݂ -Jݭ!]mՆ1]bM]fI!mWqu}K3ѽsyֹ>k/?L@#bT&!s}&gﷸz}.qf3 ;g >7Q @ D߄`$Ock))x|3 <|xng|E( vݦe݅AVtAG, d;85#h=cJ8_`})Fh`HCZ" FH#GUu@ivlA[i>Sj/J@j&3S ǀot S\KS!'d8-b\1O4 VvL2Q[d'PmOnJ qօc|+0a3;:^̮T 3y|#w> ţs`\)X:$LѠVNVrXae GJMQ+a5XV)ib-6[Ũ}E)rM&3|$O$}hx!췐}7v%&tP #a H(@BĬ $j:Ip$(&LBGn;ODo$}S!I=GA3\OAG%JY 9ΐ:qv#*hGAfӌd̪e:} Ge;Gy:>p|A5qş|0m~ oaSjG@`{.ڜ YxOƻR&aK%1GANMe917Eݔ uQ(P=kOSB,)2>ܵysa-sz9ZpxOX/bOwuWΰ]EЊˈk ׽}S"@ :m-\d9b1QO`J=8r',ԠB( '1Gx8^5ȓ.y X^Ӏ O+R- Bmu ᷅$ğVYVT!N@Q.:.aF"ER)^nԡ{Y݆b#Xu^_ŷxQCg\@v(ܺD% `QB[.DF*Zzh tIdiQc C(fKTz+='>ŧxS Ao|$[7"͠5BD/ 2$~ѐ$׬E.JlR YR*CL KH=h $ГC\3U)M'1e!f|'%<,9;Vu;H|L㗓/]2.HnPiEASjç' .5|&Er`*4gOTgêM @48E|'D=ປ]wMʯ 6'؜Idk&V Y,XZuSCSP%I FBL8ԁY -](Dm\:DV `~rG4ZO*o_ݺc3|-늣/#r8nx"jid(PIT KHE%5SUőAsv6sv%!\Xrͅ(o9&4|պ[e `1ԫP/-]H=pP$ՂXubʕ1*D#k#,SHI')UZÔCy&-g, Bm*\F_yH>ф7K":sXa.\K Q|f8dRqZ:AELU$e:E>R8yC箒G fW摡4 ye.\y;y7Nnw.j%\aUkV0"fquCUAT%QH Q(jb1RF7u:ًb7yd+V* MzYg7L5!l3xѻ#.`"ĕ~&Cq5OU:dbJ PSj &RJ9.(,!y1b9'[*_Ar>8:cxTwE z-4{AeF/ՕSIjHȳUCǓ{<\z(H r@#AJ| JC]S"hE3xٻݨkGCՈv7:@.u=pR)GvDTICz(<'BiQsņOmPmXWJ0aރ?w{t|8ݸN7.HqC$34.%JG$sd#ʑ8)F(U!R". h v_x,0`wM]Z ]<%HKw{Nv,]6%Kmec$ۓ ؤf("&kYm!el`NzmB[oh;`B\9s EPgg ,;)>TwtϬݳ+F,K g[68/fe tČ > qO|X)SWxL U@sK n6|Nmz]-ݝinv?Z Ct ݰ1B7LP aP@쀞#ր|A$LaE<).!:cl Bٙ3j .Mݺ]b]-T*]>ݠ˕Rr-@r1S<pA׊kU WtpV`NtXsĊ V%2A 9/nN kuqV յрum2n֥4v_wv_bWkvSaWKvV\WΕ@JqcT9D3A9D%H9In3uqnD]QG6ޘ7Q&Ե5nMdե)4yYw۶ub\WuW^W{uZYW6s\37'4AS 84].C0a8E.{h7.MktmPnhӭtk(. bԥٛ۷%Z/ںZ-庺=߸}޷vi.7{ݜ.Sgs tG\7-~?=ſrN4|S,Fhk8ZқsYlx}?]ٮewh[4=Z&t6OݨwN 7S={%X/R#& h @+$m1CRB(V9*I:>h: sI|+!oCV h6`f hXD3&<ѐj[JsUF `i"К. z ʁǁoP[ {{-Ox~o6tN*Gp20 X7Rq@͋l^ !50¶m (`#`mV*OsM$grዌֿax?. xQ@\(`Fa\|Jp!AbM-n5[hmi׶E*-|e' 5U+|"Cq1~d*!SKʒ@`RvA3-N̐89;U'lCNނ<`>|b$/G ߍ'['ڮ'C=Q\I6M"{>؏YD>5&u}E%6.AؠaIE rJ!erg9DohrH#@B";~D q(ִq܎^H=D*| #x}+^ƟKpYwV4(wFVrW:lYQG%^T 88s&Y4zq8CBF<8q5gZ;~4wxxŷ(x`.'@,XXX?^$PIbH`QJģ$ɥFG+4Xt~渵PCH/Zi1Yx4Dw=R×E$#U T˞ PZVbKuC#V* "*,a1GTHBPX"P;q'HmRnTΤ?)R OBǿyU&g f&|@<ӀC(g@HfX;QҋtrU'f*'T"AP!WҞ9SV/$*b /~G4ʇn <e:ڈA4:0MЈ&Ye*B'\E-BfruP!˓JgdMHC~?u9l4`"Rh^]<$4<ȣ2y*[P#Sk 9B~(FxpF#)GhOK̀8xDc  E:Ji2%&If rq &lC8D9hu4Ci,$p4we<%e$,]jEֲ!d8_\,3X˵Ė ,QH٪ .,LpxP!9qАax0xp'hVp]gx.(b̄W2 a0%bHb̈ 1(dR(BKU1*FQs8eBPQX9&M'5gh:l?gS{,I΃x`D%DvSq0"AaZ[)|C0W\-kh ZԐ(b( @,,Il[gwij)jڮv 5tHb 1XE M@TDf4 2Q1@d юj0bld% 3H|0>gw30vi+nܮvjҮ֊]/ E Й .@hx T%^'B=n$~] &+ v{8] c]KӶvbkwvViWkevYa7@jyt@ A+ Hj n$ɺ@@.K֥Bغ>[r] ^ݚKVvdfwevZeW5v\^W(k%{ENեե եRbu)$XݟYsİ>,n˳zuw>`ݜϬcuonMץ4faw 6v\bWv_\w]mq׎kKZ W5ċ;Z-vCcݢ.NwЍRsn_+0>et#Sn=[glUm5oi oin2iӮc;յ:bt^\iΔjc_G}$ÄרFkT G5:.R͊jXOX5'$LmIp!K&–LBPg>!ض{?L#Or@H<d ӁR9p e$PР GV8`݈fes-J j1) fZ7 дfEAT4&怼ek|)>9\ @&$E~8P링~4C X 'ut:8˱T Q5BjxK@n˖V̌VfFB@o!C8ǢQ4;dgAD~C:Ȃ0eGcƐ1SvC4"bԆ@a ƍf80s{Nm8_˟;!:mC00#1*Q6(qd" CO3Z/ͺ#u5ޅOELR0l %80ǖ6*'ڂ\B0 ³d T,qXb2 Sֲ*҅&Υdt0GT'* Z&ix<~E'Uhߙe-|;VB\ ᬸ q5|!ME5g!`#:Vy,Qką U" kBM ک,!"țP"5 .y`(SE"q8NXBNZj}rzUpŠUqD(" R%@Dx(\{Tcbk%"Y߬ő#]c>CyᮇȸRsxx9&Z|t%\K-p!D'O0NT\WRLEz#bJ1;bH)zs4$L#[T.?:{b0 养qRZΝ h%]P9q/:ld[D#PhѮRXSMG|'(̽AAUbK%CB5Qa6DZpB w?7Bܤsf t=>9bd|CuB:zZڵ9E $TAu1tD#>6\!%fx/xNϸ97B :/ԉP r3:7Сs4hb4zYЪeqI/Pm찭|B.B+qV|c`8 w+lH0w:2h Wcs&i !R9Q>tqtU$P.=+v &LVjAXA22ۄ:w1GJrKtR,? 2[KTifc,ybks始( 3;  )-*N9 8py(?R!L !S9+rF$UtH6MD% c*4hʲ#KzMrcFE{U1|0sOd ;۶v!Y#+\q 8eq|K8m֑DیGk+ S\a*b %/P\725uHIZxAH`$?uܤZjy2` yɼ<ރ vc" K=x⭋b 7&*ڀ(X"#33\k>ܱaǕJ RD7 v>PmmW2?uP`$L9A@ƫj+." {(a _1pi:o8Q؈a?@0 XT$&H(GV(Y 6,Z :Dw;P]K8#H5Fv`yYqDЇ d6 ,v׫a.C&YXmX"eJ%d#EJ/@dr61-Mw$; ^Qߝ5=3m7l?#d[5S"&ďI?'_3xU"+S,T\0 ~a\sHad/X*\b##p',q&p4؀>|Az`s&YMVA^"^-6])_n׵ux];,\ghYpE;1YYjbWWwXո;)y D1@hJ&8 `p@CtKY[4bwxQWxLzW 5wnܗfʉѺr` @sP)0U99@uDsL嘓@JQ,**QgSLw(qC~#P0iRjs޴S<|Mn͐d !`G572^' ŻS[[eW[ ]ڭd3]?mށ7SHί2KZ? .ۤ:'*'F儸S8.潺Y6wfz]0hN(ۦ3aMU›VHhq4{3/|+dx56*3WA_@jB,Z^|3ƒ.|p lBc:0rpSȁڍl6wռmGAKq׆]xl6~詩‡ 䯬4Ƞ ^B08ZER,b4hd'\:iJj<ɧ`CtWޜ37\}r7j&FZhp0yA5qBa K~?@|aj6U+EACd?Ȍ14mIhy)ﮊ@TA"o @#,r eq8+w‘MEd'J 14*gpn9BT o0 'Vm\ɼ-] wE5h b|"dtӏƨ=<^I3ԝ !ږ1U֮GW+O$dNiSLV6ZL9GŖqblZ74x݇]}_ 82 HN@):pᓏ膼BRE=}Pڶ5 +2" (A1bqPx3F,Ԋ' 0&;%y|3C 4O@*4|->GHh [`*4 45sLCRY/Wk$7B %*SBdq5`S ppyng١~tvT3?#tp g+U"D5P`B J.AА2<աԷ#HNđOW%\T"=# g}@φ9޽Iw7I<׀&,~H2Mxh\KQjg7(6|0" th)DKDV蠉Ղшru` ppyBWb]LԽ{e~]*WTGr u(V7WbCg 3 543ؒЃJQTS iS(;kXh\GLB ;)^׼K)BN0 '0|O"Kq2P/K)5c!lJ#Jre eZͫڣ+kw[w%,Ƹ@qƝwXo%QQCqǜٸ?N4aH$4HI%a,pUb5Yና.FЅ6[3Z Wtj0j/ x;P]t RdΉ!EtqmE5NX:Nyesrե'M2GPl`mC6c:`vj*\-)^-%wA!qWHF$T`7*NܐBa1(e`WRf)qEL5ׇ0ZTU,I2*T#j:֪B]^*ޭSz7Mڐ.KwYtwvZ\wցe 6>X A5X;! ͪv]"l۵vd]nw*B]ѹtQ]!Ľws߭һ4\v+n.Z-M4Πujbvݫ'hڵ vv]*jwڍ2vZnUFtWhݜ9{wk.ݥrۯuF*ꚝ`uͮUe*k@ٵZm)UQnK5N͞ݩg7 څJvD]ReݠƹXwsݭ!4aqn.c؜?6å``^[TcxhnMeN4ojvej v[K[uOLtl\5᭄%H{"I:%R]pp8/N啺iT7fݭUvh=v]}:ݹ.?G|>oC\RB[В;R͸UP\Ջj`\ BdST@6dS`0(brT ΢`XE5 ؚD+x[PA\ftG-R'4H> z& YFl% 2 8lx1xpZ mmV ieQMHnZ8̼e0w%\U)P3xP9][4xhL"<3;nf$m`npds@b+d<<&bvI% 8$_t|#R'^W>-/W_$ >-aT Y'ƒ 9gc0hyĘ~-7|+m2'wŁM(up^%b#ʝ[(zl#F328A-J*WU& `.]$Òg-͗S-ցnCe%3^83/ʀ < })"|v. C-R6jQ-F H *Tv=*ѼB\DC 7(9cQDlHJkx#k}y{Zv'߽wG9[ϏG:H0KݡH灇#YI6Nh?6 \RJhS8$"x̔Zb%6I26iv4fƕ)<߀^Wp0$b 0` ȷ1teq6*e4q,YS>GtAJ iaU=D\67l4$u>ob $ E0xhC2'Pczj./b IT@ԓPOF,GB?uj@|1 Zhè`+)RzupVKUs}eXT_FsEx`8ɚq#a?(2yB K! ? hDCEDcp`4PTQVu@4Uvb챕"AT$Ik!f ƖbYHJp=y^-5a FK0`OR!g`8-T*/ÈLqUwZfd9[}LA3gM`fraH)"hއD6j !K a,.:* NBNXA$X?Qj`qlpQZLB5ت$)o[lVZD+^P=r'lzh&+h*%5qHfa/6QO 1<|֐Ӱ!gfx9"*L}z[엛lL8@۴j6 mڡ̫Eb'\p9KX@4bJF*Wq"p 5V+CGg!fH =8: QeOKP28f xJj@ uzKj.VI^TQh$ $[.0KdB%3D!y>z".؝j`-C O&AX)zBP;k܈^@v 6uڤ W{DyLt!xc ^G8|Og4/㐽Eb'ԭV.q Ts+`\5r4"R~N)9;ƫѻpJ6)n mxmEW( +XJ?$I|(.ٳE#;R^!JbYP,C&M d"1t$ %PeOK&U^qn9.q%6Q @YI:/Ȫt@J% q 2fPY['ʻuZ){D"ѻM<q A.vaM CDp܏2r8QhCF$*2DNIy#&M]0(Apm8/楁ۨûQn };/̜d$# n?3pXcx>V# .re*H-OHӼBt.Z{yk&/㥡"4Xz׏2w]NK=({퀽 ܶUdς6#|` Gi188[AYl c!|y0a C Vj*Rޡ;fyq&o㥱B4WFw xx ݻk Ufk q)w );&/4y##^ d'Ns^WknK`#n* Uj."ޢ#xsW4\yfQv,@q8nܙF8΄pLK5wː] rwlAwZ]vkfyػU.e]R$E^i. q`vB o$]7Fpe2 nn%vǖ]1"o7w^.sw˵bT.TUj En2[wk. 쾁@vJl']KF%`QvXc_خ+֤i`?W%rZrʴRqO{W ޟ;wonޭ4]rIqt^P-G97 .8ަ uxn.ݵX6mwzՌ]1[FsuӶPMtB775 _cI&(ܦ  HuBq(ި;VTX^V` V%Q8Alj dR&A0  %1a$I" uF$t7;h"(|Hc錅dѵaRJg~%l`Ag_a:8zҺflƅb6V ޲@hwA .3ȃ̀)1@%A 0@g 1 4i..] H!VY$?  uۙqy,pf;+sYlub 6@&W R56pUR_Eu \R`k3G 4Vx"5px9 OT~Ɂ0F(3 2uo2s;*|im`p%E2UP> 7'9bRPI oXKZP>FMNVr +yz2ᝰWj%pJۆ+lX0DEjRRJ '.⾔i Sb0xq͔kinhU"z"#J$#OPF@IXPxa&nM.$X[E(%tJiV iXLpeqebc,8!rM&^F%X8E f "3zHc1c_ yB04pQDk< fo kClPdsBM#(iM*rMq5L C fB@R C 3iPdLAQQDH7*Cqh}ī#Ül(dAVBYQdƎ픓"5q2+2N2礨`kQX4bʊE!+%mx\bVRf bhjwi[$3^=RLG$GO!4VI<:DubهĝBvHb#-FZ[9)K2DRlB,q2D10VEJs?| 9RU$FEwr왁^@VRvYفj% ^bfa<šyuE+gd Y_9(!Zء@ 'R6UQʖXs|Bp+;v 惉V+' 3Ѹ`w֫ZKAJBtZr`ʉ L.2v^0qAD*裫)k {HYPY3|48#M-ڥe6E06ĝHj@(Rv0,7S_>|f23HC'U""iꡇTW |"Raƺp'Ի] [$ wx#?،`Ĥ&(;DAJ.,(`҆ 5MXqcQ C2t@WC4J)_[aݝ@-AP$VIPfcrB3LN2jQ.!,~@jO#5V. K%yi o" >Ih H¢D$v)aNIbx$Q .7hӗ⑌včf*dt5nP'$с@kwGL`}^K#xiC+ rˠc lb8x "G#1=J4:P  6\^`VasN3zBoP[!po8o U奩Z4W52xAA\`~P32B pR.jh$f*E4!kJ bJy\cyo4oU䭹B4WwP w@_a`_zJ7Ah ~@'KxFo$2`eN^-G;UX2u^"VtU^[Uxiowл|wA{=A=ׄo"}$+ r0/\ JxL x"%/eӼEtG|E^.[%xi3w{ݼ F"<ݺ(yޝ wv]8}Mﲑil]P[*16V^#6yq4Y~=`w(y]3u$ԑPA u].;G1qD.\fCﶽػk'.Z%#`wRR16&AktyoY #5`|&U@w2w]Js9 sWNܕBw]8#umqwb]5z/ W+R9*ަkxo 5bz&e wTQןxr\-+ wjqHܯAefOn]cnK.ݻ74w(]u!k6vjn ׿x Cabanؓ+6䎁d_-֕zն<6- •uUOJA޿N@,3*XaD?#^3[V<ODb46 Π !  -ngęz0G&ꁝاy`H+N 4{$qW'O E<~Fܫ %rJc hWR|p!"jt`P%)wzH)zR0>%A 0ʨ DFI3ŇTN Z5]CV]]ƠXz'fY);9`z~30l4T& n8\Ih @CdTCp8`CPVDp|p 07tݱDE3QfhYA槍MF$I89STaE+&;pg M&=`" @2XKbQpv: K0hvy5\K7zF/Z#h]^KSx4!F XpD'P@2$b`E?0a_$wb=WJ%˦y /. mEm颼6\*Q PLb \A ;p QK(Dh u^@) vN"ҽa J`/uiaC0`)`Z`ZaZcC;˞<4[ô Վ3\[͐m1ö ݞ*{0n!¹{ }Y߅?c?L{ʹ7L2m2-A3- 3- T0m j[ (`{ow^t[ww_?~|>%i>_#}}DM{ִom B??5>6ml_Mn̾6ۏn;Fۻ;l/=FH/K;ǯ\˅9؟ss?-JmGpNEUemԎ j=d3q5[[|nq65 RJr˃4I|Wp%N y*iګ6%Amp[VIVGZEVڡ-QMq3%W&3r03̯4o*Pمy2<yx5gsfݐNjW2MvcSj,/eˌOܿ89>%Xb1{<802 UGrJ}n򅝬9)U>~on/-Xj3>[k#\ >r#J>2gw2,MLe[9+8ˢkY\5cf̄ wYWp|~Cs|}'Le2[YX^e{`VI q~6s 9KOp|_Yj6滘b>%Q&"0LcnGC0LN` g~fI8&};$V$K) f1Xa`9t~f $< +~^=;!ƒov__]LP45 P P '"oq$~ ˩(O! ?_& 'C x>a>xlH>oIYa0-/FKB| -ߪ5hyI-<ϲPU>My R'@y7'@~Vsp=w!)>V]/u y$H*|$ϙ_CeH~K"_T䁈|!|!8FzdaO#AxAw sFtCw#yU֨Wzӯqp!?Bsf>gP$BB" Lh&AH DХD{=ja|Hˡ&tZLK6t)>RӔ^-{mY_!5'?gvd>ti:(mFTi(J]z(өMo}SjFѨENj4ѪB :*4kZuz5= yI_)arvNi=0esJKÔ) TbZ0dAC [aCm.l+O.]tNm8~1'?R#+<$<}H^nmA> ɥ~^R3Eͱ5,;Nl5ͳ2L[ sm%L.ޱʺ{HwdFo-FmC F 2%21)%19-,f4bN8v}lM6鶙^p[II7d>xo1 _7?Q'~WCh"]<FF3.f4Fhj4mM4mOնٴmƷlqFc[ER'Qߐ +߰ ;!?xьpFC![D$l47hP mS紓tZED;ěPoý2B !DAAO+-WP j $1(Gbpun+:hVi.Oo;AG ,p'T$8~xՔgWs]?%v5:jXѸm=m1i/~C?

S<#$ٻ| }$Oy<)4WoƄ lӫiWc^=?Rm@m6RPWx`zy%ޖxY^]ySEG98w@_X>#_Wܻj2 ``;AXAR;;CmC]vNqº{r:(Ns!: y܆wz+~/%y,w9š k #2WXcΰ!M"ͩTnq)רs:A~9`zrYXZq86p!nHǛ}&օ%pXb7 o42iL9nSnZ϶I-f6rŶ2kܮom]6 #ug۵ߦp?f|]kbw푦 iEQ6I"r˚f͐LRU1ô y\8Lg :;l^Ŀ8/gݪkfI% f4W0ienoinWU؊ Ԗuj9yj;0h/29a1uoLt1^6VPbx',PӴX7Lέ #_[__#6hQ@^^ozyÎ^ F*=t.TWju*ؑN^|/hDMʓyWKڠaV-PP,Ql- ZX W^ Ϋ3VbuCF|gE7+YXJ&*M؆*g;kaZˀT{@hI*V0B+F u'RRF*Z)P#Db O%|:qTLb>9Xi_M6bqҏy<ԑjZX> r<`L:K$I21@J1Q;+hƐF4N4`v W2 g yIh?@R!KZi?)K|@%JRB,6b+A/6@2DlJƨrGǠ?<}Ǖ??~ ϐ|ld6&DYjD9_ ` ?d A#cׇ&X#+::FYgIfȦ7D1${$yfɔ6MnyajqaQ.]ʢSyV5\^˃9~^!8i yn6:u6?ww&09 d.CR2ulecyZ.1EԿ 0>f s܀#7s =i[0 u }}g{Ϲ{?Xe1wY_6gb]73 b3Kz{g+}s.=Ex- lf%+08,=O,#+ &=;=x3+>0[ߟ}¹|` `.S+<;Xa+`{0: p&|;wl߿O}G*~Ds o,μ?>0x$ >hs x<"U> 7){%t?${< T#~ow:^)T<_D=e>ߎa/W(C`=/?>| Y|0̛&Oy_˃u-呲;W_埲}(:/@~o}—|iN̓8O^ē+{lN>c5(&䇖ϒY4Jlz!Fy\=/~Eh|Xo}VU~|?*i A~0y /䳀|I@ 8?z~6u a0^S=!H4w0fh̅EjlQf7YӆΌ6vslf,?GՠpR jCChGe^Eכ0pߪ_6GQ{PA #g(+~ޱ+>]犡)WFϭ8Zmb*RR0-OoMT}l׋ {V?{?*7x/V5Zn@UD 5iK2ק_&|ZUj9S:LDZVK^uP;]:kbG5@> =M{ gHJi 1J;"D4B`MBk[/Dp!F 5X)XbCmRm>h܃\|N˷p-~ַ <2h\ Z4"lRAkTkkTk[tk[Cִ6C0-f;]ءfu8eiw]Łm&m mxGڽgs&gn~,}9)TjH 3&$jp\Aqa&Bٶ'VSMvgyoy­v!n rg]n#λ8_O(2;h6%\`yf@3h6+2jX3hD3ٶnL8-Psn>ͺ4v,3̽0V;2pK§L|Ǐ I)BݘvAdNl8M$nxHJٸJx^Nm'ﴆyڿɽ&;m$w,(H|gh|8sr94t!ˑAC0PpHx5$L*j#t<0Bɯ+o"\hps G G?A%/x N@@!93qLޤڄ\LT>2nZ='g| r-@͡-\w? }&Py&\œ u+ԹW30! ěU>EQ> |4|Hȭ"vRgo|Y)> mg/tK}T5^pA:17O)怐Ŝ"x97qT9޴AڐEhWij6aj 4xyy]X-s< [ၮ7:kPN| AܺGs Bs.'ǜ 9ʛ9Kҹs4s˹ [H:Nm" -]6Va:ѳ>Dbs\ߩx=w;Uވg#%NK$o7;2o\mľU9} }@s|Ӡm|5pp 7֋^q+o*~xiC'9ΉjMHbj2ҔfmB$3t[ޥQqJ4dc'H8cG;vq4 01? hȞEE,22eLH="( UL(VA`>!!z*%~BWc2@BBaE(yϑe yŏI,ɦ?Ne>QO%}أ.y'v#E?t!'"# tL&Snlɳ7Qa36U>aseX,h9͖Gm˝5aƬ`Ț>1`?i` κ`;tmV)ler-82ü1" t3^``A;s}~_Y|bF1gL`! N1?0Lw1lc=r;g}w^܃|C@ͬ {,7W|b qs;/aMx^ߥ#}'ދvH?.',<\b`*cp38W %>!E7%~]{wIߓpK< $|~ cCExK0;c Uߤa@|O3k#!h_G''a~g*\%":.PP?( O)FsG-I 9y x~~2wy-@D=oI|/CEU2Y<G]a 7}y#/}uě8Fqa0!1JXB !c8]=PZ b0ւAԋ^XkZBS} {GBIUqG@mGZP"J ( RZ'!NTi?O\}ykZ Zq&V3ᵘ JUBcS QMlڋ{ּG>ć¾(fՔEAj ,%Y49VhTrmUY_ aH1-"+f㱞 NJ6`k_ݴ=rz;L>5W vsEnLMn|!FTfԌm[Ie%Ѧv@]bn.ҺHF7zۈ}=?{~玷4r1yA3rh8,ņ4 j7j>9`ry =@waw41,@[ 2>C.q۹m;BێvdۙٶmN8r7:l6;j$/(^^]]BeߺPxTFXh\[A5/?3=|&gxMO)T\˹,zr)#rg%N L;S;P̼ {6}54[?AfNp4Ŀ4l\ rm,1)7|e|}o!1}?L"}f${^QAзygfүϓsA \CNǺxA_ t \ʞ*}.ȫ!g$fO uh󼱐icQ ^vovt5ӿTsho=z'^x4R>=s6yLɠuzqAwa'W 9tn@@m.ԅd֍H}a- ;ݩކWx$VK]{9/-=+_W&ٹqx6A.8 ;C8xa6sFM p4q4q8Vqs$>C܊^W㚸o|q9-T粉t&+ktѡVBͪ е54kIm#[ ! _[Xb6Kݘh:kB 9y+7x1BܳSPq0SRzfFF٫M5e5e%hFNh(C,S4c4kU4{<nLv+'g۳(v۾_Fw]7&6e _)jlxbAC«ŖŶ 6KkW Wv/ }+N]OҺMWca{>xBEZA3,Y.ebl`ՁUc 6U-m-լÕ-W 2ՐLxd%Y=YTTZ&3O`%[uΠS5&Hf C@ԬJT-PA5hel"OJ}ZS5,eE'*6ahKaR`&j3PԽFK@WI$"3d.GJnr GMaFQ^a$eETn-iѕ=a;Iǡ0yo߆x0xI)>O{ }jВP?2@Ts(?Y.. / 1p2 cJfy`A `5 "a);=tI,+X=Y<[@| N܌ }qc+nM( 8p c`͛ Ût/S݇. &,LlbF`*'p c18S,$`+ ps;aGnܻrR<$;;.O?؁%8,K![lc xO#`?3>Ÿ}^Esg'ܓLC|3_pcg7li|fE$,NIswq˾c}ޛvM@Nx ' oa[0Kx=~M:S?Sy. &%м<@ЋIz/T뭴}WC,G`W0)| W-=  xbx&oΏy0CeP}M!e̛1Y3o'{@5x~+x-^*W*DdoB}bO'{"CstїmݠgY<.ˋ<&O|\nv-4smJLZn&҂󏖜sR3$a=7*|g'L$x'8x3';B9+|g'?= ^NrS ӄh%H\=^X/U~Cd eGMcHѱC0!IG QP|BҴZÂ<( Q8?~u/|~P4 - 4p pIƓV(mRZS 7μa odwCFZ6Qj]Cc}WA&ZQaŕ )j8PqEcUMYY4Z4\[Qٮm엖R6L(;-[Ol9ٳdUO,&ZvϊGa%V[+_£z?]7~C֯え/`p~Á1"vĘ e5#h0cӶk[6_.nXbjJ#F߷|?>iG@R3B8`jsԌGȩSi9k8Pn*H(mU{xZ:U=-*d~k 8<I'‡~ꇦ6l¡킲A(OQ"l?5D|Nt`wSϦ^>~[$QǷ4Z@'\rA!` \ϸ#r@7>(Yp3!oS^? 6k9q0qHԲ.>q,7mp|rhvHv,^8!HwqkuԚyRiyԅ̉:-8|oAs'bJ?k7DeQ|D`.\ z P9B&Ne"N&͡c f$M6nRV! hsM/Q_37]掷$QPЍD$D ]X4F2`!B#$ L'@'{:'w.)s)#o+'m++i,1sڵK.=̰%,=4ֿD2O"y*?m<D%4ahhTIaQFD"B$PB{<#w2's"!+q!/m#5k"+l$;mZ kM4 D>dSJO !|"<@&2DF"2H\4 qJbяF"B&!T%)*R|*Zl.bL.j02V1dks̰s={tGӇUq|F'u|ot\Ok9t !N0,ȀBH#)E@ %fN ( b50G&C :87j7)v-zЪ-)Ս_Q,H!{W`^+X9t+8,2#XYD%ВHHPL!@( v z\ԐIf a(M!0 1u1D֭aO:,781+*y*I$t6<$ZoJ_""}nu9#ǁ_?J@76!jQxZAGEhb&SVx"<?gОg@s= CY>eґE(y?y=>lù"s&/FfϕV;WB.@C86~/( y hAԅq RR7 4KB :?'B֝K`g/0_ m۞dݦ긹Z;ll Y>j B "4DBqdɶU+)ZWTXVXX~rᕗfE_V ,%G[ uxmho<]xʢUEEeӧ/{x 1[p*s/>!0AA0ZoQy0 a݁=XwՍKRW%+Z lZ(BƼÈ402FjdF jX(!( 8m)ŚWPw&;ԝBԝ]HvC_W$Y2/T]( .Ba{Ph^$%&XY0)sN :a;Zѝ .tOb -݇GOW{} 9cfxXf@ǁ@p >S?t.8N;bǃcw>u̦;"<6d>w,c_='=4  2_u34 I Ϸ_PFePF{24ϒyO܌%й6g21=k9JIb|3OoղV^?){&pd >4> ORz*@cph2X( 6MФWBt W*HUIVTv4є}<}p?D>J>Ο!-VʼnYږ`dI&mIQ0ig4iY$yҮ&҂&z3ԏI.Lb5`$k#ZgFF15 >x|Y ?ųQesuT|ƁY+=j ige/-c0`z=7[bhgHD<9X|ã }Yk< -v.3/r"ᕣEW^94dᔐvc*cfMg43Ҷ3ֶ3ڴy3޴r3V17 dؽny"MX&~ƇF(XJL`̧ #'|n0AGN( Nn7)lSSqS}ST.Bp8|[">WE*7@޳M]AuM5!0jBIZׄrhu UkHy7|6~4z9Alb_.8iZ)UQ;)ꛖ1`5\qF$|34h 8% v+-QjDB{>}<:86~4v۷TFY\CV>ti/|O+ރ;|nC*aRʀ%9C;r!QP?!8E}*G yGu=(zhH#G*M#~fNG:z/Au<``Cht980BD {"54(nx yǡsU /nеJ_oHh|y-<#)t¦Ll2I d)$a GF-( al|a@Syismg>Aw=C6a'5$O4@Z.]C, 4lbipeE$FC2#*dRTjZ8nfnnVɳ&SQ!L=ǂp;c_a_:G._ҕd\LH.6- dEri$# IG%HF &)6PZqRbR􁴴xy!GfsC\fVMl3TiGg 5B'$]JXt+@Plr@)beEKJH#+.r.^ ɨOc@H6x4oڲIgQi2?.^W~%lYp`3j*Cͥ3Lj6q1dJbXai! N$T3y8 Q2C"86{4rZ4HsA @3:H /"D2@%/,I xɋA Jbn@:!j*JbpT'afiϤajN!M_:۵E1XQC8QEޑ3V0ۅpt6 ݿ I#qP?P%'~I?Dfy8RGZW:tg*$ pB`kG9%n QغЊ!θ F=1,q'FyD-?AsT H+EIHэ<"&SأM_osDx;t`nKAL eC!NPA3dV g5NFOi P;r7r*aXף.V /axNP]u[2xVl,.ll- XA˚ MhQcϋ=(vIeC# gL"5oGhlhl+/ MݷkwKf7|B0C2e=x)RdGeDtdˊ(;jLɍ3&6ΜtADuL%LˢS2ژ )́2̈́{:l{!l`FpKk &7@rK)6$Դ~Y1B3 F0/[ʰ0ZŰ2E0VY;pQcvS:WKɒ'7hIa JM,,5OB3ՅX`hR`be* %6-,Y hjEpyq0WݗDcw ;׽rsܩ,5wj R^HUfK%jm@2Vj (QhSВFE53-TZũUg2ᳬ=uʺiݏe"CslJLRZ+Ih$E$GIh_$YUDRZפ IhG<~*f*}\2AKt{EUVwTu(Z`{^" A`hBhim"PBhq}!VV(- !B#hWJЂ:Aaj9vjt'cg=ݡLQw&ԝ@{(^ hp?'?~J9A[j_\튟-s?W{s?Wj/jtC;ѝ*tzO=] wg_A+a3:%E焥ܓ܅gu7tVնعZ;W s;WKsvvsjyvj~j@wUѽJNtbD?~ WxY+':Nx9iG p>w @^s^>G$hK8(@Ro@x9>x @w@yA7 Kԋz/geG@:| , *1| >Qv hv l5 t` x$ |g1/bދkA{+t/4s~$ 3=_c9>Pg   Z(Hy$}jmU^ ;|%O}hx!>ƈgb{ iK؂ xA$/ItC4]ӏ].Lj0-U_iICYuMM^Y ?7@< bŗasH՜i2E#).Ph@Ŋm'Y OOOO{6ל-&e;59{ ;K>{!ux~ W2Շ-ckG [: 3K'ɐCrRQvaȘ 琲!g4CҶCִ{CڴsC^n w#oXE(%^HН!D 1 @&7(A↙O+48v0hv#1\ `m^.x&],!|g F Y0oz8 kZT> BM~Uy#9庾- Mi7F)Ec\rHtFvD$8KQ,h"pa Û5q(h( ^(&BayщwLp> [#UUo|)]uN'9qpZj:' vM2RJ)y aE(* 2{a{Ԋj~˹+Dl_UU*Cx"!_{QE-AO(!h|{:i4P)!'âREM} M%wN+qP3kR-lT=[vJ0iNOMe?č7$y V~r!Pj@Tl"!PZAHA4t`<Hd)*8J'f) 4i 8hJE*qH[C'v<3cRpvRp UY.V`H OE(M@?#:3Pajܐ ȡ 7tjDGaSn$:OhwX5Cp9_l@j2N]E3Ul*ZmTBXcqJv1 BIPN$OL#M A?4}iiPIbِ5x9^CTU~G@ZFN]KH*,!$|bhj䔋KʨK˧TLLML G4/Qn~9Vsh` 6pPfZ 3}CI99FJŹыdQ7 cKFYQyg[&OI#@lPi C8FpVRT*N!p`FޙXv' R,MPbIL:JC`$娋n Ev7Rrśp!χO^H>"9}Gllh|墿25 m w܋#@"!Nq5qT.Epm7˶Y ~ )&DH!&kFʱ( *ؘIJ9iec&GN7+9oYn$ Ɓ͑][U0݁>sw;P%2t͊]3#A fJ~ 0s,O2)>Fʨ(c ,;)̶0㢳z% N(O425i8!d[@kV킽>xJ4w')݇. 7, &Q9ve0-:yѹFgW!_crv|N nPp vj0j65PcA1 dl6ƥі펴vB ݏ.Յjyٹ[`tBsV6f ٢eƉ6EՀ͍,l`dA`"+ZԸjQPseA44`uf+E.jȮ^evg*ݏK^Ke@Υ:RJ Q`*.S@5?01A͌ jZLqPbSЬN@b*T#X2a+iuXwuo\WKuCyݗL]u:IP#44Y@@4_:_Xql;y<{QF0-˗,y2Q|~[,<6gmfhp|OB'd ^r<'8*,Ͽl0cqN O`o{;3/-A0ώ.aỌ1_y.T(I1#BAgV$D $/(y%?b=Hdb(|KF=# e5^Kp=3ƊOC1^ޯm/"ǀo!I<]+U%>x4?oo1_?HLX|_ws'ᣄя}>}6"})GBw8f1¿_?s on~@l~ “ZxL/:7|·#?σ9_SZ w>^O~.Mn #y| "1"M$J${?gm_ }]=ճuZٯ=ܳ#0NF>ß4|ç;|:_2"y~E݇{No{?{3{o{9'.gokBffKObVa`/Vbl/Fb~8m@ci nU#~~^hr٧-眵i(З҇==eWڃ{ ~=h퉠} wM{i_i f2nBù -g#4vAYnj4dB/VaI]Re\[]dߥ]rߵ\Eyb5G0FGƶvfQ25n^oLgxJg(1~;aB|w-kQVkuVoVoսg)gvK0^9+Ɨ%v&G/|͋b]@ku=4F\Cqe4k-}cI_m=c=Vg=cUSU1Y-hCZ?jQS hI -k2,2h+ZzǪiz* {+b+{+§| P%UA#`tg^a%4ПVDSV}XeqTHM%54QRK/I5]4P=QRDQ}GeKS=W=UOC}~BDTR!e4 Ue҉QKA1=ԴړbNOk9AMuSSGMS 5UduN7m}S?=GKŽK~C]~o4)Tt 'W4fӋLP.Em$ueJTw%Umd5JHY!i 55I`$LdtNjPtPNMڃ$HzJ@vukDj~3Hk"꺉#"!" Ԟ >|jRTu"a]-kQ+i85P{B^ }X]Ac_TE@hv#ɽ/R/%'ax#o m AYĵa5'Pؒ>0vOu?f}6OZ>kS6B>pȝ3h 2~݀X|3v a{cvgړ-͛kv(-KZs3x)~e>P`>(sCY{sϘݟ}yލ]}}q6f[nuPC#:ZƵ7=>gy\_wG |ombO{Àk.x#^x}x݈Ýڊ8؍oxՏ? 9ґ=ϔ|̯7'qPku|7n>3pbWOޛtAw6lԿVk֡fi׃~i>aCO}GowL qA|Qꖳy늻^wwXnUVg3 0a%XW~]Ȱ8vXu{VqYl쐻xceȍcwLAVzB%oŋ.Jg,{a9}ǟ=ǒq!#N7VMV;b[9,CeqF㻏=7d yEF]WrLɟCQ|ʓW+'e¹3v{U?,GH;(wyHrb-˫a0^f ̎/p<8=v_pL6k-Pp ߂̧yNÑy2T|+$DzHUaPa5Vw,L>G:'䭞<ғb:H~k}S$~I{t/4OXn<#ǫx :p?t<Ѝ72~__O&~ ~њBTi_Hw0'~D|)~-sxotJψyן_S/=~ WKLzF?|{?Z@gKM_ ON?N(37s7~ c ]9f1r9B_Ogz_wupowvk@f(?J|z_xvv]?uLoTA3=5h;oBw|NWv\?Grl/'rz/Gx[Q|bK/2~1>k1~+-~)O >o o~O^. ޭ-q?B߅>|||| ܞ3|Iճyٯܯ=O_{{ρ-m~ki죇N5w?O'rFmPVi[hghsgoFsO݋{%toc~5~ьFgWt8CD?t=\'oFϦkN6gY_ed_eo_veOcq7p:j>fs͝o8C,ln=Ln? tO7kEhModW?ba?fck?&cz/6ay h=oƳ#gj=AgJ~K. hЄB m$4Ί9;ZLmYӏEՇe=׃=ڃ=X݃={M>/ he@͡,K2Ɇ##zn1ƎCZJ,i&1Ė c'i2{{/.| ߯A# -h`D̯JKi6Ǻji'k-z{-J-{.g i-\HB:Wү\Jn-ŴVӂZNi=YV^XR_aMuEUDWW]as5U[_=gŽk}gAW}^=`5"XNrzp5ԄVSUQWU5aSWkMa=%uGR\Iy#KS_UcU[M~ONQ-PI݅Jj#TRӘ:vjҩ;0*k(砶^z'r[)!q:{'j{b|˟PߧGº@] kOYxҺikO')Nlk;&ݖ6[L,nR:(){ڦQzw{({2Ǘ|(wj.yM’,uf$K]XצJ`&m4"فGegQcFjw}U6TJEoS47Q#ٽ7齓C >CM7׈hl^bDv#SZt"3vZpm>n,(n ) f  >_L|6AHm  H&AkZ{EP۫@nwzPܑ>~VɛGo/|{o"|! 2|GjOHoD_IG=s{P--wށ:|M[h9ޅ3NP8' .tN6>O 4>-MF&6{ᛅM߭5~Ix% i .7l'~a3uU;4GK4<5\~OTx @}v|' '>{-6\ćq +7?7vKlSnva_5SgE[5>ľ){;Neǁx!=Oa+\93qwϳΰVBwgo&ME3%8s-k^dk9-:_s3< /9 8{wnmMץ_bO?:uW??滆> gxKA=E/0 tM_tS\uWuEe5.q'&:?ˎ3 ^tF}ʨYunU_-%ċ Ywv1[X'gۑR0a&wGN1Ubb_:?qg|y#18F<ǃqAVa#k%?䢟som66?>ֱw|[q@?''OO;wSn,eL ~|'BF#gEV2b'ե b*wʟcZ|˓w/'.7m ֺ93|+߯Wswy`Na'sLzw?73 9SGp,s{p"u S}I߁=c" x cG Sml4~W(~U:9gkn}eC=fT<d~)OZUobE*__w @iOObwo(/F>w]Od>wk}֯b}/J1ҿ[?OM4zIwį/D{A@3 px)OtiS 8|ІeTx j;-|pkYx) T6?H2o._kI9AhL?8Z'zj7xz'x@>?2$U7H>JI~_I>'E>QAF/Rχy_wuo_Wu}_gtp~_7?6G?S/,*( ޼sM=-Y}]ϑ}~a~1J_*(i^Y,@s^ ԙ(pg@y zSz9 wk{7w{7o|6GȻY=г^?jݳYu {9Slp{=ܵsN{FZH/h=sN^nȞM~ *?-×g|~g-u<9F"|Psfϋu@=WatCg -EG]EsG!#tMӿI}ճmZا}Zۧ}ާ >e~Y" ][_>4 G = 7t7߈|+6ܒrSm6ښjs ꜵW4Gz37S{37{7+2'biHvjFY+ɚ6֎&v4ҜjIC)Mt^piM9 4RhQ/I]tF5Y7@kv5Wdq5,3n{*l=+V"{l.*~J;jm!}޶{*{{b||,ҷ" ŹX} *ef؛Sd?N](u6tQiEmYPnOAwѓց{{+z|,̿V?e2*[JT>nu+ڢOi[xR[҉mDܾkz 4&ǘZjnRڻg-;>O>WM~U)Տ2}i֦´&_,/$M$"܂Hu=wtwQgFzeLFSDQ'>S?1~TKaGyx({$=#8ۊAQDAIPގC{ 'qBN4WWACx;Ak$,ӆo2}tLP%H!@{Pߦ@~o?'cu>(\>W<yQ?=\~iGIX|O'|wGp,=&x G9tq :O,Θxa~8D>#yg7XesMK5 A^hP x #ߛOYY DhuqU-Z/c0rwk_Mӥ}:SϹqr -b|q]ot;EÒq/]mݨ˝ګz׮S:ӱ]bQls W~`#L:A,Q^/w=r3#}ǝWd|7fPZSW9y ³/p!B}ȱy#8^LəCr)?Nĭ<8r_n &ځ}=Of27OVrg+ÞԷLz; s1g8 {7{Tsp 4 |_+]/0,f!0lQW` <3`+ >\`6 }Xߛzl`i0$xRL̼t;cooBrcPG+^_v7@|,ߊ1`+wϬOAQ4ID@t<-է/в{Ny(C_1A !O8|ؐIA^x_m4ſ"]}IǼ{\`<O#Ƌdl2KOx?)~ϊQ4)L%X5K~90'WzKGoLKyYG1?xo8F<ڈR$\}XgoR_!~Ry?IK9\Yo! >{G@|R<|N߄ 7;)|\zJzFA܃ _|%~~Ϸ|lXJ<\NÉ= D/Qzg|(Od>̷s^oOCsI3e5>/ EϨPox^X~mW||W{W_g<Ǘ}TI/[賘l#g>ق  .0@$ h'@Do>ߛ<:S{;ȇ7o% dO{B:ў{EK]wqzI) tVRyM/9M5w]=υ}õݿ-}>Niޠ~bU%Zt+Nkگi:kNꪹF:H9mȾ -@? pOw89=4U5&ԋpSQ}VfnWaՖuTJm\uuΚدZڧ}ݧ}Z!~Yx7{U}תjئ66[Ufu5װv\zPM<5ҶNKZ,-4оnB Ll3yޚYۗɽݛ-Yy~铕i}ϴ-kа]izκ4k2^# L쮱Ȇ3;i m}ض{{1{' |/s2'c;2 YAȼA[Rl0ʶ+ lJ[km&;Njk>_L6_Çl3`c?&60_e~}lJ/Rkm-ӶrK*>Ң;Hn{-|/j|/j}_./!p Y…c.ZۖYm3f=sWq]mfEUYVwKYdP>U~Xc!sE~wa.߂}-+U"`-V[.rv+KXmj*3vp;MJ1)) p()DuS_WUZ^kk~N=LEj#RtېDݨPws{)ȧt[&ۚ?k,¥WbJ\hiq&w(ɟ*ͷ+ѷZOۤmʛ+hLIWU3)p$'D*;$_;R.ƭw2h"k.)*|ԅS/R0j({ G_p hpEp Cc)(8PB A^$[iAYtPI@\'<)/~ӇwZ}t㗔|AAgwAq؟K #>3\^Oz>Tحg=\O<6ggDoH|oGz$gr2;7ѡN/t.GsP9o,lyͅm<'&x60[ f4{ь^0!=6 AH˗—#TZɜ1ğa7a50;>:tAw6}mVk֧v=i؃&(x3yA7ao0)dku86s3W}gqq߯=IFq*Zv_qVOu,nsܺ쮏uGۙW"|'Qb.q'^r{-v!q20917ǎ8>{=o"s}dr\$.c<m,dXDz=^ rB9/rFɏ'9%価\پZƺMyhyS {CX<-G1e3v'eK9)gʝ_Ys,WǹeۀV`xjF)8;QGS&s| cZvqMe1N8ܹk' .x[rg,^`7:Op)s '9[6a]ߣ}~&#O7!x~΁c")1^~.YO%BhGzN"I}kܛ6< /)<`w^[J]y *#mOKG!/^Z=k؇`Q@x$? I/jLI~ɫ!#e䏈| :?cǯe.j^ϗפWҮE{ s+,c71q|!f8>Cx$/4b<y[__IkϳiғF}Po_9&9/P*ǀS_Cc(>CODbg~ n_5M}Ћ&=ǐ>)>2!rNԏU}QTCS}6S}ԇJ$':} 1-:/>C_ X i)|h'S:|_!C0B?-B*:|R@\?[HzͧzI4EG7L|7 C:F^ ^x v.l@3Յ.lt 9\ˤ|ȪwűG\¹{Al|p 5@Y55^ 87^ xs%k=]uK&e:Ck%^Q$ߏ>|.g }.'=x9ţϦ?{>RXǠsqh/Gr4:hF}YtD7z¯,?{=߳| wz pr(táeVE$ZbіG'"w4ݡkK2e4NOB T(5!AzK=ϻ{<3|<ȿoc- &m%0i J\5Mk: xQJ}85XUGYw_?~[y=Ž ~Q>]}ث'!؃ Bԣ{S[أr=ZM/<)k#޹#{[{{9+|9Ǘ|Ϗ p(n!]Z&rgGa.N;'Fگ:6.l9N+[Hlk--"m8귱q] r\sڈ4&!u⾮[.l@8Ɔ{#7ΞrKM6i{_{5g+6o6g}6_q-l mmmlӶ3}V6#كkg뭡-djkwmSGiqѽfhzfjk^ç>o7l̶@l0je'Q3(FMC{CZڙhj;]x6YtfnmVWYYesKխTfY=d{Ӑ=YM>ixVp\K0[3Řlfk0c[m27ކp,?1 ;-n ӻ)"0#|c|/27e_x\1M{:^7kܡ`s[{D{Ko.ͺ p(.2\+ *?l0菱v}> &^u[nW.G򾰥wߌZ}fݗ]pYa .UTX/])>txګת-/V`㽈} K~w,]`z7ߟVcV X5!UTQ>Gx۔fSM5~5TFRDYn#UZkRs-%ߩK8T#0x)H CبCD M%_|b> s\iʹ.j\i &u<6|_x,bX; uB'hJ AxQygI;"Udy {HmvTa#:2(EE) oP!^J;@G h8 Ά܀ò6xá5 @S&4?nSOܪNeo:VqZӎ=_W<{2>!¼w? -dwS/꫷a7z#ed"~d#9t%gɝ7Y'WǧxDz[g2GmF/ls #0+8a^d'. ԱLC߲|/kx'`nnŞ- :w\ 2k\6y fL2N05f`n 5xnF\p'\Tw pߘ_)% >I1Xg!,8 }ogÂoQV( ^OG x>HAoȀp"ACuT= m{y/*c>ɘCcp׉0o噸;WFW@ocD= /t 8y$Nކ}<tqN~[1&%_䁎)4C~!<گ1ѧxجo%ӷ_xf=_=ǻ~[x'pύ75^[]ŋxL\kV-IXЊ{T*{>$>[GC;kXs>Mя:=hg fyO9/s# }. Q!C !/ÅxUTڛJTjsO7>?*^czέK{HwL|Goa;WA{ ڙl@35wr΂r38š)>/q8PXGc8Φ@ZoizHh>1Wg6}`g*<>,,*80"'|Aq fah0 DgY :BJOq4r)^͞  7 Y$g&R,B F%ѮEVYytc%ҁIU*V%^M_Q: E4dARHRM^Xkz?(/H>~LKwiT3G9tڰ1jFVkz-FnbĺhQk [(~= =`e;Bo#Nm`Fr(éKR]zX5C)N<0k;C >3kB N" [pis߇|ϧ|gB_\|Z5YZ`8F!i 1k [ P챿ʦ3"־{=׻<+|ϳ|S'&؏|dͷg6mzhUPym# >]Eix_Wp|ʾwdpGxd7Kҥ݂Nm׹Gk{RmE΋[.Nn5悳 yM m"vxv_}-xؗZ_GyIE9P7^a?JX7r?%_c5Gej]wr%nɵ[n=mZiUޥuyUVKY au4|W"}1?ì\-CB<ķh)n$KYjqWcUsħ<{pSݜ6laЩFWb"hԗZ2Z/^k˯$8anASa4g´R_(qiI{"e8iÒHfDQG}xlGeSDD\ԍ"UW逯хk FcPy ?!PB 9@|ȠP/Rhu^ǥ9t!kȝY#WǗxSfgy.?-f ֎o?K0,d'9F1]/&dG9t)kW\ˎs/>m;v`) Y7O.`(SYF2^oba<*ߤVyU>D@|ޔW]R~ /9y&{|*H~#|0؛XWWy!dCÐCNȧ O~>=~gkf?ez+ڣ=M|sd|3?e,hi53Ef|?B1'x5/*( 'Se;,ϔxJ<%7rgxX3S|bCp( sxtN|=&n)#^Dѯ2=ԗje v+,kzGHR߂/@O=YGLj3m9ź+K=s*П})Pi)|i_2ʳ <{0vA v~VҞAI}$Y~@}ZCa0<=G2=kzOg4}F2@si2J/\GOTh:D1Txԉۜnͪ6ҼJ'ͬ2z-~R!_Q'?C]H* So09uR;z%*(NzՙX7Rf=H$r]Mz-6$Sa3I=I5juo_ Ip^-]ҞQKk1K QK;ۨmܚF+4~ac~c1X_g_- `3p tvY 2̓]wƜ @8xR ?Z,j"rsx܁t\L@re+7dXŠW0k9C5Z.b':Ō,b5TҽDdx %Y3 L'Ł$ i@I GV=nq( IP0œ)T1J, ;LlJ ,.< j"a)A-MEix uE.}ݸ:R9Ȑ$EhUbH %*4RBgW&: TRi\*+eD rА&I1qEƬ`^yLa6ԍq ɐ dN':`hÈ&(p`z$4jR"LR}BJ H.*H04 O5 `!E,1  0넄Hos5nd&CUX&EVa a"v V+=ZGp;/%P̨#ȑY)96"#*[X$?"`3w< W7յLg1U jqA%*&DPK<jB V|ز㔅2k &INcP Z#KN_^T cqU V`oz9Nijqb\1,F#'1Eф'CBT/ \X2ɒS23ƤQ@+"<栠G}6Lԣq`\1;˄X ٛH%FJY0!!/ 6̎j1J%PJ>YZbJTV|RóE08Cx@F }2ܞv'{`$9BBRL|dPDtP"Ur2'(T(b'"Y`)_$b1 el0)?w cq^W&9A#h0)2z QO$t H! P@+"V|rcY )Lc>_l0YBA cq\+W&92jCQKhB$ԇ!<ɏPi%+N 9JȈ+=XGlDY&a!+"(%b e/] 5IPb$tv?0 $MpdC&6D,A*% S*GPVBˬW.5NG% &̄LAHT.pťLqV\wǪHHr%F:Qb#N e R$Ƈ)GdKIqzG U-;`[X}ÂtH QD(QhPb SqdΕH:ArH#ImȆ)JpdG)HlR1C"-HQQS.9F~:f#qD.MhPybBSjP\P"Gp@r &Glr*G~lbD"6:\"CKXAelj^lĢJ&+"<*P9" $4,1BcKXLk,Պ :q7$!DC"K4L! ! ነ!ˏx!e ]p 3D1.^Q˜P]5@px (-cG($qI\=imݹRqB&.'p9H,@xj2>>ؑaKh! b` %6%XQ` P 0\x݀2vw.1흸Lk)U*#g(62{!e ^t _l P11VℇR -`Dk3 x]C+"H qGܯ=i BqB$. zT#u|np.\= I^@;vx/ލ ZkDuKn+Moy UTMG[$%OQD3xy`;Hy )/]Kxrj] go7 솠H@7ζښ0Er3? VP@p4MjzР03tCL^~rX" @>JdlAҩ]SʥtS0Pcdž"d2(0!WIWPRG?4(c@~B 3 \zRViOL @%@(G) 1GuSL;S091%Nq,1G@ƮqZf4WXɺMU X8geF!P< N+:UB [c,!I;TRG( ,4R`Q ,2ZdPղ t tœE/*4`˜eH[ (53α R8V@C/!`@P1 &%"WƓM ,Tr ''?^xh#5R81%*ltBF--4h'kP|nQ0L l%@JSUB r@ő KTѰđ JF DHXIUC@4A + R\ohZy%aP%& 1L aTI.֏j~\)9d2cB:HI!$tJ !R`JqT'EXyEB$&$|hF1aC u_br#NR%Ɓ*(#}T[=ϓ%qJ ZEvďU\RY *%Uk!$(rB$)d6aJq]xbSʁ.V(U#wlRuw[4{JK !%,=JH uiPHhH#JbJтˑTX"$W g亱U^;OU|eh9B.&vvUYN{r dR!0|d2I?)CX}"4m@s gEusqt\ R<ǭcE AE_lfEa^qc)XN}!H '0$Ȏ)OP!ZgP(vH6 %no6nUKa"T~@#ũ,Lp̍!ĸQZ&F)6ZHtAx04"dHc&8t}'fB!Mq$npSSĮZAb5 \tPsc*4`BF2^ǜp%ǎ)A$D-H32]P8aGt;Fqj*ՎEq!WW,[vĺ~QB4PBvW'ʤҸEǐ(X;Xuu#h5KWcj(Pq#@8BG&9ʐXv,"#ֽ Rφz2[5RqH9n %P5a,LYda*EGal;ȤH VJhF$0|q% ./g!XCP@`uqn\ UP`1Hi +>J_`!5 1!`TۊĊKpDE$ZP)-xAP2* quqH8.! ^LBRXf?ز/9Fx!`) ,l("N ¢TILĺ:yJ RRd4*dAֱE.*,tqPmq6A@̆CTT-e2j(%X8eɫ )HVQP)ℕPp $F[Ty([Vհq%h)@BTZĊ%Up2")-@4PĊ$' al\^t ["QGd*8S8({bܪ3"ոO$4 LAɔMm"E'Q\pe'(T0)*EɈS(Qj +#P (MdCDYTSprdĈ*AXGd",(DtP@D'mx/nUڌ P'NFqңC'9B4! Q E%+N$AJ*Eb_)l#h$@hf 0'M0hṸUj2.VK4 "4 QĆ&3›yw/]гkN%F좦Q]ح+t1LSVMS00M4Gx$]C$=lh`R8ܐA$:-DԠeF@ sME :p Xbŋfa$Q<*Jc,K2cEh-SUC\`P@T@K!t4g2ZB I\~r"X*9^XZjV.8F+5Hh! Z:eŧR( +m j@d7B8c,D a{I2 dÑW%*`UI%EPx rʄTH&A@<9*P)88ʈ"bߗg "Q 0p-19AH 4rP"A.a#mJdKƌ;bFt Hp;BK^\@nѓ[Cr!{ S16hya44B j0 /;lh91K4H&FlrG-8䀀)p`aOqܚ w_pPc*2TZʀ Nmc S8A"D!e/'f @c;gv/*ȭ)r %06Ru/^8&HLj aTsrHP NprG!:^HpHWێjE07baab1P0B!*`Tqɢ#ɔ;4ɰ# UDڸ2HK>г!f3{l:חkYf[cp%x=!)hA&T@lA(EF)!n4iz# NTTθ҃Q4GCL=!__=Q[vcUŘ,40cW?ƬEՅ,2H9((I0̠R$3ƕ0A@ 0hFR?nяeUŖ,@mQT`A6VD@re)24 #J 1JI[T 40F{3Bsqfԏksb5ݲԅ!b!rGAČp5ֈVI2 IRQRӅ-H|A@ .fCl)-;JexF r@D^ tȗX"\^a`q5 &PX]sC16HyEP=e$VD.Ҋ VOă,"XX项 GYJ]Dp'#=8S>pC : Q!_j|(.+Bb"5 V%U`aHaɓVPJABʂJ*ZPEE$ Je#!ž+=D{` G-8Fd! J(Ub%*&THuuvē&"LDYb UE) B˿"ٸ_d;QQU| #J]re%V^|XRE MHu'PD)GRQT)NxATAo"qf򣃑*>:,#ILd MDʊN@}*&$\,1BJTRJ9aULPX~-M! Pd &Sfxh"'RZpE'PR>qrBJQJIKU(qh 0&8MQ/FУqrܣ+׆mPՁUCQ@`@"(9|`qh8H9ggPjJjYAaLk!.Y-sc gKE:d+*D IUQ*QA<`B6Z逧G 8p^0jA)cXD\}ѥ!1S9Yr .-PhʚzdQ /TxÙ"4̅x 2Z3 E}D@@l u3,N#pQt\g(fU|ΦJtS6(!*pxjCXHehbBS`RcȬ ZJ"v0䊇 E:\Bk&"xhjV# )]z$Ē#JP&9M$DV#KD mSFBř!`IObȀdă)+T\Na r*dUJ#(h"Q 3-4V)`IJ/EY003B)DI և,>H8#J*+aDL0HbdF1*>XyȂ9%Ū%%-=AI7=N6(1;e-"fAli eu +?RP9b P2 Ѥ҈#JxВ$2P9@1 E{brRQp~bǨ:>|yK^V]tD,AQ>h"id#:h}@( 4)%hš䂑! !G08Pä`-t-/aÏRL@CJ+?Zx $)SNB* D^44# a?QJrBY0RNDŕG)V[VynIUݒ:˪uVD#0qԇ +?^PDF Ch8, =HQ4L&挪t0d* FD/:Lt,S$ΦbڣS3!S&F~cdV/) |#ł7@yQ:bRqA!i/:Nx1)ZPgo\!c !?4AIp@Rf蚠@5LOL(-[#DڀszS*˜xT . b(zALL@F 0q)T,BE#XXUF.t G12zɒRhʨ"1jh~)1 $d`"&T`v3,x*료 aTc٢JUK|dIRTQEHc ,%f#]Uk䄟 &'B"d x C6(ABW< hEC?PFE-4,0CNU4B`C@Jrܟ%'"0 xJ#Y:̔xaQE1+xQmQ:P0h぀f쬐\+24Re #F/Tqu=##ZBHD8EE*2@ !ɒ1|QE# ,$v )&{-dh(30PyW!b\c|a=ad˪ $XV[( P˜r o;9C@`q ̉ X"uE8E+,LYu * (KF]L9颊Xbt I֑{Ť )cjAd` E!cP SBȗX"]`E"b,&TaUD '/(bOUDLfY3vYFUA ""aX´FX=eD+,IXBE(,8YmE i)G0"Dr9d9cU^9%?DIkQ0̮J0* ;Nh U:":4ˇ\Bc:° **]\ڠE9rpb#f"!uZ"MV`jtIS%HR&a-RDN^g9hL,kU*6E@cq$d:d"H)W !1uj)AV i*DD!PPxȉ&@r$,[^``H,];p+>L8#ՊU+?ZAȈ)ALH9 O4eiEC$0ttQa#Ɗ/X;ctElYR{bA$HA4H )B^@0F"3% J;a)# .cXwvY݁Eu*T\Pyn9r: ,>XX9 iR2D#vK0%'~<f -e8#KU/:\xr#)[Lyb:˩VHAȎ&Fak2ET/?\$%./ hbRoA!")`Lsb#S]Ny؂: S.$Ȓ tH:eTKA[*RNZkYJ&+T 1 18X1˩\RyBN!G&"OY0"]FTH/ Z0b Vg :5TeNk sZc6\91 j]Rw(4Iʐ? Xf˂pMBA h0sep%@UPeuNi(sZ#6lIË W>@3%S ѥ(U+C{fF5`pI`CT< -TX`F Dȁ@ QF5F03pQq %PFm k`1{B/P{"&V ̫ c\G ZVF@re*2@!dɅS4޸ƒNGQ,$ B9!(JE@QΈbRZJar*eUHjU!QTBI%ĴH %HH ЄD*%dӼP2@~ VZZaqjeHjU URRA :UH(CT<%K${c;d*gLZXgOH͒cKV,>^ Q *AZH œ D`0<2H92VtdaaEuj\PyrAt6ViXL}r#AfD0G#9)qJ ;Xq!I'L18PèPi/:b0ŔNJ-/QR:`hG7&FfPs-j;dBf*6Z@:FS&PT.;fhAqR8Xx&J^lR32bv`uk4DAK洆 LmczE1%+|bA%HsN.;)!Η1p UPgF25d8C `Psʣ>D0HA$K=B˗G2dx(Xa2P :E4^9!T7¤EuǏ+>D$I`&?TA%#x /bPq+@b1@Q aRqvQʨ"P4(A䉏-(XLtg>==<<;::99887765544ۭVjh2XZ'P #>6  . & pP 3`@ @e0FL0_tEK,WT2EJ(O4aDI$G!2DH ?|CG7lԠ1CF /\eBE 'L ;Uk+j*iR<;:97654a.|Tx2X~)I"'L3Pj ޏi>ky"φ䱔ђi5G 9\ b?3:xI?7u?BsXb|=2+*Ï!y< /gmP߂KKO-VZ'kB3lVw8lDeU?uDZ޳/|'U2<υ`)C.|t?1/LY/l%O_4?7M, s=sm} 𽒾?7d5L?D?7~O΃|Wk=kq=1tYvwjwυQxAyc;t#4puy/y,":גjOb9}dۇ3= ט~/o|_|O }G1OQ=S")~-~ެ^ȾGuk~cmv^(;?5=TGY}TG=d}\_]x^^^OL]_@]Nˢ^+ꥢ^)ꉠ}f&qvI#k9"zOJW%_%W{$+P0Ke0}kkhzɦlzGc.b7Z.G$wv1"o)z[TFHHO4IYIi4FSp` @zI!)0~ I;#jN2yf }v!Yg! }3$tZ"/Jz#7?#?Z{#'|CHF}hˢE4FdZC9K 4sAB<Nu FhhBz'r"7z7*"'{%&$A49VQ5pVʹ/H縠͂x g' |=?@PZHhm 5PU=Q=!.a~rV3 :3)-PّBρ?>3| Z'hzLJhq~H遘>郦^ꇸ~H^hD z~cЁJ0iz P$@VA_TAld h&F&6B p!hnf9z-l~QZf"lqioxz@b@pO! grFz2Lj8/9;GxO>wn[dU[Α\< PXH?.[[y뚇^a-Ly_p y{psxzأoAtq!QVF~,+|԰oX1%=ģkÛX/nzޫzܮ-a[7YLaBSխz]uGacęXr3b[12mmc},uD5'~bf1[|X&17=Ǟ8%q@>\ȃo#dvώ2WƺPh0=f ecr<;e?J.ɠ?Rμʓ_q-'e¿&3춙f`% fZ= @K8Ŗ/^>0̫y+.?'1~؟UT%~ a|"h<ZAL~? !$m~ Y~_x&f:0~ˢNKŚ'yczIa!UZDދ=ߓ=V;Q}v~_rŀ軒~鵾ҸהC_{{qYm2y0Bzz,R,30{2ga}jw=󩽛ڋ? N}v-{oEᩄ~襒^饢~ꩲ О*@%[o_CSv=au}Zu;q q=ɢyi|~K=C!C9}GYKG=TT}Vo\}Ջ^%b}շ]g]}+Y=v}nGOA4MFߤMMOSP^UPnM_D}!&oNM=ԗIiNUsSGQ7 A3S8N?0]ZdKHLNMUN`NqϴJ[4(ēvΧO4:/ Գ! cgC?w:ShE :-h:z "(,ĴP7ANAQ=iɽPP @k4e $(Кf`4t?H(-泴S>M78Pz:g @VBZ?Da_tEm_TD~/@kc=F[1:H0zvACҙ=K/04k;O@ͥP:S/PmSu?:Xd5͏Sz  {R!{!r w`t7M_i:/pvyԙ8Q3H x3 m6UXUsuV S4Y;hD5Q A6^| Z{ fAnDA|d?3W-Uo!WEh^9֍g-yo 7hw-\}5RݴH=4@ڲ}f؜Mn3?y?s;.a10s}dir om.l>괣V鵏^[hngqokm>Gw{y _|^d3lmK7)G;Vn[m~kj{^5|77OW6 mSVۨa1{{켽[kj迁gs\o'!5.-_yo[Gp p:OZq<3n9/ ~\?,x'1/Gq _v|݆<ɖɵ˟˅s9p|<~\Ő.9[/_s5{+L\ ia94G4=^WGn~W21vA|Ñx脋zn!6tQz[gu]uG&~(7|51).}uEGªcz+._|{~w6q)>{`8K-g,{q: G=w5,dıt.{.Ƒx^cm70enόyZ_׿5ÏR;I'{\g(Z']` F~Qĝ'z׃:8dx$>ƆO@?:|Y_u o<[7'ȿW k>p>ۼnTq014M4j6_,q$_? x٦>m> ަ >Wr _'y?m;߯;8w#5G'27[{11}o_)[| |6*g-oѼ\ο<Ч Yғ=XՇm}؋ۋPJꞆݷC}k}'=ٽ}n6Ϧj=eC/`M%^X_b_rշm@;q{n?A߾  ^)iQy{e>?aCu]K[U\^E\le[k`f@{h!3>1-^ ^ ̶ٌC6(sg9=c]}kk}_A'R~VS}[Ʊ{7.{'?.FO=`:xLE᷂>诒ު魢ފꭰJު׀)\Yti{z= v}]v`]4cquD\9qsNiފ諐j駞~JꩬJ뫾ھ_q / "kjj̲<,ݰr&yj+z69g+ TMC5SFQNQT/S\?%Sh?SyEOo0sS7cY0pvJKx9n糩~IRAE%45PFNMOSdPZQdQrOTO=?(@LP>B\F( >]SaNBg5 -DSELF1Ô4ڥ_zow2{'oRCX0K?aY o0] hR9~\Zp;۔&K:+ i*>:RZ#w9z$Oz%_zo"wzo OGC[p$# ihHD"}# 3Rhi""r:ji!gj!DXoH\JbdKl$KzDIot`h2CG3!::(1ӓCM 9'\PiARMD5PN@V#a-;@\u='}R'=Q>"5P1@NzO+N@Ot?W`MuW>[GpӵA>`36 Qag|g1>S3:O(|6ʞynv;K'l*tN)(l {vԾQ[@l/CrOdD|ODCT<|g pspkS?q[&l@l~);l1hƢI[FH[hy'n | b;౛ͅuw64ĀcN~߈8݋͸ڍ8؏kyՒ?=9ғ Mϕ|v͟EůwC8NɉX/zvONߕ}9ݘښ9֚s/h 7)$/[~+Dz?Ř9n9Š{N~g8o aA_;tEZ>5.6=Կ/=93d ۘ'=C_N:+bOS{Ygu_{X8j'}SkvXR6O2=Nūz"0n9!7'.<Łob_vͦ8ZNhSG1|o0"Ʋū؃m|9#1;>|v{Dfe+'S奩,{2b L8Ũ#BនP@ alâH((@(RBJ$ŸO- &JjWENkk׮\ QQI! qw w`3x|O6s >ދ޼=0M'vcrX yzġmy,O`o Flșkq+.Fz&;̭޹UIza:ҖWmƵ+p ^ކJhNVOqN:soX e%۔?}&=A0Hv9H}ts*uOWCVmFZ^faB{kj#5BDc5XNXXmnR93ɢ*PIMsAXN^q`8#ރOK+A <4 \雳@WVÎbhwsTNrʡ6yj)8bQ/rB3]2U`n~)P7ǏߌS11L;xwh.Q]DDuꈪ;4w%j(ԃiDnKa=PhgVH|6qM.q3P81}-d#t Ju=.g6b"K$S Bg=nXEx߅ K aD @2OaAL0Y黹xsɸj!lzQ5گw Q< !讣j.DM[3{4]Ŗ Py1 ݭ `aBF^=җ?jL2?t1NA& n| 絃8Cd`Tԃ0t @TA P1!@0b'B^'q))fI_;Wr \ČKř% \5r%ֻ{.D~ IK)7Mİ X}urt'`@` &!'" uUڳVicx]N{2H/O󁹖s]Z&H2P{0h,NQmNa?ivbN"(7ɐbS٫ PC0 beO t*9$qb#?(!;ٗ9KmN.?Y?sQ]-{}~'U@b0Lk[E(x3%]ާz(B~]z Fԭ_p yep\`njPnB:dQ6,2[JP9cP7`Vt?MN`} &z,fD<9p='uD=c>V ~~AE>qq yN${;ܹs =[~'F C 1SP5<@XbUt 9vW{`k=T@.>r#9L=nG[\s c4Oer"0i!~:]"ʜ@ד>۹Ó@IvU~5Z9|[D_ѿOL?^8p & Aڑjv>u,?Tc>Mo>wnn~ZRR||)ޮ}VrUoOq-U~ wQm";?u׮k \7o7{Y07Bv S`mlbʺڊ3M; >Y U80i,i::_ FC-O>ODپI&ٛdo"ٛe&ټIecW{Ji%Lf”ڒRR6JF Qۧk$cmR@za2VQngny fWnɭm`i&j42vԎՁZ(JB50Pk>Y[''ctdl~- UrQH#(l<6(rm,nua`jk;[6ԂyZuگm"k4& 6Z&-^$kxҐMH2Z!Q۟òC[7AcX^:vV fuiNhڭeRZ]ڬ]*!k:% vUBJ^6J&9Z#YKGڢZKakJk/lyʖ mgi{-C۫ZʲviVi6ijTmUtA&1PHlILRRX5bE9 Z >Zyj:{H( Q}/$Jg,[*cV[VPugQr.@:Zԓ ҞzYsjC7R8q6E¹B@ς>: y*(U8UU8_߄>Bx'=Hqc5la+i}p[ eaQHqh$0gU)UBkD01B`yK:@U `n@o0̃FÃ!X^<6 6~[[2pi[FL4te+˒H;iNZM5fJS͡F5Hk*hv! @j6|S⵼x)9^ wpyjCJIR+wp"d,[.fc˟-frQEeҊ &!$0Bob2p1ˋ "MAO  yk/o.'~xZqXw '<@a\;i)2r&#PC+O~WT|AbªH[dD#YA|/b̓x1,H~o dO![!bȇc" "u He0r,3I%ZM4L>t۔/*6n!D= 3B`w߮\Qpk(ҹ8DB.8` ԑүJ=;0T{ta!䴌rȆ/" pK草&=rBd &Jވ?<1͏XQE'se"x<pɋIB0,_#13oQRPpH hb^N_IX \:fLБDD%V,"R>_Q+ 6_5t/*@ ç~D,!D" 8ǀT|1H 4F  )Bȉ0ѦEd ^4rTdH'- WFef^=nRM-%\UR0xg"U f6.JP8d ,eaʳD-Gk.+(sЍ5xsOhsGr-M\T=`MȐA iPSjH,aT.4#BoC :S2.|viGC*PC хZxpƚhpP1>:tܓp1pQ;ʡpI9.i9ٌzP2 "C'c;ᅞu6x#-3HSQ .\Q#ԅ Q~*Fm0Qۨ4NH&?8R$Q#9dz^Y ##lEv"rL뷜&Dv\°h8 B!6qts#Dh:ELM}fx"f-Q#՝PcbktDiu?2ݍ:wȵ:nf"p1c % B,m6PPLB)=\9OJ@|:yK &JjE,֣1QPZ ^%*Z'w^JݑF̵&=jpRFVJK#p'{ Dp!h"^L3auV* #YuqR:6BG!}į%}4:s 㔽'V8benF8a@X9KLFt)QN' K'-+X;V,ӵFisJkuB\kUmm,Hv&R3JZ@r7]1tJQDQۋ6j& ( Y83!촠itQFu+tUk}\kZ ztn5baꖐh$ df8.A!$Hl> H؀ ED0NG TRNeO! m $׹>h k\+ZךupKX%n ĭ ,n!7@ iK-@L d1焝(Z!Te6iBU[UU+rTk2\Zm_[Ӷ|mk7= .ػ= q`E'{Aa6FbWKD 'T}.d*.nў9nOt-p׭cy-hkW): F`lN]c Б0ΎX;aSLnح׎咁Z'ۧUZ$"?3\#+W^eWcl$ [ }p#y `-_X vۭqeCn+۪u+zNzȷE~ d>l3<`|{۫rɭo_VB׮keGm&etzGi;Ը@mc=G nyzW-scka[_[^kUk8ڪ JT_vJ9ڨ"_ ld-SMJ[β<[c8as[Zy<>v;O_'+\jC [dM#"YvɪN֫JL5zh-|ݲߵ߷~O[\P@>, IB>7vQRy$]['-umee.ZV-iFڶ6E{8 i:/e $D. Y&!'cV,І ݪʭøY>ӷ_=vebTϤR#X,Tm Jc sȢr`;kLvܔ4!E0wX`<:}?o$pxZ5BIt. OFgO(CVqоvZfj]xu-"i rV4/ S=G 3З!]?kHXsiy ,͇7x)B>gm67U>p,`LseL$]>Du`gU F-F$A]AmtIZ%FhHa_l| τ9| Vz[2uufn7uor;ܓ.x⍟7f0 @RG,G( d# (S26X %b⫘%CYH1CuTV~ۓ#oό'b|{!Nķ1ld"2XZ4éFt/M:5ؔraRKP!_ǚW0<\!kdpB9T T,|BEpGB@UFXipErKM-h[0XXk0&.qcg̐X)[pyg^?=xn0.C w2ARx&!#$?(PHXb&zN<:UJI5sЊ *,.)ZC]lB-!9|>w\P6y1hGFc 6!K9Ǣ$Q|4B(Ge |!4ăaNZTR!+FXXlt=P܃d=?lA`hs|"uZXNcRjhHVNAI)ed )n"zA*#/ԇ: $$ S<2C+p ;'D֢4nM2/4!5x a r},agd$A,01۰ĬBӒ.DW0] !CP$8ux!1H"A(1jc2 9(ɚZ(NRIpk(iфw2u8XH*@@ %(OXJAiPf]@K5ʦrZ ,AA 9Cn'@!+au Z4tMkzkU@\[CJ!&La4A #S!h2H.K$ #ߋK4y%4̭zF*j]TrӴu>,׆V@kTZ.:ŸW+-\!+Ip%n$}YG`b3] Fidx?ѐ,nq!0`9'h *$~ZN n°)νNM qVDp nceB]7ь` m3@/.w=& Y.Gct0ĀmRQL07Ej#58JyKtt}DsV{z+V^w*^-o -a}vۀP= o <(1܀aq5?bQ5#FڱIZ;uȹL|[?gㅹ8sA z"}Gyw۸2z Eo/@[#|ojي@_T4Gw>c<_c!oX~g-|ĭoٹt{A n[75tn٪حMrf: ކ.U\*Im2D#= >g36y3\=nՃbi&ڿk"4~ `b< L%Ӓ fҐ0cFl#*\!b0% 3U[7[;ݻV-i ?1N?_Lf]YVUݪ\TCKƂEX'-s ]4&]S\7$č; \> CeJ5i5*Ax3/^KhS+fAΎY(kdѸbֈmJ]Н\;ՃB\&L[D#N2!ZeU@lH|OIJ4nk͠MmAX[#ۚ$}-M 3tX@7A; !}yU #U 0B Df?a^>ذ!Q61^ Ty"e{ @]"@w-9.c#nn uqA.{q[8~k)ˌQ0e_M' 0]3v&>hssAQ<0ͯs\ w\BH ,wD> ޥPpo 4q. YFb(}{`FA: IVlm凬y <-ࣝ)v5 ӧ8B>%jQhDM 5)$uXӨgfM[ϳFLXD[-~a:C ͒\|k3σo1 XIb%%t RP.Z JRiK"c.L ~ Zw)*Z~iX8ͱ1tǵH.rd1U'e!pf#RiL5r '7I4@Kbx! f *Wsp4qȋ>L"7 8;dD/&*\ȲYJ˛(p 6!G1kE*]zvt0͠iEBI +PEgЉK2N@hƟuB%q]1AfUX ^*I:;҇*aXSzvA4jAutR9"2!̈ QF ! "atyĎ$082I +n8X(fJE8f$NZ9NJX.eXZ&)F眮(cT[Jc.)!4'T*6  'PTl2a:PInCn@UcU+ bL{\krz0³` 0#jT_Bw> :NF0]., #VKEpD15b5AS8Gq;:JQBX^kT:UrQAh NBgxvJ|8` i$ g#L6yBd , R f@F/TĚK5h FQ|62P!k9L K)Y0p2ĒdsK /.B( M3*o"0DQ!9V/>p{aȭX{ʰ)Ͻ^EU r/E&L4h~idK&TǸr_2LBY%lglATI@ PQ#`9a$> vգSw&mڙ0u!mt[@ұ91v+nul96Eߖ0U$\I}:<\ }$'?n볦G{m^rW-An.۹V Ɖ[8-u&nݖmUۑh1DZ[kUIOKopo}^+- EA1(.E\%e9\[)y+)[ kgkxmפ-> &D~g@ۜ/8RҖ]JrKHAŒm:*OF+UUD[aqK(p6,nɀGm|kx6֤5<{!ďVtSWpjRUa*#{5c+HX-T`N+DeKdlH{(onmЧk yڴV yЖk!Onq\blWC`_:6kŒqy3-]\ gtS}Nz$ضqhl'}VGjEJ:>{R|1w.]emWȦ5{ƑnXY> 2\ 8NpI!mC,,*r*myXol!o7@[8sy+g.o?3m;nxPNxbB] HS&jpMYd c%<~ PWsR[ Dd$@ +bc@ilp#lކw!) Ўxv ! jNHx7:S^J78`r5 . 7ʎM80A$Ɓ *l\TA02'_hPUJ:b&GH9B|tcĀ=216O.& $ !"` @' ͑4 P,puS[BFPJQe.ZRfXzv0г, ]!2>(|!@S$ %@/QH2%.,UwHjGN)2HI-Vw_ EƁ+V8 NDZ YS.ثZBw((A"@A*NI8Ԅ/FM\qQZ 7`r`1#D7R9##'!I5>rFXL-k(9l>`R4RR5 ZbI^H$!BB%!L1b&`Hqcԃ 3Dpb ^d0jLhI9ɎH)JK"BِʱW{})е6} 6X`* 0fÊNV]arc4"-HΨrK$3kGtT^8LrI8V!ecyP5JTys7TbT&P]AU "Hu!T$ $A$Gpd&'LrI^6lC"D2A#r숀"5 da\X\jņ N_Rp2>H`J#7D<@cĚ" j%dBM \&3 b }@0v$`4cplP؋ byN;N) U Hil*8:L0Ã?>DZK>ptҡ h B0XAQC `6Ip03Amp N҆i92B&@X6+$b}>,T\f!GjQ`A @p  :pp@-lzpSL)P32Fh@}#aDTyEm2@3Q֟Jn0 hq؞ ˃֧@2 ӈ*%lFQ%K3XAbɦ  B6 H@@Qu 44:f.+VX +lpn`]IsIp)yM%h:9 [L1,C*grR$#>XR#N9(D7P` i*Mv&GlLWz) }X /\,˽BeL0dCRF32:Dz (͈f G @hೌcW7dJ(q\bEvQG3UrصFF8bFS=B۰@0dU s|r`i哃J-P@'pj 8#Has" \Khȣ =$QF&U]:e{԰b6Bͭ.5 \ ˣ5ר$l~FӃ2;h|sE 0/@:hTnI42 HlH {Kl;aS7uF7Ls}GKrQKRH3"8q@1P)Cy@3'DnA\H)dq0.b%|$*WO΅r.%1 J.UOlEUrH$E"EP|28v!  ށperKq`\A"r:P)enأn&inZ).^(Gp!F:pE(ܠA:i@p2p!-,=p;Jk;&Z+姹Iuָ?gשEl`!$I$0E:!(~XUp.b=2TI1\g#qZ,nDžS\5P\-˕\; ?g)5B K sHtI@D9~{dKV\A' xp.{t%m㦉Fnj1Rn "=nЛ.OW^"aB;rvPXN!?;m!}[A2+p.C2|AWOG8\9ʸh Y5Hz7?hŸWȥc`,щu2OEESC[5WB%4ZB~^AskŽتt[<-5iFkh_0=lVR2*bAqX'˵bKKDfI~Foیm6k|iIKi-Z}6O `fv<%cjKLv9Y/ CZlXWRݲ- {jXvոWn*pvin֐2yj8-+nG@W$(YKfަl5kӱgl.6MmIJiXх+ܹ1ʵ kܽ,]HT*P"SIb+/ \y&jH8ͯUs`ߔe+nz9 stk''zp&kxFb~[uWq[–Oղݡq>Iub08B<:SN')Y(+w(d kl |׹xH縑oc{U 0~La%d7Kce+c^Io>J(`JF2$"y肄#*fshJQd#̻D:ptR𚣓JMH^"yB N!s4e$!qRUN㣨IEL9rB /9 jHmÈk$H^QaA he@ lXtfʝk6+"u \NlqL taH]@0er )&PPii]N\f(Y$lT# ҚmxXj=U\>dy)^:5îH if`pa QAN $F4a8BERrD(Yr4Rz!84\ܤaj 37}U%ۣcyZUJZ6,X0E81P#7𴡄G =J]D!">a:Fgg W:vg8f _8@ 2-A9A(pX3^b5+6HFi23Jq4j 1^!VH--sI(R!tC&Pye&xz2IHfLu*3aJ$B(1cj Ttȁj(+<b0T8AC9t Z ,R%j@,LX{]PYI KjH-y3V%mq@/ Q""J`D(q%r)AB'n )B 2XhcDB4X":J%/N-V)P_}UdAa n MmEBڏ>B,ZRR0 |P!c+ha7ZpE+b0򡆌"vpaB ,lX!i.AZ&ycC)=٠+oWsGERC`/P@` V](`E Va`UF6ԸF .;j,#FEV S6|`Cc.CwA$@ IL򁅽#vlTUR˱D`-GPu)îH@paj ސ1@jbJ̀b䅐 PT`eD \60Al.Gm?I!qpVHd2!dzҚ- S?[tKB 7V$:\pFAb@jDHJF+-rA4Lh@ ' <}cg 8:4sܡ;g>kV&6k$vb2,OZN{Ugc^و2]+R 9"(p$KT$ EEXR()oN4D|Q Rc5:sC%DPda25X6C+Wcdۊ=2@`c5V&@*) |J1@g l)@f X:0" %X,/$S Ê8APiFO+xb]"Yx-R@@as%֦gxF@gpj񀦘`e $T3T!@%LIԐjB#K9b$Q웢-\[z(6*NRPS] 1 ]krR0&hv A\0e `5 6@J 20<6AIG3hJ'0.gիp%֬rX):v@bStN6s 0hಌ ,רZPI@*:)' DrpZE&!G66Iligr K{6ÞX?+eaFhlsy*|It-Š^PyJ6L` GS /8]S5zy`J$N$C=BI82Ky'.rAR̰qLqu7".И`y)t)ѐΙt3*G D:0 4!zq !ʣN"t"$A3eKv&e`bs?|y.\%1n N.Ra.V^@WI7D:P +ØA (F:]GH:I K"i 4,ET ]K=s4ݮҝ ]9"ܞ, 'È'<#HfIXGJcL(%\*yS9hr[X!x<-3t 1lNy.]YnO^>wG47h@S8r7؄¦#hXGb ]Kh J%&Hy\HxCr_P.㐹wf˅R~TG?dۣW(1K`jD0lD;eXvISiE('H.a\"qs@a DQ}.4s/pnndr%r "MOhe5se2XP6IuFs0Ir-,5% ߢQ۵u+r[ [ m%vfmvHAPJ4j,^ ؑ{#;sT5&1 v5NKdz#p e."6)$b^[PE*-!Դ;@ {tZ9{q6,S1rL+%2'pG-6(b5GCܤS9Y&#  Mg9;+ʎ,4@Ȭ ًflD9"HvB`&rQ h*hfJ *jb!"@`1W `$J+=Gs,@(`mrV%ы,+"3(DҀa# b8v@*)LR8qR+/HA982Bֶ*1Y)s= +њx,:XQ]RIC&4IC,26-$" ,|$E$>$t R:18Q0E&nj, @~*w8@S76P !61Y eBvKDX+҉*4/VașEpS$c`HReH( CH(cD464itJ,EB1H4/ @sKnYlZTE'4$td^,T"X iVr"f\x0BF$cd*cC K`d?NYaC+N* ,Y$'Ø\D3@! `0V7*'`5Y)5#tc,X)kE*@Ҡ($ CΤ QČ GȬd K¸d L ATC,R$ Q)`(+ l .q brPv낒jLh:(,HX+D iJrD"fL8BD$cNXE&`P`E $:V#liGFz:9SyyH/!jnzQ1ٮ#[u!6X#$CΈpAČFȈxdJĈLL Bd0'.:$XjFi@R4Bɢ%ٯ"kcjY֤C͇@|!D|Q̇GxDLJĬP2eȑ!! )C :ؘA`sSK..PI tIV-Zz=",U*}7InPMr +9܄z 8 SR<D?J!Ha#$Z@ %:$]A 3ϱ!3A˥bHQYV%@JBK>3k4eQ:x@0Hpi"b^C@tzZ%;/P2-XA۵b@Z]RA@hZA|A@pcL`|EMZ@`E P Q8fba#8xK %0r@J[b WlWFfSaKvK<83L8ϰ8` 2`,`JK)lRp"Ig$| C5H稺8ݠԄ8`Y' Du)6zX); kday4iuPSL 6ѤX@ 2 @ae(XRe&`VP#lA>@l!* FS BJUCAl`戭$j# JݥXiÚ)р d!&%t@S K8#\ J41PsΆT+HEZR5E a$C" \];5:Kݞ {eҰkD@&x PN " B >XK9d`D3U 쀔(-0$QZ ȹuVmz e؞+Խ6\ !<<@)$xp#)CI9P`rΆ(hXrALPhvPʃ!x;J7tLK--W,ߏñq~1Vo:={yxw'|uGrK׸n˨fy{iAn?tJl;(UXBJ 3GV#+i`;H vssqG |%NS6t Eאs-r!p7VnSET2Շ$rO3,F ~@:` 9W|0I!^I$q#dIFI.spIv݆XK[_^KCJj Dʔ'KY3r@pX0Bp SaADEi962diaɁ( ؼEp\l ZfP2Xk=Q ˍU*[thG,BN`A2z%+ItR[4QbB(,:p@,.BFO0MNkl_ꔩUY4&k6dpϕX)\bE*Zh|,WtjCu#[0RIOB$Y/@,& dh8@/ d`G[Es`lI-6؟k=S0AEV(K Y ]  \pa%EL<,UJZ#FrGH 84Ts`ءkoM6삑clRSPQE VHqK X@ʋ]( ^xPNB DuSfLg*4]u[cqDK2؟)[PE*ZP%E+XRbEE.TT#ŋ/@\cƏ'$t,J &eF^4 \.<# :{QVɾ!Y2:!EchOkcl)1h)!J3\)AK6HKAXIU|F6%0@a% LdЉE'' 5Y>$ wWd쀬eR?dJ2Ɣ-#jP2F-"nX"",!vlG*pYɕ$8y"6E$$ B`ib`3rzC n!KSAe Ua%Wx #Zl%/Ux(ӄ5DR@8 O p2EJh,,Cv/Ȟ-ˤ&F.zL-Tҁȕ,I` -V"&7D!"ET c2>( c T@1A,.H%}h :4N0x5V0rX&bI*H)FʔZ1Rt2`=cL(E@d ,!HSz`A0\x ʼn ,8 }AgR3 3qƮX=˴bV5)Œff4A4n HH% 0Bj ؑhC'RdЀQt&;_`h c_KLk+KZP,/>Ԉ&5m|,z`4GPv0= l)]``H}`E J`!䠮'^X.ms,7LrOpKo֑jm)Ė3ikKC=a]7OX['b<읞C]=<ݻ >\\÷Ml+F,L qRԢ2!ՍyxQ$ ;{.9X?F&.r8!P1uM~.b. V#J)%v-in(S[[iis}tGh(cP SE%:0 "'7UAX4 RT&'1XBJddF/OR4!D24"E b"F9:r 7$i!I QKSBVtQ?~kY}/A#B?IxFb8YWug8YpZ xZOԙ7nQu n\fe65U jl]De _'~DჀz! M`]D램pJE -+ [U]؉,,Bl;c lE\*̞Rqv R U-?gL|G5R8l00D\aG1"Ʈ DYIEY3EYE](>nӉ0[B"n(r}ݯF~)瓚>'e=^tcbAY3==Y3R|pEͧj k=#4jpw gU@g`w4Ao0սT^J{(C>)cU}*E&ygA/lЊ[Q#o@6F~Nh %$ͻ:Y K~ەF*8[C~!qkhF~s>'D7[RyF2@IsB#p =97䥓nHIgPλy@{>}=d$N6)?/?31C2ZJʼ:yD\#Zr7Oť_RACka梵/Zx[fccaVtyS~$SrxJ_)-|o=-B WD_ɿ&D=$\H 4BDTly|RF^)'xQ^nFPE#;>$T6~GN0G."a2 -#! 4=i,,xH1QhbPcO#OdO#%UӪi<Ч1Ltb'rxu'6? &!k%znag k-k.k0k2k4F\Zv㮳!?!hɣE W>ǩy7l΃!e%zD2=1Cւ #d0xvɸ q=AV5Y+1V1Sq ȃbB +$}5KGZYqy03 Zqݯ:^3pl\tba]ˇ+ \B^"VCH%#f9 s|f,*/+E9joyBcgZ^ޱac%7UEBu'TH&2dSL1RO!e QR.'T-eB.1m_Il:"`i(x9oUS_u y|7XzISD)IVFeHYel?9ḤGc42GsCl&YBTGH%dvYylQ3A^*?bV3 >es>iVѳ=lfT7 pՀ! lh}'q+;3F}ݧi'2k-nf;YӬk.s M`"p FqW`'s \wm]R<^D EJ;R^0wpG0ف Q`$Sp-XqX'35XLs“0%'^gP#!LJk;8<0kp7\18eLgpE?a̺wwwhBZ>x:EX|frlg%y'zmޯޥޣzv)n&|KJ wwww V@{_bqnl:??A n^ڗ{/o  ?r}vW,GyI4'o_/ 7Jfx=s[  3D͂$&Ɂ'j}y6FT[JG|%?X <À`c<&[x(>y6?3d>{-`eؽoG>?߁kX2O/P歔yeE/-eߑy16|Ia$}T,|oRIӈ.vLzU^+@˹+>/s+@t# =xh+EY1:̊XV~r4 QQz)QXx$j_d}Q6D[DY.EH4LdKV%No.yQ ԁJS"u[ǒPj)$V'!ZHA؃"ï0,ha +auVWzXYaeJGkY?k]+v>vaaao[H<ځ8ޞ|M~ _bś<^>g_xËG_=^f| aƌ03Ʈ4ʬ4ά4> f61֒6T b}}a,ⓘ"?1DO]XD%adDŽP(̀¤0Z (G)|4h Mc"ژbJʧx;,Pt!OC4~_% ~p5(Enh#6;ڈxh#"󡍨ɇ C;> );Mx= !x! !»@.#[;4O| Q]|_=R-3Z9#N. wd&ѓ tG>2ȀG"UO4SχR5QPONl*JeS'?jIg|u[ͧт-h C#C^R zH@0Qh( QA!PBKßGRRFR+^itrʷJ/"ռC狊>郂xE<d&jQ?S駤Oi '@JtiD$($*3Jy ʅfSy>knx ;yǬdP,DZK¤$Tp%R%"J5.N`.qt,"̈́̔ ԬiԼ1q~Z8CJ \nxK3iftY~>`G xHKH&2 tERqɈ$ I$m#6>&mRL8:2eidBq2%F5B!;2#?$#C}#G{%K{8I$WߊM|#=2KRF0_-"&RRDʤM\ qDJ-KJ/v$f&Pܤ BHuâ&(%Oc#jOa*O㪕j!j$q㉋Y|gA>;^D2XePFKy0i 3@(\JPsKNH1 C)0`jXJTzx*y!Q#jbbDֻ%8,QT->*8/( |/A~-~R$A U(p4 `)G(3yT%B *(sÏ`EB<&z)o-&#qA'G_>E@+%ZҢ+5Zң+0)R{Hc2 EK0dOÑ U|BR4GτOG<-}0s3FVߎWdOlt4 흆`TnGg>QadND;D/L CNP<>x0x$d\jXpXvc!?yhIy>,<mƉ1q#đ DXT )c!E 8@)dWÇ4FO\KWG9B9 -bpK#+[߸|]Cv| A"A `F@NnQCm5=%|tXƒ*6䪹V*Ě俵&ϩE Xf[NOg[ '[%e7~ ϒာGOc@zŒ26XcFxf!Sr1:j:)z)6eG1+e9پuf'-:0 7ctklyKHv ."ӵO[UQ[5JՂ,2)/C0s 22'4{xRs'6:9)pI mɂ2 IT{_aڋK.yy\Gi$9 ksf*GnVr Ghe &a?&زȂ0h@ (= j'ځv"jؼDm!&!(p>'00&=x$``'ހX4m_f$wbFDl_Up2 ~+OYK/$[#y^wj؏q{0zX~7}X~^ /cx!x~?Sxɀ?* A,'`Ⱦ˅עW?J/D:|kӸy&mF\lca@w(OSY yBD^BPZB\"yA5x 0~OMT~3?MK紥O[_}QZN>@tJZ!aRiţƢJUHFUI C >~0y6lZ6x!* #~?*gM[yEi? jA BR@tp(TQ(O,0(6и: J*iIZIYhhٓwz};ua%S zx $~œ# 77i5VSi7v3Qg"UT^8p.Jlr $S)ճ2I&fdhb(((= W9 h4̅M %*ƋxQ%I/ӴNk Q{(5¥#T`.\FNYD"iCf'&Q%#'ia穏g*u_"4qѦׄ9s{6y+?I 9M+Y鴒O+I R$HRRI II%FꉤI(O'(;8T.V"}Z]^AbfBفNMa(d%>~kFy-y=Qi51j3Jp̤1&UG)!Q5ARXDm ʸi㇢JGB$~ !QEEE8 JMiNpg {*lHbˆ>GftG!+_+\\#+_`L</»-H{"iR( %X()`B)] (v2Ѡ ;F't}1#yEx\XGe;-'Յy~ρhd͠q5(w(8GfxUaG D5 +"D"H8$?qJ|@`=yb-b͛2f+9 ί״Ah׀&dȂ5#CkԐiQS"DI&@:ԞPBMO5*=ԬavmRu*Oqij;{S(࠴8_|_?ۃv,IВ) Z2&@vȞ#0bW|eA"b^x^}9!Al5UCfLhA8 He&LtVgl7[\W|+K \\x6re`k̵V3&ZiDXydrcmZ+k,F:O&د2L+L lG ۉlGZ2Ƨ|>Cg ?$㧄gܙ Wҟ}h v]hJڭvjګ;x`8pGX%;+x=}g YG^[IYR[t.a pO{w'Gރx;{{;ܻЮc1YN; C=;(Kދx=wwwwhJڹ5`>}"=H;5=^gdKGPg_7SwPT^%| D)y"G?__)(?@ 6p4~͏HL"? qa<֜Io%z^/p=Gd c|(d|q>M_gy{{-e\>79^t]2N-dl<$ :Dmԧh',L["u(@hW:eGU0 Yd6 f=@cĪR0tdP4haO=r"Jh#2%BM!: "{@h !g_>3ZPVKx41N)VS-3[QL2b8}2IJEҩ3UsR" A~>uTk8PHLū :~OmWsWs>!jAQ˄PG BЄ:rM }*UC UF5NI;@KA0M1$A% fځMU5LT*6Bu5g@Q^tkݬMJ:sKdNʮ*\E8/ntjb9SJI3TiR4h*  Vj =L5PC ?`9PKL Q K\)SphLٻ!eGԒ=P@x<ԂR>!IP@;bdIc B)WA,ֶ.{ oB#H6^ip8xZOwz<ɱ@.uDa*Rdi]%vKT5 (DxT:PiQrc;^ 莂$Dx}m,-+´pFpKx{4 R7)Q9f"pYPm%(5 91>rex\ l77gr GNtLGkKa՞ UBaդPX*F a!@ Q4/DZ =I#d$mP2A5PKcBmO6=j| p\3|k|-am|uvmbۋRb]v ю11B#3X0an|Y&@ϐ0zv3#,a s! +WM1 bX6خ J+mZۙ#mG:ۙvkMrZ.7?ƧʆW„;?΋E|;wηy;ַLe&;BS(:F[;(}Q`P6uF@ϟᕜx!64y9n+-Ux4jB_r04Dm`Gept$$G# z>oi,/%x!3~Ǐ2d]!D 2;$`DMF%O+>uP9YRw1 S#-0Wmͅ'a _|(d ;z|$?gX,+\]+xS% W#o2hYAekK^t ׵ {M`lĮZ3vԘ}-` gy|*x!.~a[L9Z-^Fūh|  (N AcXZ& Ga|TÜ|IRY&iپen+/t'yۧ c*ED6'/6)kZ'eLL(gHLAC2C 4 ;Vā(L*t?Tz]YF/*OMhJGQ&Y} '~nja(yV^C mTmjI-7(9F.@9r ʑ'G\cq.IQ'"$BI)A)Y)q|K LI$BRE_f~wkQl38ZFMm$@LUƲ,dGTrhbCWULTD;|Rmz5 i1D "VH|+AJf"W&\LG LEʯ[Rʰ%IJ% (4R:Yl)f؋ͯ\2bZuJUnPur\dDQ?s T\B8+>x?U|))xDSlA364slC1B4S KhْMb1DCL`0jeZTi' !SKiPTG +3GCU)`7 x8_^`ia9=ŖӓLNO8=Ͼp~5 %AlFg) O.PC9\~ JĒbAwh~)@J5ʟbyR019/^{9sTl1GF`娙K2(dB03T[bӃ* %8=Q@U`jqDi >aD*M('\>syv2Rf[ aKad &3T-2a$0ZT"aYBkEST..7c0=v֠`cԇO(+$W>W-;<Iyծ}൐} Vc188B# ;M|ŀLu"EW&cPVҀҴ$B&M">tIgOYM.lٺy3| wE\8kI|" wKah"M"0,*,(QdCE[+i`ha1)$LB(0R\s 媖iL.'Y2lt>tX+(#kihm` 7: 0>:(B@"W3TL"%bp &MP:Q"^RThf16G))Jp_Igo7VG{B*WB{H)jɞ {PpIG E~XR z1 #8XgPծg \P LD{ӷ HrkdjPcRvNF"ǎT+Q]@Et;/M6,T R3Fyj<QЃguk_[U7H{Q.JV M7+Qv%j߰@훖';o]a2Ҙ3KV(1yS4 Ҏ77h1b'@ш6%4*O=ta`G箽jog)Fs;ĶLi4mU#ôjfV jk(tjI3KZEQ BrE Cm j}X󃺆'[=mp{+z!ncs{VE+Io;Sc ibE'10AM F 8AT0b"&'XA=4!O 1~H*&B =dvx V:mrۛv*m"[ݒh&(- `hu\r~*?bB%~hDZv|dhГBZۓAkyDBOSeC gQ<_jh.JhG{=$^ ֊ǴV7Y5,k`[kZ]whuʁ^)ګBk5#.B{"W'B{E" B{!B{=!WB{ګ%^ֺGaqLkMZ+ZŰ*VaeTf?}xlaZSMIGC?K{$ M/ွkpj, [ Qhϸ/oa?kyڟV-hZ_rNO=x᧩|0ފgb;_dZCyp[/f{"JK-KFc\]@'p{7򿈾߄uC0.#ugYԡ`A5IV tM\Q0uUgypuX'u\_ {~ ◡|/oe"7g+Y wH]'#uԵUX]?ĩ`j GkE8[Y5]^U % r`>ggLs *ɉ/R}O6`h~8@^?R "0z@ٵ3iV@Ԯ+5".|4@n꾦{2 ߤ?~<%"ӆ sZ1bM;fliː!pƌi(XIsv$ Q 4jE!д ĉG\":?4x:4zQf728BW@_J3F`˨$T)iWK%jQ5E- 7("Z.<TBܒ;-yF[rR#ze-.3mGW|(~I/xC~ƓOW#p]kiD.+'rZZeiBWF;N68}ؤd0IQ"š(?xQ$yܤXZŤh^%(|x"*Gk,W>7E lq-;q>Dga!xa[Y8D놐B: *%rHID#SI G,h%AK$/!I9>d_Ð&5: g3̖~f ;r, @7V0C UG#*LQ=2}tR!:dԇ-LHxHsC>FOqA^SNó=a* v,hAڒi;Z$p8&T]2.4glVMZ14xH7d;JlCG:\OH,< .10ǴѨ"vb[nP[l=J<0!yF`*::WVlQ:"!U }2@0sF5Fhx#OH[UJ0$ۖC[  ( 'ۓ~xd32!>LG`$ai" (!V kG k 3Ő_QLy\~̗pq4SmO16fۦ^^j gAe\TK(oBR7jz zat,T0Q4At "Jq%A,PiSq O*"ɛ#01$s~qڎz-igۖv^S 3 igR7"n7LGX@M%SM]0ꁋs NL5zXD"Ƒ@o%x\L YDH;@{sbn`[U\*ܛKoZ.=\Xr戵&Rh("L',Ux<-A <5"dʄ'M4d)5?sGxOr_܅l)?-% !ؖR[#. IBInN&Q\fYd$.iXXże#G(G#I Ja;b:陃HV^s{- `qE-8`C# 46`D;!䏮 BD/Tif<)K/V0 fnu|&0'p?۱elA.v.@[itiIx#Lb*;Β((dƢ0EF8Z}TUلs2ee%EL-D`|8X:yG<gK6Q"8i ` C`G 5Bxh"֮EZܛwoO۟6kh㢵u^L֍B TsS Ք:v <Ÿc K"@Bգs/ΐdBz\M5pܗ$nSj\-\}[col վ E筀 p1p8]yME,`gJ- Ӏ90} tYGh+ZZjŷ;Nvk[ۮ jHmR@Ԧ=j0F8KY24u$탤 j P RPa) Anya}nnluk [kVZ:*j-YN;fӊaдbrVlҎi@1(d0btԈաE*bw(43Ȉ[b[blJ-˳IL[¹Z֊ua֚.@i>HZ+ %v)*\w$Z#H eڢ([KkpA*v8ZX+۫Z-+m[hk*[ 5^1Z;E{uhLګ\V;d`Cgv QZ&V+dj~X- ra?$Gj}>+ldn-^Z6V ek"[+ZǺhvhVh6 j. 6h y ,O i<폐>2px Qc98g RWOwv~vu%tY|mV'l/ȶl.v@k2_LgP\$ ʹ9vfAY@i<zҁ~Rpa ,Pj0ƣA035\XKOm= =0GaHt|#ʗ.ƴ XPZ(QueE5"8#*AP*k@PB֣t:kՀP ]@~mg6LgQV/d0)Z5fWj٠Mqu6(2A1yͅ&o8C}Sw^?7$@5]{AOH(<$X`8'tEi3 pRzka,, {ֽtm]^w.-*ZS'KYS`)ˠ; tqP%΃)x DyɔM;G+$?vf0õfu pS|k[օsӵrtm\\&׾UopLx9WupWaDF UFP#e#NTF-*&ڭ% -O^Zao֚%Vu{k$Z86mMj<:a )F3z㊶#GtrCZ61w)>KzoN9sZKi*9F[m_6jijrjjFg$^ uCzh܎vr.qA L_^hZ~nQӹI65[^mVWh:JlXij_gggpVh{6geO=szњ43H[:MOҘޡ 5QդL1*|! 34-m5YMg^#=decOfdjOv 4|Xzɢր 5ePG)G)ʪN- G2 ɶ#:kPbc']f6O[X{ nav/b/b:W ]́Xz}Xz5XBב6,l:06*쬯fRkퟮz.{ o-\>{xrmpiMkk \]z \aOrUߖv[h_JmK0ضr)HXp a=cVs}W{!{i~s,o  l+X% ;T([ٕ^vyWm]]xm2+ƪ;n%*R}W,*ߚ-R* *˔CЎ1GƔpۄRqEME[TgPv[{ *o'>r J *DrTDަub[hZ[KSEܞMdC&[noɔRBRPI#!>tKQ?T_UO%f|iE{䦓$wHrsAR6.w zwR{BߊG w$nGftTE31nT'=R;QC~BN:)  e#C}B{ N)TbA%~8k?89VOQ<'X^9i>+/$䑲z_.9kN~ssvhZ>Kghxj|>o017/83ke};稃藏8鉛>~wrk a=5q$&Y2~2;\MDF>ox9pvĉx]bM,7^1.VǸmcuAc"L62$x1va)Neѭ9-r\^˅2ߝlwMT+\t[@ M83sy^.rsI>;#Ow[I:\K’ZTJ~  "#qE q1XxOϼ{K·|gx94^LbOpS6SD1[],R&~o GOVh?)~o_WC╔x&>4'^ė-?UF>?:q>1'>Q ~0?P-pt%O@\(峨Ǭ|7|ʿ|(W#ʞ&B}c+:="f }=~m>_*~ŷ`0>Ɨ߳?z]#l>54UcMC#5=x4O>=!b=}I>yxA|!f 3)F" 5phCC +4hB c"URGK := a=_]::[˧+9gkW4?QG?[i{'.꫚gוŽ\=ǥ>nSz򐦒5~Y嗁>نcbB,ab &baw5viXښak?]v Y[ n/6ʻNj+-~AX拍\iۅv%:IԾv 3t]Ȯn8.ն&ʻkK+-F(+_(NL{%XnoVvڦ 0Yn/p,Q`ɭE'VݔV`*=wwەgVYU%TJTQ!~U_-~oI>w~sejP=f&Su[ޫSzJ})w߅RE ~58jPAxLN5=~}|ȯY,_Yi{>I 6Ӵ&{0݂ pL2 T8K*%nJ%=9PH#Q<)2i7H0R;ZP:7ywHq;CZ Q8 EHCO>;`~أy`{Lc6#`0a1%x ;S9iz;w1l 88.︂eg_ 0!'^M g~9ssYX%pE# :"3O8c_׻Իѻ$|IQMIގ·Q!3珳eV|'z!\g=C'HB$|g_=^<d Icf2+y"FoTxbG}ŗ@0P< 3j'˼__#[dI#}.߳ _ʓ#>x>GXUoAG%ɷip'/ɛxvP gAy('䋔< u(zJ/b;p}ٗ}{2Wwx&<^O=>vR?C6ާ?U&~^{Xz R5/'su,ޓ/X<֊RK)‰'2?%J>W=ӥzP {̵{Idʗ}5nV )<çyx0_Gq?DGßC?zL)Yoi%1~QO__ѿ㌾GB0JCob>\雕_>+ a ZHRO_h0%W_̿s47|E9?98q^왊&Ο8&,+^rސ ~/E>/dH^2y$'&}>r2ɡ&,:mh GgM-5$FIE6/b}b`A_M4zaHtF[0H;2&-Ȩ6[lk OC-F(u"<>ڞO@x^wPi~0-I)\F! -|Z0jB #zj1«+\E`H[ v=`=}=^5>]I|Ro_%A^>'AU=w_o ~\y uVR}V ۈol=S{< :į:ίC}:?z*xWɻ<]D6uטz^7}-gἼV{ l.vCAvlOw^^_>_Nǭx8 \+q}nk Sq}]lԺ7~#l>nKlNm9ڎs;)mw7C7 /x"{!} o68 7[(N3mI8{c ]x[[rɍn6y~MB<یgS'}A[fvv6UmP).v [ykr˭ͶF7Z^jw_픦74, ?-<-ïx5;[{Mm4hkCAc,. nQ4-fTJ/&eN_wH!}z gut:#4R"JWx"_ #A8<ʹYrF(CE{}nI#C'dznA6 R hV>f E%+&㟞g܃ KxBqŰ!g:ꙧ.opllo}bUt?4x$%޼/d x1p;El9!/'.~oalcmsLa?Ib"Li/Yw͋XY<5bo5/Ҽg*Gc>(_]~.?ˏ⤈AP_C_=u;5g|+ʟU U[M(剜OɯO}X_@zOKߐ ߔK1,T!b{yE"G׆א N@> ϳm2zѫ@zOsj_JZ/ wCd<:)gh3K['\PiQ<^Ÿ~ك='g>&gzDof/!4J|YK|_+f%[$D|PzFPOz^i)'/S~X AwQT_/G5/xMÏi-էR&UH>̓zK/Wd_t&OZ!+DW`gM ?3bCsV@t0-h% G"Qy]O)jy8H.,ˢ`A]26Y M=h IaIM 5TMs3M2t:*L0汼I {G_t{D?{C7t|7$}oXudǥOT:dc$^" IEH6M*$ԊRIN]'Iu[ddW{EV*'Ȭ zưW<{D7{B||'^{H"NS,Fmf1&SXĢըWk.bz\)v赘[`S%Z)Ql#ı{Ͼp>BoZWPz^BV8M!P QuBU ,:‡a]GHaff]am l{@ ߃=#='|`oyk@ {plP)VC;/m?Vc[- Sw={+|<ǣ|C}<wydfol*{d3ưۍ{n]xO9=.˛++>nn..4|uR9.C;CʝBֆa6;IδNnC궫n4:-EwIr~>!>\Mύ\cs?^Ƶ\ȹ}EDޖ!O攳;2nĸ΋;5zK'r3gk6ɿpowp  y8./[7&Y.]B4.M ny{M߃mNfl~vxZFi_M,mDMHwCna^M{A֢5mmBoմAL1mB C; 32kq$}(6 Lï5ʼc}KMoRw2qlU198Xc1'ָlaEear*ʓ0|17Kzs/eTK*+TP,"yIDmչ(PK>a§Js2 3Կ6\a$)'?uXE !IH0$9$^tz'R-RD0" qxsGf3 ND⣡d|L兰|?*0:{M8sVY+=g|{1s-1>VȄ o"}d,6T>:=)q~qcq<~ CqW7H=ɜ/&Cdţ,ZN_1+ #|f&WH2<)"X>]ˢs.[fŜxG3l`>ڂ}௳Gy={OX20 y9.3ό4xͫt=9` q>܁㼶-V{#L 6ma7Y\I>/^q\Εg\8=plzz?i`;W0[e ~<s;^]Qa뼹)?w; UDCD; GA{/0`0nZ~3"r:|9eט<%w-8DSِY;z$d/w} Ƈ $px1|>!o ||72@eJ&W|GCއ1០|Aly)[ǖ5ᖯg[~+O]%+T凨|?3E>`zU?]=CsP}y;G$%I*TJ^.ɗ%-$Oe䙌Eח0{pjƧLr| AwEϧ/xp|ӍGE6?a} O%>s {U?}=}(~S<)Oe`'X|)d,ŷx1V<'҇pUa3}ڋҽs&o>TÏ#\><o 7g:>@bh%D0CoxԪ7ѰMθK·||̷$}K)%-zK6t8BA zImd ZD1d02ч(i IeH,XKC24VRϰD oy^Kx9;^O9MzMhPBPQC }14P@Jh_VWHX yn<ʶ{Hw,FoF(䥤z+ʩrj0 3j*SȤzE2:23kCL0 k˯.peGcT$C}#/b'F~'~bOIƭQ}z!!2$"ɭ_HvAkQI+%^;0rlH"N<; ,ȴNz\{^p  ?0 Ma7bC E`D "",}#Y-L1lc`,LF.\\omŶ.SVq7%mH7=!  f \}/XWp̺eV:e ˬ[ ͬF Ѭ4 aaۜnGhaxsҝVXwYwG $7P_)u}8Yoͺk׬YYYm^;50)^ [0=@fܛoV |o_o @/F<ߌ;z\g]7YI{BfgUg7gEWgwwhޖ|z7]wv|_l//o3/*F?oxXκvtֽJGg WgewgՁgUg73~Op+\0;ijcœ=sWqOuoVݎǃsd'gݹ<<+tyV1tz&t|Vt}VtVt IGs !7mr9\Io]|w|8җS9O~|Cc]??7?oV2o'oVoVoVo!8/x߅ smʭ._Z|HBmPÙG<릩Zwj5Z̚5ڡZ5ƚ#ϰ8j?I~F9`ZyiˡayXf;nDL,ã>[o[n8Bge-ZӘ5Z9Z=>,ek(CdKeH|c׉}&zY\H3+u BىOj>~ɯy/BX V*/v S|i/;>K. vUzi[ҰU*|8ˆ*_(>œx3"V|W/VV櫥Y<6%,;AS 1~x L>˃HS@jLu({@I$6ãNtpeSDD@&-8nIhJ-Jrܓx+b=/^<2Cck=7ƴ8ҪyLCV`'&+-塱<7H|#ƻ?x Lc187.rs?03,d "_ndȑoO[gW,Ge g63lLg SMɻ e9Ge*;Crc.!a>}̢3[f|&8mZs4߹{y,&Le4#Y?f7XCL0 ,:sKpLos0 ;}gw̻w`.`8#X &rxi\.b ^}ΨgOŠ{_{B zx780oxb`*o;8c'y%|A?aww ޏ=}CHs kO6#2Yx)Ibþ>ݾ>՝O"D3H?X-o_4C3 t=8x<oi2 ~ɂ7  xӈ/I|}_E G }x; | I?#WM+@̀#&B'Z>i~4_\? SIz(Rτ덜}5?F u<b~"Oy b1y0屼<WFZ>(_X+OE> >޳@|_KX'W<O߄P^ʷA'䣚?ZCJGɏ/BZ/C[} '14=~}<}yA4S ׀|R?~ !8ːzY"K}?壒~ba| 1F1f<Ii< i3l 3C9iD3A[07",޴G#c@Q_jcK"-t&~Vs''~΁(.E1% Qh8G8 W(~:4҉L0^٣^xR O|O\cU^ IE(l@<$ƒ<0١Nvx:DmA/S_qHڐ`jmJ}y 5eNY;:J=B FTj"S8éբ<:4?ӭLn3үJn*=;~31+-)SW&Բ.|fTk$[zצ`k)6{d}y'[iOuԹk%WGF>C>$d?Qu')ì[ij҂L V0aV%bdVeT̳;1ӖT;m@̶0~0`pWrʺ{(^!1+O17x"xźhuDuϮw ϮD]H]H]uHYSHY7HYC%߶QRnBɹ"鞋-n2H z;o!ǿgOlOD hm'r]D"]7mWۮqۮeۮNݮ<߮,묙v?2}Șw|-7"W3%^ho_o UG|_;H(w¸ qW x@9qHwuqhwpg-=ԳnY%N<[0pC_3$\á?\*/C'Touh#GЎ'A< z<0!V@+'xx n.HQ>_ymx(|;w]>ddzƏ_?~~T?B+~98Z__|[QwPy|#ߧ^y< 䩢'C$FՄU^UToњhs\b- $~Bq{c3-ǯ ' @ jLبU Xʗ54RG^>0W1>b.rxor[(NR*V-y2%v|ꛕJN`mLOa">*,FĪV&:1gU+THC*NNJnQ51T䩌|֒<؛תDFԈ.%P1)TdŠ# %9o>O&2Lwg$gFxxt\g Xd!7.\${XfMɗG)#^忱\72c:IgB!<оُlg$9d/7O.rwσG4x(9*x 3E?\=Tމ| @ @V Otx4+*^qW ^~cT+{"w>/_4ͣAJм 4cy3_w̼Nb)/˯Gq&>/@~ۀ|ba*?Bo_> +Oj%hU_Q)4勤J7!z%H\߁7^߷POx2;F#O'92|$߉ChG_y'#dD>HzWg W& _ŋqށotXy~bGϦ;|3⎠v chDw jGL;>qO2* _Q'. _C櫚W@zW ,(\#a4GAa0#OJCXx/iO{EM.>r1 K@>5O'KSߴğx#!ʺ "jD@ bJ@6JL=ե%,qb Dn$.Ft4k#`_A} 1<~懎~(]||gj]5e],+fVMV:V(VN]s‘aݷl em0l\ЦҐCmxA9=9C|g,R®jẢ;j:5M+Ұkұ2jUjY6c)ۃm땺-Wza5ҷHjt# u=wgf>cjxy3<$L4D3<Ѡ󌇍4 j<.<ٮ>ݮoV8+܇t}}yO<Իo%N6?/R);)=<%N^tJt O =B.zb`9ŬvbY'1󬋘{bk] u9eF?i#>2K G)_(gxqqg!|< @r$:w=$$xR%x@%@~M~Ap3IĻ"dGog,Fxk_gx61g|vF~0asѯGC# h5 hhEtD:*x\ci1Bn("A ^q/x%l_a}@ mAB1'`BQCīdu1:8_"t!R.h9!Mv>a ~`g{VPgDR`P PŸ>+}<*2t:^+ !VBJ_w_ ̼lpΡA7ߧ? 'Apyxgyg}!P@^)ciPz Xy}"fZ7<s?н0/&{FU]e^56{ŽO+|{퍾W=ԭRO[ۑxu&&cW ;͞v<:yH>vJ; VG;RveحNdtgyI.Cq%>N׽x<sk\0y6.L 4}qHS.qӸgܩ3.52Nժa:7s-Fk--ƿ9ogݘ#}^ [hO6ў4l=UHyXCaz ᚺus̓rÍk!6\ZNb0SƗ6<ݖCk zYk2 W,eW$e;<\a(a]%a5%+a$;1&#T81B،,KK]?bFɳUy71(-:X̃u>X7 aW +^)4VXStDS^+Ɯ\3䢱㖍 pܧ㹬aOUI.zYQʓ}y5?wyNn֭A'pc0V1b5!V4QecU8&AcT;>q蔏5~,)Ũ! F nNLkrR֚2,^jfO4ާCpL<&c*K?>dJBDIFtd II.MR'1 e#)UyhE]SP|E _Z Qh!#J%'b!'r )*Xy˝>Zc y¬SkǬnv`! ~H}B#xh2, y9rb0X3e7sl_S4 ֠'i{Ylaf\f(im08z3#n}cp\vgMg+T!m% r`0px ,d1X[<'ylA9xי;)Yw#h%=Ox;؂g-_| 瘃g,`0EX#Lz;3'N3ك `*0Iuq 163>߽wO;5Yw(-aM8OXw3Z`LgSGQbspn,Ʉ'*yHh/Wf^Lo!)Dt<u:;!iFxK:/(¿aߔaB< ~5͓w+%򕀾ׂ_  Ƃ7bM+xI o xjOB :/Ws}3C?&D}bW~J7y#:N/[q<x|wt~n͓4?22?{)-JepwI|ψ\.?˗JGƸѤ Ue $h|J) T=dpy)&G0w l{L쑙Ee&Ik Ƈ $gnh"&)]TR1K:aC*JL eKL/t!wemPdr^|1ktFI%ixbF *bhҥ 0UJ07:6(I  2f2s [6QˊU٫&tR8х*1Q6IDH%)2J& +H11*ò@^Gj>҉ 0MNT"b'e0pTHj =N|1Pft%'|Q4`M] |AA$]cL}  EL-0df9J-T8p!K&a8 )-C"5.p3$,)긁E  ?.Cۖ5:sB`? ވIfF1)|1UP^pUK(^T$@5Bdp %ebD/ C@WxcJ`zGhJVXX4@`PLDՐhbz1|Z,1`ƒH(%5:B6 +hqQ8wlPF x_ R@CC`=$aچTj$IuBLB R^pAc i"HDHf LG fDzI2#E<ȇ72hB4$ J/ IQ2[t|Qs"H)q9$cBV}HiDvX6HvHbBCVТH5'0# /(R0avfVA ZyX " !6Hj b=`yC|NXPE˒.fv!D H/p^;h`V+B5Y ;da\6I`H c8l>)1d J\Ye 5;F`3j5;xQ)9EUS|æ(́tfey*,Y(h(@g@(UN["t%F s,$Y`՝ Ž( ̝ OB=b̦f(ddC,]b;EcAc:HE(IФI)Wnsd 3@4a lD(lLcZ۲TtQY,!02z0`#oL yD+a6Db#]%@5s6+ӌPh愸B1k/V=Y6"[4$ /l4CƎj bMޜRE "Yl@J3DN×8xyYN㘍3lUej@;"8d01E(oJܨ-]8 EC4B2HV?d)0gelU]ejEc?'+ d|8 Ohb1q=8Y-懪\Z`Ch 23Pel_cLmAhC> 9 ( 1 W̨h `af mzlHb(h͢A#lܡ VuK E6' ?Ka-8eb򡮦vyn(喾! !f-VG&1c媤Ay; l2v0sq 6tI ?7vS\r"-oux-;V8cM:&8Ag'>9;| Q "VY‡N. =qaoX;Jԛc(fOPO!* qJ3^U %&$r~[%L1Npp*+10%hIY#e! *8,`zBCH>!Di 8V`la!^#ž7Kr 8PĖ+ۣe{|hRdAUh1 )TK l6`E |M]r(CTe}c0PH3ЃM ްZ@ Y #a A.`N Jn[֨l1Y*FQ %cF58>py@`}`M! R0ATٕveʔ֒ jU,VNR0.㓍@ QH98"b A. LYRc:DQ:T+%jY!e %bzMFWL`:p)sWejFMJj<4`FbPRj%ƕQ-9`2'9: 6 ̑\ , &@ hP@8 ` ntE +VLI%I!":pؘ 'HaC*jgfedc#"a!`_ߞ^ڙYؘX0/.-, (P822**"" ^֭)'pN uH!p̑qXPH8 (PܴaFM4g̔!3FL0_tEK,WT2EJ(O4aDI$G!2DH ?|CG7lԠ1CF /\`BE 'L 1BDF78x( " gP1#&G&"^ļ1Gy;lw:|ȐxPߧC>ɑO= _XV>)aipx}Akz ?) Oe㌯a9^? "NޭϜ>-9__[!i /7~-ѲL^oXCΗ|1з}/ßن ˜~żL^—SVF*#5s=~#A>,pǓzlǎMփy}؋wރ{|ߡ->?,Iuo:z.߂zz/2Ih~H܏{ι??{{%?{ _\CuXI_+L=kmVo[%O*^t^hy*^?k=_W1}[Q} XdޯX&aBM>r^~)} pUX=UDOeSH/RN?TUoV_uVq_b_@l Wʮz:Gz/zO#' S3UW)QF[>)}}bz)zR)|g\_~ -`=VKY y"& 4%Ӵթ)KuG=;O_z*Rc=L=+RԻMoM=;I}#)~r:h &( ϒtpiK>-MJC')mT4QDHG!!O::! H QHIi$HMISdJZJeIxTG/uPs}|R+64"ZϨ FFUD"f*JHi2b##U=#q}R'}#a=C $AE m)t~JLH/!bIi%!.zZj"{MQUȞS#7b|"wOL.SxW3$ɣt"|=L4tXZ=P[D 5SH=TPMSuPP>CV[담. >73L#7 5O3Q-5SMuUWꩯvJcMᬋY ] A\/CaOdDkO$D{?dACN>5vMTwjϭ~+7l-\[u^7u` 6`maÞG}>GW}zz:z[cɻ6뽿-Lv_U KF-4>iW{UD{raY}ؘxـmNm^kn ?pS:qf5EX3am{6.m[>nZoNz~Asst#0Pvn<?;p pOq:q?a*&\;?qSLgşr;Ncm,8]ɞ7'CeŧLr[ml3{fi>ZNog1}? f,'y7ra3|# ܁`ExV`3{bpOف,2`cl"^Yo],=.>%QyH9O{mK0R~&(~?A!f#敌yg~sy?2{0rdzHOzB [`(K/F)e埰DǠGDzAAhf^|_x?x/<7\a3)e˹mB?@ ?\U%>=]YoC ncW6Fg|H}5P4DkVBvA/wM? h 'b_a(H񭆁|_]Mێ>MϢ“}ٟ=$/'s ߫ (~W3'KzP> ʞ޹/{&vK2Bދ=C}}[~Wϗ [H.z];l@Gq^+~ .P}O_2z Du]IZQe[Y]c]t쩜=ixi+~))} =X"Bz곴^~>z;`[;>{ؓ=W''z!/}j=W@zr* _ [)Z_G!z,\߃It}h?v_u=Zu}%GںnP?XE\}Bz*j)*z+*z|T֏b$b}֯a}kaYS*|t%4U@UGL!#5ikGEO]=S}US=K>+t2A>՛V}eV}u7˨|*h0U4@QH@)韠jꡬ> 른^*^ ,_xz-O/Y@YuD( hz:h'rRڧ&MP$NUT៴JIUusYS.~4'ЍLD2]dtdKHs-%m4JJL)5==JQ_~ꙮ)뛺;S;}35~K .HAA:E2ZB$󐒾_HMcQpZLF:AA'*R/q3=3=/ ~R#EFPdtEF)23(iP##&4j EPE V$uVTET?UDtQ2º(둴듾>)^^i J_KQ,i@M{r4Ƞ j4 ÀQ?Y3lĵQPCC^ym5#?{ eHe~Qw,^5PՒ֫ XYjcsuX{Me a36} uwA6cdq}ʻNk-lv{k.[j>;鳏F{jAl/t(?xn` v-O;';6nNl^[kn; 3}a69sS>x@>ϓ@{?;gw!5vkCvn}avn{nκ~o>Yo@=np+91Lwu+~wx睷z}wc8Ղ;=x҃MxЄ_7\!?}xo)M7o8>߄mx܇ىx׍cԐ7 9Ґϒ%1p.#>d_8qgL&d Kys*S~edz73gV{b ,'DG 8rJx_bǕ18b1!Vs*\C?07`58WN%,4;H~}t6f`+G0+qVAwy_N뼸 ?awwO8;[sf7R/4 *X/_4=<3?#͗D7s L?(JS"J}-r@ɋaA(_)HY^ 3V@1<_iMXb|3~IgqCsᎏ`>^#x!oćSp>]8ʇSE+j&~?x &~H|9N1X6=OXJ'w[OGs|<_ ^s I_L7[9M˟\yK:7 A>u|M#|/ZgjuStGO$?m/ޮ۬~ʞmM|;@?1qOYJdD4=x=ѻ%ӧQ֛Y3Oc4O{GUGsL7;W{O'{ {k/г}Zҟ5}ԏUx12/ex_V@=QuX|u~ꞩH{ܮjCfeI?aQoׇڋɽX[P|W[Hۛp{ ny/܅Gs/PnؾhxAjFdG/ 꽨 뽸 +߲S}r*)ўО ^ξȬg2ƈ=X{1=oMoi=Ww}w}Ws %X ,/->U1QMKUu~> @_G_~k鵞>K걬> 볶^~K^uGoM_ųWi^?[ :KhF*h-ZJ꯮BWz, eYmUXUo'[a>IȲ!g,.k趬ɲ: h(,>RZ׫**z+z*ȗ}|&g8U4IER$7@J )iHM"=u#ԁHU!Y}gHXQZUv::k#(X+3}S7;7='qxcA-(FR0:՚Fn4ZHkߊF+:k.",E$6HCecd#}+/R/'Q~j6?Sa@Y:֚@];y=vޏ~V/|̦9m%~ bS{O$OZ|$?2{M} -z؜8مsvf\]apo# 5As?DEvoGtGoE^Ż{u.βB>hjKm~m&[n֚n0D5 >Pq vn2~pcXn^nv;ooo7Zp^Uy{>Gïw߭滛7.ާyߐ}r6g.^#S/H3^t>?VX.(_`< _q6jԓpѩBȚwLwy=;]Ţ>:Kt3xk a=6%U#Yl_ w K[0?<xĩcbWx 0[t6F;aXk O䥅l}>".9N1kXǧscȅ8 ?rIޛu?m)]e kk.CfwfZ'8,ǃ?{"sd22^6AO`r~|wn0uڄN;#Kާy?f``-S0c9XKCXt:oN–0wV||{ {-BӰM|"&|Ҝؘ {?`a}FI޳l 4C`n^͓SGOĆ']N ȶEpo(bVB+way,od,ңgzΤǮWDCOx+:O>>ǟy $|g1J? ">}`{kW{{/aݐgq{~-RJ2[AOSyp̞;K|/qOYJtD3Wy@C{{9|߄ħ@,y`OGO@c^ַ}ٻ߿?~"&_t ?);3z9cz7_j]6jhv~ ߻?/0y_DWS=އ}K=S#ٽO =_@z6Wz4727#3G{?+|M{ ➌#{s:s8{s6es5Vs4JrwgmGiLeU?c^?es_d/ l/Ͱܾ>-}V6|Fjmh hHodO/Fa[C{ /fao:_cr79{hC> wh>MtVgD?tfAfH_ɖ^-L{iWك=X܃{I>vN9bF쟈}$z&r,ñƈ3mh0ĐCI,i$1˜_QU\Y\`5]g^t]UYoW'tm|4/Q| Utы_G}!Mb^Ksy1mSI]OuE 5[UUᵶ^^~~ ?+R_,7@*ZE2JQK@-vz, j¢ j XVUB_m}` >+>KVߪ i(Yb +WrA~j UWkUU k'.g*W |_" r ;I>~Jھ*Fi$2EMB4Aͥ)j#MShCբMdi>阼n[l`Kag-M4vRHJg)ݣLjPnRu/S/QtL'^ /#$Y^F5 $C m=Ե%׊HDi7(-Fl#Ԇ:Z[m#(\ؚtޞ)  ߉T_ku-qͥk#E_(0Gƶ,*{"҆#Z{m20NBq# ]4TQ=+R3 >3QR'yx$/ 4?d;C6=?h}6Oۃ>y{z㙛 Ww/]>{o!7G|$O$G2#o㽭lS0_nn#~3 oֻ[kiSBOFodFoV|P:Lfbb3w{Kw}ހۂ=؅mxևOxӉ#xьC~`~>Ј%~~췔1+|C'lq%QStG[.47 'eʼnx?yK8Wwc.wmo~6b{5VtIch}6UI.f+w\-_8ěk=CGÂww.7mgl^}S?La =O7Y@}}t:St 85w'E76(v[g/RX]cq> c{Id)V1Y\;2~;yx d5+䧕4WQ?,e؃mK9Aޱ}\b oXȤ#[ȗx 2Rֻtcm. \uFkY?N:|%Kpd%KyUv1Ee w/{f,Ok{ mZ9 N:'/7A{$\ìcV!o^5;9Sn_0,8 rpv{zgyGyͻ070wK`g9X0M'aKxs;Wnxoe\SF@V漕9cίI9'Ӝ'[K?/03TBdQm<6dͯp;.P$8k2=?#ڽkG8oRbŏ^֋zI,(+  [}7R=b {wP? OEoh rqx,IoN' ??󷴾d;N=_}_tz`OG??+wyš?<>;@'"*$t%'?p&(j$? '?OHoy=E9$#U3?/_,* }~EχyKotWob=%o5w>cW(_{ ~_3_V=Cz8m`fl[5>[{{ܽ9^Dދ}d]޳Xo}Uu<~6:+1ԻU=[֫y}اZ۫ͽޫ~䣉qqA S侇ܟ{ӹ~8ޜps? &'4QrD/}$gP׷w{zOh\ofeg,.eyx:m֭Y 7S݀tэ?܌^s;Z̍[nHW%ԖtRElL=}ԣ]֓=ؓ=ۓ >Ym~{F Z -!iCH+z5hϴ!ӐLK/M hK14RgO/E}t&՗]=Y֋u}X؇}ڋ]x1| CzŎ3f4cF{3 3Ȑ@t(ҒdL97ticRM%FuXNbW'U(, [_-=Q z1_KJÀ%L`LsԔ`Q#MUܗl_&+ ^ZGymQ@\`[du]ku+k.LZQw`i1[N/zV#&ݪrj-Mk3*MXa#a=EWHr{߲{."- _Ake^Umq&s55q\]]¦WYg^mxյxW`YY8*l1Ⱦ*l%*Z)>?+!5כRv Pj(f: /֞zb# ==K=UWU_ _USyx)o6y-k4M^?izGS4DvgّLg2ǤvF[r;lm%~R)n"%Kw߄Py/SOeTOS5pHeoٛ4vIcA; R=^$A؞Dr;m@$(Ga#Ɏ:o)>2~j<)P5Z1(ZMPQ7 J$7%݊Du ݭ7PnB{ &7WWA? DxP>EdIKϔL~+x"#ȝgn(RG"G6 wr@{8Ϝ ?p;qi7>^CuRK4F[4h>P>}x"/䁮.†X6ȇ8q+~:7>CNx~O7rWhc.\onuO{4E3ϓK1{v#IƔr _.:k9w>}wrc.=ӵFԙV kpiʟ8tO/s-GW] :m]bM5Nq,'ay]#zk8.5?zG1%Ď8qb[q4.^j;c 8h!Qڀ>Ż_ɼb'/~qEsjΚPi;=ރyoerOYVv2w`S/g\͘Yr770 Mhcl/F;V3dޱXO' Ιcsn|E:_؄޵a}I#a`(ycv1&a\A7aw:wwh Ia1xggֽ Ey&D'-Fȟy#T) ! 灃ƋňU4C~s-^QmvZ/ Wep3^l_xX[c13 @@ 0  "AEB#l CF29c3H#`ק)OS+NXiX™DE4IDE4Iz=.D JT(]C]k"%Wٮ. vC%:bpBI9!`}YkX~˛.jJo2vՁuO/D{7REcBW88 <\$k!D ߻Ob *V`q be(n4%nSPyև7a\nZnŁəS(cT?.?34LEl1[Fx!̰y&Z `aC/@ÁIE#8Ї 27v~OaT}"8W9>P9` `jWZ S娯(a@[>Rx g ?|eTaaLVm81y'Dr~l?|Iu!p"$@LW6Ďnj(CѰ]VU3`V{!@|0L/gRW!T6Ay fU0[@@SHċ-SSH@bG// :@">T);n>A0 /v̗lz"}ݚZ`sģ3j3 AY!^턏.LB!%c /-?D9Ca& '(L5_̇M^2P9Dز41<ͿvwJ0nbXhqۇ!{r8f[0 :AubrLKbD"nBG-^ 0ghQgu[Qf.ЌѸ ڢ~l0GDP`MK `9@<Hc.j8Ar7WciYY"j1,'!_N ^ lՃ 3MO C0XrԾޟ|NfJ .PVy53jI;d?/.loViBC&u ?]u pC#FS%%?ԃa?J9d[|G9hr=dk$`n@#1Zj@B(Ww<Ѣ8S+>^t0bAxR?<݀/=w(4D¶ǽJ:PN0BBu:h !u< <qGо ،פS/2{8 p!?AHL @w} ޜ_{_x58hf| k!Ph@5{5|VO_M}ߌ?o_¾NM | <1X^rAr|3AՄPA R0p(مpw }~ F/w=2}~aWߥҷ91`#ʬf2\_J$4!zzdgQe }A!A `14 ht 0m3+!Qaùs1qܝ]~W~J|O~yZp4U14Q{)@6tn^ n (9 l{15Hjo?hDw : }Wkt5Ϡ#u|9@=%=:=oIq/3FOnɱ+c^!_%Z|!B?*B.M7fw.YB{~}_Co=E??O uΑvYۥ8XtyYO 2y@&I68A$Y Gmq@f:o"E'<3??MԅmѬ# W-IWv92'vR;G8;N|F9]p#ߜ wq-7?_נ-=SmJ=C$9-lln!wul>n!wpxqh6FK"搓ٹEof5ĶFlthh>wq9*1t%8ct,Ѓ3]3tL,.бYLL\H1 geV=7olr')q6NS00 n].3Jp*]  r%@_/iaws&~H(# WŢ͢•6Yp{xA"kkMq-a=l; RuN-,n08xV͢tg,9VUEVsIU"ZYWD+RP)P)YAY,J̽@,na;xV%"h?O?9'''N(+jeAPPPV@Md],e~=nogf9 4߬?)7))9r~  Z;9 845ħ;xdQbPmeږi[tؤ]m}j`RRns6gi@ڦmTpBFI( 6jBP` K>EQۧ$j$dmr- ڶInk 6n}xmNWt5Kii $B} >=b҄X1glmӒe"L;BUBHkk4ٶDot>lrC6:`rms<5+myόQ@;$Vhsv"-*R:-Tfک+Z]7,rY˪b֢5h~^@)|>̉2c&)$Cc/إ䓹P>pz'oxvNjmz|mX֒] l֤hY+>_%KQJY);Udd,*kETb>Pn 1nWPz-ފ-Yچ)[uڹYo-goӠ~_/Yf(-v-%K;ÅBW sǎ׬q,Q޶ -mގZk*Z6r 7@`,$djhs9Ry$sQ|* jpg.6u=Yкt5erE9+O*9M{ &qh!G 1*w8j@Fg*Gm@ ilf<"@n8b}:u_Wj HmdtgKRk(`=8 bD#]"=#׶Ľ&..Nn 8"hnk[EVBP0 &CGk(l+@(%xPaRA Ԙsy*P2C|͂WZlۻ` -t|S قgc˞*S.o] 4, b1LB1Ȉ0gT)-@`ȸRٱ4A!(_ddX/$x{LI'{84$oP=!No<[}Bw NDF)mT.pbD]Mm`bCgC!'Y)XɲN0q,*D't6H!tCD"f%NHd!!-$07]gL9%lMD!>)(%ņ*0ް0*"dg˒PY˖wG u~u؃8oq,ѡXxŽDE t6AH%!9t09̽O uBFE)x$5lP ='64EtVIO,RPZ쓅=Ocwv41݉0ocD #`DC #1wȐT10 MR}9Fi(!7#UB"4cBB'?^TX3X sPaR>CG؃FZT|2poZX6r:)9HI,*$#|Q ND-@ O_QBl:dOH*#J֖L{:%CR(`nJ݇Dj$|pN7)8=H͜\crR L%&6d*ЄSQ{.V.cpq 1/F8ij;E(lkՓ4[xDF6h ؉LDkEzܽ=U0TQNA'(R)٤42%J]8H"uT L6$a8S1L0<S܆pLD|8؋NjsI$J(%LtQRD"bTCFh1zB U,MOaabj4FG>xaOǕ'aȊ  XpSV؍JZV['VĉAFP$AԄ%$xJJ<i3E#2D]!#%vugH*0CTlpQƂYt@p5*G+S=)ZFj֥[P| B*bhFTYEs1ɦF1kx0lC(>A@Ka.ILE E-?a0̇<PaV(X\‰"X+RZn9a-' 81 lp$Y,g82G"06z+zO&rQ&QPTL;lZ5jZNך֤ɽ:nb[~ͥ &q8%rG#IE& RHH L%}'8~NE&p HHl4(OmrZUC:Us" 5s[n+-e@&(Q!b(RqH@O4J8Cgʪ0?>eC0N :=\+ ɸN$Lr{:<͹"Zmk<[6OH3 &A ak|`Y+zP·]BvKrWK5p1\nS"&)8 2c/2\OkD;: ^kwWzn; p]g nU[-'yeDo7۫|Ko5mf21nZ; P;]ŷ9Zu{ Kg;AkRZnP[-V+jn[avŊbWI*R[)yto< oB x[.+^Saٰrav0bm X"#V[jR1)"t+n3İۨxKz4ȷ6o}ou:mN8[j**n:۪ ඪ2T]vJJ9[ F6C1O)I^[$üAm|SƷ<Vǫnu58O-Yݢ5 ]$Uqܥm'CxvjGmvkGltMc9X,օ)BcdVRr!n#2F+VTc֌oфv-hm~vkv/gj Mh5ZV|FO_Mh_*د ,X, ɆabQ4%{*]3=k\5&epnJuFqG~A vЧ*\mj3Gs P<.')jL̴XL+ %p8PAm ֮TUp*M39)28pUFϩMRA7PpB (ZP8N[x0AJ_ 6kd6FF)l@ - GqZ@e"@/K?,EA8ĀcHg h }AG+ !xȃk![0y(XșрFF &xKC[ L cܩ@lT2 ('(b@q+> ,1Uy!͂ѡ Nha1C++[ V:N5s}#9,k۱NkF $pDpgU$H% X  !pd3Nr(aQ8 b7 a`ՀCɗ<__<fo4 "^9K2ne0|~DC" oּ8*+ C^J`?HyG wy_,5 . M SpxFAyE )~:.RJ"!i?+zX2 |%r!-rÞTren\8 OXT' y yp Y݇DCD69B(A4G- DrdrǑHHS<&XBI$/gdҸE*&~B΋&0HHj83f2>Q Ha!k-\r0*HBJ~$jaǡf=z4V T&(M#|IL52YfD);b(K*1d&f ,ā0QtFx IE73$!Gi Nj0vjع䦏4l\R y#tqBT=ᆘY` \xQ`pBĖ/9W^U` H\c/jV $֊d'Tt3r -.a + ߄`4!tURPsSKgN&06|θz?K)o3œIVex q KmE""#A #pбuJ^Tu^0*t LN$tFg3:`CK>&gx y\ 5xbĒBR(~U,U5 #FH#hzRA~vecs^5*uL#dW  A87QXB0ylǦJT*`&b҈ M%/8(Rh*_h)Lc UlDC#lnᒱ:V,6'6Չr /,}RE2%,y &H%M"P|r L+ZMx~%cIBH\@ 0 IF!2VbuT7G^< W ˷Fv $(D2qlF(kTzaB)$G-@ 0`L;؁"fYh @.xCTbT3E9Pq7o#8SUpxBHH2% bH,c(a,ˆ&cDeu#8%X@ "Hᩞ(D;=C#^A6wT GcbH6n<ݨhD! JPr9Dn6}.RyP:W59S`m+Xb@;7!}8&ȭ DnOWhPc9=TssO"~[ߒ ca WppXKչq H rCp P!lFym61Ts F[X|o?[-~5o=0ۮ!Cp9+;zQ$HvKƸ@ce;W aPx*|`'- y~Q^쭇3{ vY[.'}eo9[,[*R"p qp{> p9U}B{axXn=pJ mx۵r [aV fE[H Z)ap.wpppBnOX|Nͱ4bN,: u.TPbj\ A;U@|{s~6˧m|C~Z;ZBjr6w֢og- %;4drl:J]#`ԹZFU, #۰[M*۶ڻyZk2ZL/FWIel(%)zx_N, uÚD,&%[\ {djIˆoFr;gamȖk{8lF^VZ#fp x3:^ˑReCv KҲdWOȚYسFbצHMCe8 ;evNt|Jo+xDbĵ#a09!F2m f#ړE+ 8q; 9˔S|5<8p `P;<խooe^xAX"_dfe:妁)*>p0gE1X4 2+8MLQjdEQR-LCU@y*c0 ;s8Q9D$OhٲBIMK@a]"骲AV6W!{ץ{0bO[‰QMPfbt'ΜqʉQT$'HzJJC5Utuj0.Ia؀#ǐHKP9 T  (+ ]+:YIPȡ({eJ%ѽBI.%%2=(q ) NOO`jj)%P%B 60KWwDI d(u? ӕDM۔̤d;[ETȱ 2ڄbM"b~@i HSP3"-#TQX-!i2Daji "V(rpHY]/iBtKr' 5 ( I <]B\ /ꄺWνBE @ D]'S(2L !"B0M$Mm:%2Ɩc }g rK,PJC"n|:؝ 66G<؜ͽFA.$ 1*|s(5Nq*fG *:Jt DF(N#` Dubh,,E@ Q^~A"3,ycw8XU9Q%lސa3G:>5Q:3AJ\!Ȉ!IBup2s*$,@#ȩzok6aU]~)2׍ɂ:T)6'jts0\5qMI%K,pr`YdHHTBeh'G,|iC3˥ O/cKI ib 1  v76Ìj;U'Vh x6H&ET Pъ1XD@je٥s$d,gd\љF5A$<1 c!φ Qq4$XåbwNWGHIn5|D $PqA# |Y$ɅLgF4H\$%/.@LTԂ:OS(SCȢ͆)`{Jj-ĊB' (4{O ߧ9WfQ!#j8 wh 7Zk D&xUA+ 49 ,l^DcG%S)Ha@-A pEP.#TZrᄴ# _9jdK&Ng d+kJ ʎcG.4bE18h:@)L a'N S8A4a c P"IMD ¹iED%[T.~qG8G_H:Щv  B5',nLIzAZT tGX4! a\ CDq C<("'tP"I L() '0dCϐ60NhɰЗ dj)*X^PsG-Q A t!fG );$[QG&RQj*TTHfLl :Fh% *Āb 4x@' ת&{dN2צƦ dh`&24.aP ;h|ac 8J8ݑ:bBhP^_b'pWu)CF怢f& 79WI6GC>k^8w+BQ!`1h PIZtU&j%!GJ A%([dB%npzZ@Rڗ @` Ó7:lΔ́j9N".H%1T U%i%)L]Q5zcE.Z@M!t?,5yaI'c`%%5-X 28;Ս͉Z9P#괹TTTLj .&b8AMu!Ą(ZB*S>>E rɉIc*D. oJXtX(pbQٓ6@_HG T %Ai S JUS8P+bx!-\)3Ps abPRJ0?gDqԑE:D$j8 PmQB8 䄠EgXX tO p/Z(0" 12^L>FwQ)(cf/<K$rY lIgHR >\:v d8:>ti4L@jcuO/xpqœG%?V C$PQ (* +#VYs;We#8Zq '_B1X$SJA,&`zfR-'I 4Xb0csF eGzZf* fp<X+bB!eU9J;y-ZOkϚ~*~͍c~e_n /6,JuŢJ%\ hNdlLrJr[zFdGAY3:NzEV67Sd+)T6D ȞqilmWf%pUGUsS[p2^9-."$QnaNM-s ȱ5M^0em˺pbJ6.,ʅX0KuJlׁs*Oq7.&NcZ2ז i qw8rN)dN tp^m޽%Y6 vc\l_Z`%$C2n5V4lQĖPaXF7Bq1Jcu >R L%?0' Yd21t͡qLGg^D Bגݹ:dN^FW-DXC?@`qCj 5RHbE&BIH&'P^!ve$%4-% IӅdwz`ڀ*Li”+JpYQɊCVБ 0^* 'F2C)L!R X0僥4+n@8ٟEgÎݑj:HiĦpĉ @1(v ŏ/'PrBG:vxac 1V[X5 RGF?w<12@E(j<%8,lԤ)[@l3:R{uP+dp#MUbJ1E#%djF :bP"1ޠ©d3Mx",\Q!i FdH6 p;S+vgj8X } fT34zXChAHD0NaB SHGɩC de4I#b^栘Ķ$Q >Y4JuwDM@ J#.a<C NB "HIb0m‚)(NB@^0@бHqL4D tMPbCHeCVkdXݩ'=Slj|`t1'_8ҩ&4~FqcD[PCĊ31@`d |0槤uITj+If@v,hX+c9Ok:&6`@ 2Ps(7dRDʌ!Z^bƈ1'VMѐ@A@( 9iPHɃE!"9!ˮH nAV)D%`cZlXuVpytK$X2i@eLZ1@e\*̈b)&2-~lQqS#MQLS Calw \#d3 g9(lm,ݹܜ'q &B IEEY^$E#9+j\4,`e(zp>@-J1DQl4eyfPjX q: cTg\mnfiE+-UtDGtOWJ6Qِ8 l5HD E8CO(L [$5\<#KCsft*蒍دKXR{%)!6<,7د'{:NbN rǚ$q͘7][ y˖4pHl;gdyOHk ֑j1svĕhS_@ǚYV2ŢUnUUkS&n&MCa8ܹ<'[R[¤U,BGjI8z|Jjd۾}c&(,Qh EOTIjeX|UPջ?ly+%Tll !i6b+S6֦'k]W]] @ q?U qA< t0i"L |~R.PZ耪!4'w09 Lu it h:惫8(${m *6Z kPG1;)a$aaoH#d0W<))k`<"#P$p{X`6 X/2cB dsbu) 4QdJ F0Q +:R\aQ^N  IEEo/" C %`6<du|T. 5D&y`DH%F(,rD$J*XCEOFgbEgUYhJza8fa!+u~c p P6Dc`x<ΔbL(>42&e+A,$yG"4Y%2TJTu"#5SʎQJ,E0DA<$oxJ ʌPTE(ۓUdwn쎆{e:9Ƨ18:`ѩdI$@rĐ=P}G0DH$i%X("x<5lC6GfW5Y-!SUcwNPFdK U"d)4LqCU/raC!>Tt9BIH*\'HF=4hё CBP!%dyf4XjVO{Y=52f/-N@aabIHvXQDF@VШ=¤43MI(a#2p}!BD١HgX7֤!@j"dRS|I+II1`aH;t!dF)$+B}H1,*r /L]u\Jz™% 3?\"`46kM|9Y*N6Y#=6٠Zb]$)dFDv D%FhJ) 'Y⣇(5HuT& 1|h$oܑuR Gc;Vᐺ@!e* &Ȕ1?lS9D !T0$nA/2 kDV3WsCiX!/'XnShI ,(ʎ$99H4?X7p2(*pVP" >UY|@Y*cn&yc}b۱Hd5vȫh.X?\AKeY|ˈESm e". \ Çjy12ORe,5ٽ#'dt|{n*vZ&mb}$Sɰpޖڦp2&@e5`f'"FQp D Xa0G?kbRWq6XHΝ\7Ldx{+֖F9Z&<NrdAf,#\4d/gل `e6>D 1a [ؘ$G$TAN^#spPqB `@[,:> slaL ֤&kE±X(+}R "p EedBT"5Kx`y9"MB-I(-l,!rzmt3aH N02( ^+ԕ6٫ * @` `PESx &KN9*j'$0)7@ BBtP$ q#hRЗT!P 'cdl4i{h ǒ{Ǎ4:^QJ *_*ⳁ7 -ח5ty`i>8W!pȲ:TIV' ^`,$!32GI4҃ #e(CLftRMSdx.*&~daL4E !l)cEdu2뒋%Rc0UX G&r1qtg<f8PkTl y?h,OUiZFƤPe%j,X`q%ɊU~ʐb '.bH!%t,1HF2_. Y7,ZE"KC^?ÌJQKpA%˩+VP̸*ARsHɡ‡,$YCTQrȘL&k6#qu]D(B֜b{IdGvȫpFQL(Pu 妑 %PD?'Ud3 f?؍Z!g!f4$  *Ph~yRL%g Rx0HMqR Pz) uH*Y.;%^c- .cubsRl4P~p9ȘJ,LHvi * afHOz@ %`#*^hYIP' ZCHd,ݑdBآ36+5Wv @EI0G@)!V. DG<D9HH*,V;4!cG8Rs@֌pr Y)tc>ؠ(< &P)G4>LL9\2PI1pBB)xDHej63Tır<֬rY)8tfc\Bݜ'"G33 p `_(!xq2P.'PUi "@ p@aR .yظI[vcxT\eBFb+Ub{N7Į` 4. T ŗ N`UM29=A8w ,NP{.rƒ%X.;5Wc`"ERh8)I LPr@be#`~d3!#`m#i .P H<}d3N&5k4C`ۃUbsL,mېb6Va.U]uUYU.]Q)i> tLd䵘8 E3Aox|M 9dغ2S]<UbHwKt(yG͕w{m:?TK4D#gkhֹdq?nԒO+-@*.m!Li =gʼ! cpތ%{y+&ڝb6}.d\NI.fvt{նSg{ '9Jm @Y&XAG mu`X " 7',"hx$! +Xi%ߦQ/6Ǖi2[!J\4 XZ Ch@cCO$r`qs"/OC$3NFMDN9L\a 3V!Ȇ4hH<` dx,Uq0ESg QUHX$L+1 D53?(!)9 Dh!ww9pp+˒hx8D@rl"{]ұ:!d k m  e8j,H@SUPTJI/-Hف DzIb7B3:lxMR + .0(\=Y >c! bSpA YD (n0%/0fupfAHj P04>,%#csll/_0(D)3,FY䄗%$`%3kVDGx&FqЂ.z{ `VVqLĶƃ0(yQE0([HZr%ˑ+Xx`2#%U-QNpeBh>4ĠQG H**p3{}$`fG Ge\}AY,!S%csh,,G0TYrɅL.?hn ("b JjQ,/prqR @HBEYlѩam IfXvh؞4c`ṅJ/Mx~b$ ##8bz%RJ4idDC@,HC.hpG)D%S@Z= 4*Xn2%0O|ė>NETPhBGP(EthZ¢KG3L"qO ֌࡮)-{!m+$c`Qb(!ȍ`vHeZV{ȨrH'E]dg+-,!)JE4 M0pܲR(kბ A% [8!ƉT1GdC /5j*;~<)䈑Fl3_\4c`a!qeeޮ,ә-@dplՋqTBEu'bH ^pȱ VT(G#BU慦4[l eeު,- A[6j/T1BTb#D/:VlᢈK!H-~K n^RӲUpSlY!sBʖ3X1J0ONS/=RRc+.^D#%v1b/:,@[@Lb8`wX .6%Y0 +Ec{Zl+WFu|"(E$9M&ZpJc˓01qLU &%x+qܓmBCFa>Vk\xE*XDt:KK0!! *YxjB#ŗ'-Z9JC=XaO>R"쇼)kdЌXꊱ=W,66:dJO#[p&"3J(#lAB S1Tni"CT0  @S N5B6e?E٫%FdvPsbɗ$Ql2b%3ʖ)XD ȧ*>DXbL%(\&8rf (TCg?|@Rb8$P@T @Iٶ#b'5yXbcdLd#c, 岀 R2d3F<$:F!XQD/Rh R&$zXaǶ% ~DNBؤK>X*6B CIv\KlLt`F` ᄀ T{Z2Lxb'XK˶c?Id0 lPc4:L*CL%6X:@c1dBɦ+xRC9H71UU"Q[`(:c.s,cc|,ۍM*{DNՁZխuN]{ׁuX\Ӛ\,@W{[H5vil; F%.NRxydr f2g;6Ӝqy XL`f,p (!a(,p-ceG-04jIa%*Y4r e ƴf<.c2!H< "i].[RZ7Ī,בʱ9O:*aDs eQ.ŔJhWJ|nIe &Ɛ3=lL$Z<%@hAsz` vVqiJH,uY+c@IIIN3AttL (`Rr)QE(O>()HJ4ST<4с%֌GSZ=d:Ya-DH("GF 30QD jXHD%J#\eEgB` `C7 raVxRG P ecspl'8HaG@㇉32Ỷ#5.!E#H:Hف˪5Dh@wjJ( 9/ VeƔPa@5dHr #NJhpQEaRRȐ)K$G2,( 8@j@ D, ,9.6eƒPUcsnl(-Ra"E$1VA6hyCGUAI2$OؼEeD!Ƞ11͠`'flZM;DvVՁ``Q i hpa& aV0D'TJ\Q"bT԰Adɐ7(&{_VkFA٤1#KA|F&(b $DVy0O*!D,W<SZh+nw`کv& {Hj/ڏ6oP/S\px28 Rx@X pf=('O{,'Κ>;N kC}uaWZڑ vC8%Xx\U6a'S8pqixO||O|p;;Ͼ%KOI{SCEm m g7 [0 p YQޕycwwww:ЮhpaдkpZἯp\q}3]ޫ;yG.ǻ)K=% ޳x*]hRک.}'?[3wاo7ZT~"|E;0 t=!^CY$Jh⼕8?do‡F~/W{`',  Ûc w,?l~q~VAM ZsЊkS34A4F Ι'#` kA+tO0'B}x6^FjI%rQz/J#&i+`..0Mi/N#~Kq^0|%`]4Ej ]V2sdQ/Q#/dH7V0ɠM4 9%<> EEH+(UVQQQ8\E([E-JaP:k)(_+A[I7 K> A|!ux0>_-Q~pY z6dD@$ -$DxnIA"^9EE®E@XY㊘V$E$ DF"@Oca ɏ2+hixm lxIK %8tZ)f;9t s`p逳!to 9_g"ƯGB®+r:VDA10-Ȍ(+"-ІL[u`t֣"nG 9x5{1}s\ I0yq/E8UƣLW)jF[BhOӐN8MSMI4%'jI=L!0ftj9!u@(| SxRtR漥P-"xSƇR@~j姆~j<đ5;ZQwoLpc"3#.AdbHt4&$_"@ARC9V%*!9b~5Oo]UR+&pwHivKo80s2IӮ)ӌμ |H#<4=-H~ ) &.ԉ:洉v9ΓD|#bċ>i'Egf3oKZK>cJrI<,X= kazDIxԉiXDHԨdكIyy=Դ~yz~)'Aߩ6^ȃ&yN)_xA 6R0i%/ld ODE E%HHBL%>',')!+'-+{,1w.9s0?a"ILԇ2S|DcZy0HeIhxa 4BuH0 c!C'L,/6:&:Ba:JZ|R䡛aq,}*S\D+i<@tlrd"q2IDb呦⥅E͡EΠBFQ;QGӫ kHե"[l)kdKrGr~Ёh #Ri !>>@ecBH(%O<)`Z.(L-<4,4r< :DH'OT'nQU2Nj1㏇;y4(׍`G+hKX$`eP],a:dȹ Tz)(Z)8J9HBH`>9j.a3JRb"oZ^}\ Ei~H뷩}M$mB=fɞgS=P|VF$8gZwJDgB00Pϟ A9m4c9*!aO_0\4x,hmh.uNӘMRHܕ.+ މ#Ҽ"%ںD֕8ZJ'Н2] wtFR9ȥ*SM1H t.,1Y7CҮPC;}y>r\)=)eYG/ϳHxZ8USQp*Rx[۪JZ^$1iM 7E.lX@񆃈ě!P8@p>~"( ;GVd*_m9iN>sx~o?۠]!j2Vm8i4@Hq/h:(*D3Ђ Hd"ШO$=kKxٜEkbs˲Ħ s`hlЖ2B[ch D*3fd!%cI-1ٓU2(>dRzFQ,Ci%gьU0]2`/PG!2x(myFغ£= Hς!Y0%? &?|EI5ŇW(^Wzxa᥅_^vR|})FGL&k$!+k`&CG0 }B9d.aVݲ³[Xxhi@ˎYbvdѱUFI!Yhrxd S#+M5ԬY\A^f5`mԂ*Uv[_lNa>$A_&gl՘"UfrV G4W BEJk)I UTRXYHQ`=!eT*k)Sl}Umz3kkڗ:EB~ĵ\ɀ>pݧqMX0]`” V&LX0u`SVLl*F2`erclB*Eiv0 kڰ#aGvY`ݣ q2 Ơ (a1E%|I+AXw`QhkjZIhFkEȌ ~57|X'%\#Kd>js^@e4 `h vh# ^/p} ۇ.E|>$a@u(z(4P4~I@RPZ/ %tLw@iv:i#*:\?f}BT"I9 4脁Y1PWDY/0 38hZҌՆe+u\ 09Øp#V/c=K6HY}7og\씫7Uv2a XwhWplV`l#+hTY{)u$4>2>R|OǻP'0F: pI<QA/J^(SB N%dR-k:-SJ94sRVL41`JE =}KJG& -@C.UR,KU+U/ħ8)d|țB-3Fд|OaP=BքQ] ՛2'C/ê( T M# .G &A.!: +3Q;弊Z9j?]}8Uț.TOyD vSi7v3CM6 ʲWd3pJ栆P9D;2=/H~B"-NZ jԉiCFJj"q:(@Ic|i/5Ogyp"bDBfH"tꤼ\Lr4B% I'NT3չzg:m?_^rOD`Ȧu|ZGH 8EBW_0BVaXA2Q s 3 2HQrd%Ϝe5,X6^ܱOTQ>3WGް3i-vi+VRM"UWa+ ZV AĜa53TItQIsq$# $E& SIM¸aͲq%*哿]^3"| K{ɴN{ jZQ@iEpJ !'BO)Z2a84jXyX% "EC,a=)a2"$aqOl9xK;ڴO+@Ъ ARU1upTj 0MR: `LIIѢዡAD@{6xw1zh4F&[`Q?~yG3HF|!<*_+]ڿR+zW*_{SXN|)VpB[e JF6 s3 h d)jy1W2 >۽ԤKzL'/5w)4p݊ NVܵ<Kz6@p e(1HHO^`7A'VS"Բ{XP"|_Gk"i\\غLq^<ŀ0r4x2"wÉ\s;VPB4Zӎ;#CxI Eёb5㲖ߘ8OyN}mo-Áp8-R n;&htGӕ6 LKڄ,i#r$nHTߔcR$ f\I9*?sWxفݙÝ1-pu0_Nx}'mׄ &hՆҌ(eDI ڒ.hM=A2hT\Y!JM2d%(t P&H61;nfr_Yѽynuv}(mY-s2eO Ȥ}0VcY,b"&c^1Fؘ deP!Q, :!9`C"-;0 7FpEd`Zl Ђu %h/1A| ~xI'W>:<&@lip6Fٛv im*ۉ٭3=@n٭zvKM ZhY vdMS$Y@FV:Y 'T -r\Py`=[n5?H v#~k*l:[ՀV=T踡  r T9Y>ȉ2BN)69BD1EBNrZLbR79TpU{5kކiUkk/xؕ%>aP,9u8'6J +7W&N*\8pZi2iN{ u^arSk5Zk {=Jkڡ Y$uF. E* VX%8ZhazhPhCh6iH{%E&^:VAKZ7LSW6TW`Ma(Z-ZCk-3-Ck2&Ck5BW D{AWD{]AD{%+%^֪wV61u+jXTWYʺ?볫|֚ZYk>k+>k$?k?kV?{=W?{ZZVVzhZkVrаYOωaþԾ~~Ͼ^y~>;d _ 3Ebf}}XֺꜴ=-B;4} EcL=[?TH<87W?V>4ur3 =sX As P(z NH: _?`z g#@k^'\./QCnBAF@6Dh 6&:`1pQmׯ1{ ߇I|-?E/ICh< ,]&+ `jMV&8YsHeMAԠ`X B ^~{j@VXԀ'u^KK%(~m$9CճPASV:kXPQs$ 4rLd]7l]tY `>`%Oh@\fl3g[ĽׂU( +1C^|ȿBX6P-\B!p *KЕ.t4ω.|BTvYR#%ez֥m.ocuC\j{+/Ex>?7<'b˒ICR&8 K2JX'bIF@(3 # eBQ zs/]7)wW3)zVRwR}WFH|ϣ2 +OJihfNq=bt3,CB<rtcH!lHqK$g4YHȝР=#}mxBGqG<vNîM- *Xa%"kK $-tjOé= /0a2A TBqh8cqH#\ I49a76n|tCA7J1cH^Uʗh~2 kBojMU<Le]wYJNmqb *(DbLq$1pEdQ J>l.:u^Z`8*D0REwY yMބ5 xG 4,iԒZ-H BڷńOi $PR&j,Ey Ԉ:yЄ M"TO"vT^=0ԪF+Z ǫ"QT˃yK^ jHW ZU`]`>|n5̚!E( )O,GT>=b2!L ,,3$wZڃfD VT'q ȟh%/)O hXNF\M,6;HyA&b(\A4jl:rPyeIIyS*I1TiPt߈!:R>k9a%\X7x4=%|$oI<\ IW{ ұT:vR52r%%UK˫L*QLN QM ;M>Mمh36:ÚEc'?"-M~ʋvN3oy3^a)-vj'+9 IHIf ';L*i#2C I+T:+GDUZpr,y PSۧV.QG Yk o3,BnV7]0bv@Ø fFέ`4b:"HB$S$%EJI-,aӼsS>L edrC ?xŰ1jARPѳC.,\IEz rHJbR(0h6>M>21BH*QGy#ΒOf&6\i.[5<&<)A^oDE$1Wp I>("k !-&Y@O7(Y0fņ){0ɰGjFULRp##B%B}O|1nn5Qqs!xD ^P}ih8 գahLW;Wrj w>p"OyDJqK$i"@+>v]t X= 0C.2C 9`׻=(n/]Ѵu>Lm d9"S]HA;\ =D՘kԂ(L$\tQP!_=?3:nia \(Ll4xC;ΣDnqGɅC´oI- ڠDieӒ*NBڮ4p4m[%p_Fuʕ! H.M9syX(rq_ 3F}T Ej[kSvm ҪQIZ*6 hZ*@4%.ha3I9 Hh"T5D ɶr@(_EЖ/ |]+ !_Ɨ?-@9g=amxP!sW׵~P[+=ھ'vg"٭ ~v. aK <@+/Z"TV >F2' =1J <\Sٵ:VksZ6V.Rilo۟֊k9Yxa'vD"ǂ*7<TSO*8m?Ku~~9z ~ |4.of}$+ 6gkڠvh k5i& l/ԋ3 <<fπh [)L^YcM`{Zjd3Zb+Eӭk4*giqZ0G!isd`#0kL|+ߤѱ"]pte麀 HW-&$]Qhd=-ɺYBՑ' Q㍐7!X8E p݃A+ohdxD&ȰdQ :\AG R:T0 U΋ rL/nZhA-^\Uk ~E9 4~̷BNTCp2j8Aq* O~pz|`AȮzY 8Wj[Rm w[>߂TFI?2K/^I+i|! i1`H`ƀ!-vc1cJǘ q9 8'72tW32wVdwҘ{nX.ox <^ǑAS@!{(dP+ 2( ERŤ*gN.1VaY J\Q:+wA}W|a a!naA< DX G }&o;|'KаDԤQjc$J$iV`Y5AVI $oL$MR焩o R^?#@&CyL $9q~=IO2qJL<%ʟ T8:8%0<޶CCuW'zQA_$ $ iVRMkϟXHI3~GqeHy\׌jamXxY˄v^Ġ# ;@duɐ:o)@+R614Xt2x/ǤL7=f=D~ᇉ~x.^1e4yWބ-+3ذVokU sV.=(o0ưZv0 10& ,4 0G[0{dUPHaC$QL :VңF)ef \dIB3G9yͰ90?Y)eG({A W$Fߑ ,XcBE"ЉO(PU "v[(5 "!Ir"MqRw F- 7D Z="W+bsgd;$[dhYc!wPrIGj)Խ,Fqy4 8)Q̐_9r#n1fT31Nd CyQ:]kvN2lbUœ, ` w劋+(rZq1E 0`P+ULS(09!04]:i\ X7~p7 ܉=m6 aiڸ,O %j߸d8͋mah+#5͌+mdh&"nizVR@B$9&(B/rPv*pܓ"(n_Z[2IVFi,UCBOldg;rhBm^lkb[kFZh>.+bk6]m6BA.pµBU6 ڠ \kdt-1ZN<vNlulJeVNXך|lx ΏX1Xk`!?=Z'Tf-uXWbXZֺkUóS+0= LmORcZ.RSEj}H "=9'ҩɬڝū۰:qZSƵW/jujmmZ6Q˃A,dK(D9^(g]ulUu{tܤ^mnmjTsW zݣBc0wb>?X7M;+CRpΒ7P;:`QB(HX@x5 5 k [4 xV%TF$'/>A\`-6Ï|Rp#. <0n3= @\4 i`f ƀAGs{ |-%>JOyS耴 x8rÀ# 2:Q8 ˆP!@Pe!|Hl`M'8!uO1pB5Fx~/+C( 1-P04dOB(s@&]f-)$8C4tAh|i/gL@G 8󳘾 =|,zjU37Xή6rv)ZЮhQm* [69Ir[R䲇4^ 9/=i^s@ZPs/KQ}&3qTaV.qzL:ФÔ&n]Cqh"ڤn 'xVO*ah"TE#)AmT(yȪd^|eECVHgx F^GSѼG]? ȉQ 2'F$HAT2IDHaգ GD^KbYIW)'&6rM\dh6< 1_[{bolm`/x!3+bcƍ>`%pzՁcqC$*`:L"bH)OW#,V:c#7>HqPy]>E?_.`ض$K-hѧf_g9Z pa"6eDnQΨj9ex3%;Em=P`dNAr@9,Ḅy ^rM(e*u- A+(א0LӁ"E*# UHQ_+.,Ylf=B|1"C` Ӧ<(l吡ӵ#xYhkX^>ř/E91xSH=}epJ0}`F@i[RŶ$ks,㬳 .NZ^"l!SNpDv 64(خ,PS¨G#5_x!v" I>)whaZ"Q(4-9Luf!!}` l3Γuta]-c7-6@d氅ږiMc Aa#bƥѰpjPUW઀XiJTN(2fodAԋ8R<`"AN"L "{tǷ=l"psJ'2Invi`Z큧] *ڨUa4 0(3iDmIJZOJ< YZ L4,hs FpK(A}u;fzaoWzkpVbp[&J{ ҆`i:`rcSf"0 fnd͒.1$ J9BIqG)ֈZTۡGt[:I[t{0שjx[ZȶтhF@ZNs$ HcIK"Iۣ%b{DDlzQ [Z@!P4[ óS[69[m{jkZBuCغ9hP^o, x)bExآ}dbu6^l֪ArڭJakF! vȁ`khPCKvZ![+kJh4zm ̉NP.f5Kl\6pXݰA*lؠ6l6( +k`BMVȆ anlؠxfvX:I9b al\ ]kdد0+nܭupּݱ­m vD֭-jZih5LT,֯ }y⯱1eɩ?9H(GnP ]-誸EW5mn/XӶ:O֯`Z)+Z-vZC ΂xB|n?;X~E`x$0dx~셝Sbt8Irr WCNNԭmhu\Va?, nZ6mm ^o qKRG +HMRAdLACd+XágO ];*xKL'.7cױ7fW(YPh; NP|,ߌSf| /Ize-B2@.#0"p5aUԃ .B"*ؐgbB )P#YOi;q}v `Ϊ<}(1YfV*1b&UҲ&5hU9֨MXæt5rOk K*$`?!} @`@ʀȕ uΌ/( y QǗ_ku-+tY5yiK[_޸X|:S! ^f=)0Qb"a$z#SV2L̈>df0'>ˋ!NǖqQ'rJ(qd D+3@[Y)peHȍB'B=S2rbXhE1G=,IsZ2Ҷq!Zu1Qկ[\Da?| 1q%GDB)rB:6Աi)Rȝ#zg.8bB$&0f1.*lb&P#6)? ɪb&+2r#J>Ȕa&xv`gG[; ءe^}oA,3Ws1>MTAMM=lEe86OȒ\%Cg1t2 `̣sJ\0Cn~8[Oӵ,Ca.zh.G\%4:GC**j' e3,GHBN$aSBˎa Cb(I`%ӹD wl7SVEfx{sinӼ4yZ&M1DQRuΆ*Kl(2eجQڙC,󳇇HQ!YGK8i;Qš.<_-X5 `"hYJbD%Ig@i쀍ADTZ:˽qGk]ۮ=la ma;$ě@'kAaKO ?*kU-Â&p.d2(jhÈD4Yk 2| IMz:ITjŹ(̵.IHqIku[µW:l N3I ^[ƘA+Sv>pGLiTeP(Az@7Iϙ囝`W kmj\Z EX'Z>2]Wk΄C3]1!+w5[^_=ࡆ^\-(pQ@ 4hzhFi0;G ru JոV(ĵ2IojX-W{W;@kr3ǀWrj@n3rt"wAW9 i%:0/p"\YI\ UFLs#c /D֩4zg)3ooMpw7C^!oRr{Rۧuj8ڶ2l 7llB`#A57R@- Zd¶%mq-}MpT"n<(n-ڭ]-lm^Z g4BӦh%@- P)5 PŁZl@'/hdmmӑeL8v*X[$lkdDp'>ouCf:6^W-UB–Yڲ  eT2q@LR]CKYvIJ^XRMaH*B-L!Ƶ k[ :_q׬fu  H+iAIZMZMZAlڈb4A*Je6&U$'btDl~ k[ e;bևNmyV'ms:mΖ^̊l푛Ek,Z$)=GW%[ `kl^|X &–Tl$l}B,ꌩmΗtl\bd9=֚c9#:9ۓ?W-njkt-ۤ뇽`M֬ZmRY՚h;~>N{}o1}-.,O%Ixzl-A]Eڑ\%e[ʉ 9k;cVװpZWJ`֧qҚMj,ܛP})}.-> )hu O@Q hFX ?)6Ï\ƽ) <~s\;pA'mrDז]:m֦hY@#p|4M{Y)9>7{eD^QXFgˈn("lED]C%`A+c?yqz񨭰C[Xpgp ?W`(}0o/)D^MqFjS&x91)J&!m˜h v!E cJ$%ipvIBZQlD# 7B]y;a"h0h BIl`Ŭx,7ɐ7}| ȨR* ȪȠKfUJ0A4)e@-*' /pxݑ{. Z`g .}uܷ1@H ^ ? G_~{@~Bh"F!jE*$h-""\B'+ԤPeǵʐ(*%W%Yf9 6F8+F!`5 ROþ6Vo8ڝ搔m'PF _CPCcI!Ol$)Rqe#`W4>/ p3- ǴHBQpHƿ. Z3ntF'\g 5$ s%P *O7#?ʒb&YYf"f | ݃&7nUX ˻]QFUrЂ2SR!NHaBUS O;,Dȁ ц ,r sа)Dp!S/ iq9/ 7|C'8ݞ; 4bjIxDSο{'FܙΉp\6٢# i$j#4 Qx:b2Bf"ka=6x?Džq["nK)܀PXFp&A*7) x%NЍ0ƉEZ 2U^/|lQCݍ&Hj<|b0BBh̐` U"ehqߖܒ[R{) TP hOK@GEc1 Rt->KP4q 2W!Y/d!% =X~$bPH Iun:W;\cרɽ6))pk|+ Dc=@[z mk`-^ԐEo*]lTd3%Xa$ Jy+Xp\`:X{W(ýb+!~xؾokM^Bp'vHs< ȪqaE@~"p, ADӢ*1nAnd9nT^5UJoVpa(ڼ {X u|ԅf@ _dJŮM)wn5$M6؃c/Tb &U ,"; ZS1B+͡k^7V[}a@96[*ȍE\- Nj@.,4s+dy b9۫#uknnܭ `"oBo (/pׇpw:Ά͹+^7ƕ@pm΄}ssU78WUokm-SKVjHf!d-*6R")[~շ:[{nsam1lۛ ۖmSAjVZmL`[)V`j0öm*mIFq[ndmV4n8ȑn-ڭ-OX^ <礐 (3|e+g[i fd&C4? Xpޛ`) ްCF'qcyNlFd&Xe?!t }9`/ F`v <5/X0eӼ7pl| r/y9|b.E/aMv͇q{Ozjx{"pGKMn >N+8||g{{' zBcyF 7<ܼWPA? s,K_|&m(OA+)x ^M1!'?2?KS(I3z$kO$} ~K(|3Ȍ0ḟ(ZeØOD^d\R~(_BI!iu ʏ! O'Dy( \<'5y!&߳K~%_ɏ7y!DdPsԽ.>xz?6 9 ?~sz=?7f?Ca~ _|B]8//Oq<\<aO_0~`ы'z?/>.~EѳXzRbYۯ{UOzI[$~I|҇EOP?P_>_B}#BGzԢ?Û{ԵG{?_ZGO/t>G3<-?>>@ @@?_A-<҃F}(֋z1#G5臞y|&_73M(埠닕:UWsJ(@ eMBQ-DIN%W>ecr}k=gw'~~go&@>賔ş{,1hPF!\$njEX4K+ţ1("tAL[5|#{H{ʿdLKԯd5>+h E0XD_!,:-A`҅J ^MM :%4 PQ'b(P>VOcw8?/_"5{L/m.@Y.Li}MsD0S]qVo'U=^F"guMm3ÎpYi5gosp7ܾ6Sۇj+{[@'&~9az۲~ӖunӖ6omO#<\rj`}x,-l4@LFZHh!BK;G~noH0^<_#qQx<Owۨfa` >L,&ea( E]9V B|71 SCږ {),ns=)~}][w4)y+be/ {dQ1yx5h_~HLmA؎j[m2/PF򐷐@dvn/gX mM>ĖJ;XWQv lzh[ێ&슛Kn:۸e;&\ ?D>棍~{o9ȧA1/\o]o.l CKZswfe!8}Ao {ml_ | k+Íj|.|2GSj2~ǧy)ăC!l{`= Wyjoʪ{*p:*ݨ?*\lS_I-%8ȅ4Ps/?l| _Ű|>@o,R~ cb(QE%\kPA=SN35eNyN:}-%?x1"佺|gp.M'iB<SL.1ι/D>xA )YtyRcI{H t# T߀㹀|%U|Ը$%8zM$a4Q,-HǢ-t.h=D#aCo >%t Չ\|-L9BWQS P\D\!ms @=GtRsNC{NA}u<Bgus85N:B;?~#)(̼# 9#::I҇\bKzkYܸq1{t׸mc}|u6K9Xy.oT ^M{ߜb V+22N$18\Ǔ6I{]?)/es?(=oSqqqe7A!Ȥ$gə츓rSֻrc9m.Ŝ3#'/"F6#Sp˼)7Oqg<[X&=ˠoY.SeÌ8?s C|^+4[Xwwʻ+Ws=x-a2sli&}͠\;`'n n[AX#VKLf'o0`W/op!l]s0* [Yy})|j`.w? c6x_L*nL5Ox޵1xwD|c~sSx*<qk |G{?{N~Ὀh~ WkyR1{co `< ldvY6|JHhd\2"| 37.0 gx >Gly,E_`=_F>M ͯIUxiDL|%W&3DļOw_R~I7q%Wa{!z#q\TyV^Oba\y+x|ڕp嫪O|P߁?K}rB_c~Wl|C~ QOyL#_h|#[y("D䋆А!Gw\ϑ7Èħ2~e 3^υ.j6Ɠ._el/B6_i 1>_} _m|MR‰œI%)F?UK?T}ӧ\S}ޣ@%L<)D"~ey~ $F#.G_(wL,Ȣ'$H-ChSɣ1%NT/rKE24VOGAB @R}$O}[=oB>'sfcxH=lz~ttEk(6"ԍL̰].C ,Ī~:pCY z7K~%[~W RoWP)f A :uO#&Z/pqF Z,P[cr%@yi_H)&3xC dN]]&}X۰*E뾽<̦u_z`k}*/<3ۺx/(Ϙ/0 y=(v\+mg~5W<;.0w9ޡ{<+'?T}C?Wl徎3s^:>>B.lR91f#[0<8Bv^oisx[{ʍ&7y{;|=}?4%+oۘu@X 7@X čnevcڄliﱩ=6ejmwZPjs#-lu'~& oߴX7L-uBXCX a]ZEl!jhHK4M4#6Ds3B}"nsw0 fy+Md.F_x7`&P_}) .,N?njKlV ~v3-`w#Ɨު^{ӫoGzܯCw@ZɺJ|CW\h8@k/ ǻ y&{;mBw.p -Il ΩEYf!8]m0omjCMX[<'K{8}bƧ4#ɼ0 `Ca6WJjO++oz *&|r2̽6ǚ[?ȃAy/.%_MOq|0aN)s"KX|K/FGL"=0%ކtb3ԿTe"YqҎTG~Yc ǰ{kFht$HaD%>A"or(ōC8pzC.Ќǂjc_/bKQ> Ky j>jW+t: =:1>(ғ}XUw\lcPgx2ƍ976c=krBY+N>@W-/ȼW uDo-] 08]k,8-{L?rBȍp$伛 7ϞWzNh3ux >q={I1yC6ry|d'yKFɣ?(g>ɫ \n3w-S ,_gw&LFa'k8Y2,d,X0^Fˣ1gnq6\~p -`7huwyrf03\)hX/.`&Aw!xr f 98n{vSx]g?>;;p!Ld ]o0L™YrV́0*.gOཁ 4f2{kWMH{wc>eh+ A#N =Y~3|#c+x5?5??\>K[)d$ѹ~ O_=;xD΀/+x i_ y3?ϒW GO+"}wh~l|\>Mst._|\Z[^J7a#,Ot|N_ Od'@G߸BśWHm|J˒'],P&cZK~J;)y$O䁒Nopxb߃u çA0DE꫺xq79~of37x)=ǫxGD࿀|Q~ C`x*._A_|s0 x/kx3 `,ޅҷzWY|$Dx:^Sה-3 :?pԜϥ&|4󡉠Lfbh2C{0A4 1(LPZ]:nJo/Z7Egi@ 1D/ ѢM)zt(ҀIӉ*&4PB uCBuԇjh؋?/"A'Lo%k4˥9 ;dZGf)0ń:3BL ajS˩?\RuYuեPW'Yb=dKYnY;W&c6JYQ82h 3R= ^zdՕW'>baǬ&sˑk]K9vF_4cGW*>&gL)q7lb] =IJ."D$bY$nH"]$fM"qkLD;aXYe􁰢Wv$ه|!Cn/m=C6^VzoC}7wP=` N>/x < bݻ2-T/4VC | Mp/47,3ƃ _ D|݉s{7^/1y9,̺hwW]à]YyY[೚v[ pE3#C!LqƧ_"|h:_åoܓw!YϺd}7e} cO>/  ax# SNq8yɭ>X!"uDꫭ>\rHmʫ> Z7lЪBؠ5V3 Q A2(xW^#zAt^yC`6Сs ]qاP2ۄ|_!۵+tPra*@WJף*$'59VZ3ja^xZe}:,sq }jL,\/Ɠ#͸EIrҺr,XɃabawj\): /  #(F UJ(N=xi5'9ŘRK9Fc!c9*s  x)6o0a.҉(n#)ūI*>EZ]ŚH.DqQ f xT#ͨ`EAƲP&"% EٚD塲ԗ x |[b7EW@2C/2c#C2!DcB6NqPAv 3H@D9ۏ|#e)+'ty4ߴ7YJ#Omxp\NʕH>1i,Չlb#ϓ%o2O.\~S+e9k2Gf gGؼ _BއF`$kYMfc(XʪSt+eϵ9%`&||ynF{_#9O}"}*<r_Yebv1Slxt<͘YrV<o# .; %~n 1e.a3OpY& x Ln8OGw0!l7 *c݁~=ov`/S-8d 1ϸ[)<-7Qp;p[ޑއމz,x׀0GpYLNnMn1 ,N\Nn^et V5L};7c{n ޗޓُc_>!>_B-?$5 "H/i>'tx.ho>/`y3l^ OJ6·_|ߔYB|? kvЃQ.R믄x)udD4"44c% <l5kB 5oA ;h3?} oEp}o2H )y6bb˘2WǼWyc^My,0_F]~pc~RP3z%e$}Ox@y3PIO/vRME Q^gFO~<<WbH!d}ſbyA oEC>t?y@"/GCy!53 !_4zАBhScwǡ5_%Wu8dX߆O@x$5 %lycg^GE/~$.^xI/̦T5JO1ӥY6=JSM3ŰiJ+tjRHTD!$ƾKޔM#KK<A;}|eM ` >3|SB>9gՙV7~b;Oyjs\kuvMaNuz ;SA>TE/_=.3[yG'םܰXV* \bYXn`aeEܺӲkHZzg%k& ۫R ck$eD!Ρd{˻|MNߩ3Cy)umuuȣkG/+ G0+cuQDDZɾs41gg5՘vUOl1({io -m|$_TX {c?tA I(v*hfD a]DD[Qm=ĵ^;lmC7Ɲpº #E$9q:y?.f9+Xs`Ye9_r!xvU!vTb Yݬo ]'(!hVSy3Mw6!3G< Jy=09˷}j9(TOMZΎ>>+f_ozG_w}wƗ7;m^F{!~_r>bW\} _#yo:ot.s$NG^\Niݵ*wgM˳靇Ƿ^kw~u';1d8НU~xCo | slݜN us:锩ӁRW.^)]nJIg]78\ms+ZsK1~A!ɧ|K/ߓpir6OG8< tD /5j hmVpo-ǫ"or әފ~=Iy/V/grVhOMΔ?!l94sR# lV5D+k5>8ZfkUAr(͍>Qo'x= |ܘO[hM̉6AN4B+h9Esl!^U)^I-Z9g3 $;r2 V5T6O駩7O|mg %lbNe9-d!cAB易cb,ZbL9Fi>]8yzcύy6:wAzKa&d0> ǻ8$'ݺ|Z 7,:Bd&\>P^Y^{]^aeZOiZE|qZ5<_/~^%bH e`^'Au,.ꊱƏx4#׮|ژ/C6f^M.+.ϫO+.P+-Qk[$2rĉ[ jO+u:-ٴVr1k`c$lX/f^L؈Zw X HX\Z\:2q J}DU*ZݱŇT/qՌj|6/)_i2# E1y+(e彸3,&fqX[<\/o! =l/סhxG.95X g,,-ͯ?cwqz-V뫤}_BF$G@  |~L$II4|Ye<W"Ec>H^x~cAz+P?xgRG_/kñl<WʕrW<(w~X˓aWG]y%+oYnRJ7%W둜>a{ |$$PJS#*y=%ݔ\Ssg$%>- z'IQ"Q?xx !oe _O:?s?KA!t#}-v>^a4Fs8O=F꡴P _wD8c_>MxC"mH tI4 .4fc8QX wа `Lj!Vb] oa,WljP3Dr&p\R1XjF&RL2jTR7h(C5NptF'ꏘ=>CNYͮ6r, ٕÄCN`bF1 gzfqƉg8d;j#ͷ4~s; sCC?C[' L^1dN鼙 2l e؎J1 tXP吠4jR])ӬkVJ*v|2rkQ-5YCy?iϙ1>sSF(_EgC,N f<8q4Gَ4ƵNǶ5(gs[αn5ǻШU#O~1X/k<+m|! 81th#J"GI/!/|gvVgA8|2#DPG :k oz78`h 5x`|/ }w:#:-fgx֯'_~t?i|Ǔ꧷#O/zhZ WC|O~W='9Qq~ xE;GpvUfw:? ݧ㥾_C>a;3ar> l+Igأ#ENEи˸ǫcPN (GiՓa&\]rOʼnX)Aq&~nj|;}P^o k([κ%bO n> )ti[Yӣ͑E䎉6Im9mȶ˫b[uMis-n#r#U[N}y8 4UY5H"{"H6Z Kh9h9hJ4ϫ#0M 6šF\Zii8 M3 ǻ#_벼gS:(̹Y3̝0y*(!Csg OUМd"d#dWIR"Y +!vd)n#S1`,5Fc$3#o x0>s6 TMXNDS4`9/J\Q^INX;R`)`*` X r`2F1W|aA|ؓAӶ|Z?;A\]mJMU"NiN]&rpjJ.RZqj]z*ú%JEF-jOKvZ=nX8 9 DVUji//̏y1!b.uXX\.`JJ *U7/rJR*R8 ȓTAf2ᔑ,'3 O@) ZF O3%sx _m fa] 7(K2P?d$OCDdNFfMI&mZg1eE).Uo)n BIɨx)/5WeT"* y|Bґ18RFMb&FPR$eETQdEWFQ [$%ї!ŒHD濡3 0P{1TP+y𐑛w.N)d '/4EI;[ [c0kop;xxo\1n&|&AWW](h?'{!a.n&a .n݌e~򛑬A3Wq}}{}iho*z) g^a3.TB/CwWcxO@;e˾}$6$.W x4XFǼL4%8ל\N$n~'5!M6H*6yD_O j ԛbKg{)󇐖w'k\..P т߫*>Co 1< i| ћQz1 Yؾ?A|$/dPQXk)|>I+zk~m͇y~joAy2A, %뭄K }(n]^Wkx_~y/żM]uYK9\>[az*RE럄}_G_H|?ŻNxdg6Pt.U $ΫQzsd]Eh(CEZ+ SQfRtQg2IHQ{"vD}8ͯa1X< ĺ@ F HMҧE,b aHiD#4D4FC2 4Ddq=~X/cĿH1>Ǜ'xPʊ (PV&89pd8 ZՆ8qplXnd]uCk[dž({n?Ce>w,P_EGqK y&)V*GV VfXX]`":BˊɂآÂ-k5 X|8[,@h]}P /k\5pJ( 4\i2ƟJ<0~ScI'ϨY8eb7d@#8!#rA7T~Fz6v"tr{WGB+ۣe{xTʑ&lj'1ER_Oy<#g~hNh:눴 !9qpϧg1pg1qP00/jhuS\PlL C%V^yN+H?y%<_b~/= !#2GdoHǠ_?><;:9WFk\5Pr1ʵ 1PQ1wmгs>KsA(A]09TF.rH9 r?+.}L2{@6y.:w >uBsD\HtuV:PU t-6.y$ޙc>7K}JRHK!4nBU4mhi#MΦJ7RƷ÷ +\ N!XSp5Nzq;yN^}y4&ɳj=GO 5i@XD6DO 4Si;i&t.PDQTSqT[έ8mp|4!%'?W<];0xmv֌gH{┑ S.d=ML!CS,*T:#Xpȩ,qu,q=,a,a3ӱYʂEVd% ˯uy1e<:;! TlG X(_.v|%C׌_5rLn\Cr\5t\'xX~X F9#-Jb%BĺUƔ)(VSQ&T/(C).i2f(zjࢨ `+IA9~g ׇ!.p,'R=Xg/a`Ye, /)!x/`_( x[D<}[ S'>oƖy8eM?[)@fC9sfv>3W7CMd&h1EFD᫔KH;R [hdIod,Eof11~XÂ4?\Q:ʅ,ti W橂\=외}7bB$04^aXt%NX꒨E,0 #.orJNSRu\=k$;H'Cx|oe OYS9qvPЃյkWW>>8X7hYpMk5|S>^bd[y7ax1^ǣy)=Bg]h -61x sE];3dx1!+sƇbcVeֻf8lFf5c 3F60|ŧB~DbgO, RH% #)~jᓚGTlRLQU-PەONMe*+w;M5ߧPbSS I3,hG˄vlhҲJˈ(:B" gAi?l>nՊ|%d㳍hO9 NP:IPм==Ѿ+(MLD^%BZ?1~LJM(? e^YK86pG|C w2h\Q :wBz@|>~<;~9w7ND8 9Ԧ6+s7E<@3[NyG0OxFm;2!.)ɺ$0uIL"zDԳ*7:t989sHiHW#WxU|˲ UrS);udW^60|DW{Gn(`Sø䄑cQ ƿD" 2 "}yw s %lVH;HnNR?/g}By F^MXSApu@XyD&H | yBgIyE+Y"ypg2)&R#wE0:P}uy+A+gBGo/ǧŠ⯾WXo=|B0ByB,/ּFPq-4 a9#.rGLB~ȉZ!7H\iP(]L:>u(xRڭkqa]ã7NAvI^Y.wx ̈́p#nо0ۨnE`JZvJẗ́P 7A&R|[kb1NFN`+܎怼5Gd(9%E:wޘKt6>ϪU31&iDdT2*EP @i0|LdTCCqmGNZk\ڐd)iYg-Ǖy~.FY3>kgZP"A2Fhɘ c8|liAYd?v@˂d ɉ,+hvhĊm!,fv嫲.+gL9i<&ucacg?8zT~xKO/"<;:+%9_Lr<\/\X X>dYf`cBS'4ƺә`0Li´%KzБ4fĀ)RQ@=-QJtOMLLJHFEECA?>><:87755fdc >}yťaÕUՔ QPONLK, XR(K߁wZBghre #;{hxF˸; gB]Py*WϨ<W?S$9b$:ok`\#ĻU+Yx<oy\R=ިȁqG :·xzYU>5O@ğAGR_ET?OS-T|j ?Λ NbÛ{Է/_ ??bLO>o yD?G~G#._4t8O_i|oi!"_I$Gs&+/K I;޳?'߳5E7.$Q >‡**1?WƟ11#5Se_}gbOr܋"vzxuy"~*Hgu}F}_lO)of ۿh{P_t{W{{z{O{ p/>r3v~;|aG{wd_|DOxx>_R2~?Ax/B}}|]x; |byXH/8}&^ Mؓ>}>ӇO~H@_gCmPgs ?-Hd}Y??[_+~뽸1p7z=r~Xѓ-}Wz[{;={=};ӗS}X=ρXXiXv^ꛮꉮ^˺[C՛uxoc{8ÿ|̯}߶b8 FHG1S(yR&bR4SOEig*UsXz. ^$_ND|꣱Ӄk~ QPnO?\N_d=&xjikzzUكh{3O|o6g_b/`0WLϸ}K.\vAJ<_)!祤&BGӅǀ᧬j>|Wr{' 3OG#} HVHUAt~Dg#Ce"5 T44*2ii t*~=}-~Yy~YP}0olvNhg,E< )E"鈆N#"Ð2J BH %M$4Q;E 뙺>J*5~勁郭5!G ]DhhBD =T4awAG! H鮟 c,i'z.z'?BTq#0Oja> B;xO$@O[z.s4I{买!y;M8]WNY9jUH֪rס_dImT7@sxtץM.-Kw#7-cK0{jꮯ0Ss8꬙鮋Z}P=3~TOMcԿS=3 _ì$K]OZs ם5Z5]oal?Gm6nSC|oD3|Kx 5*ް9q5;=mqmQa՗fq0u,";f9A_:}utQċ^Xn.Ve4Vj'd?J ~IMO~)F`Wyu!x0N]#7<njplwE6VNZN[h-ef5 \xq?^rwLdu$̙<R}rky. /|D70<&P&a)T\-˦91g^87mr+L{AgH.gVa4OwYK&#p8\:.S08'sw-i9lr,CIgP-$~5?+OB<,eZP˷iy.-凲O% _HG8RoH{w <!/Dȧ y!7ˆՐ 䅄|Na00 oW^S%/aůY&,Fl.x\N +~)>EԧT?!&KtBK VD_:G!HHH'I$/I$~=OI<}/{nOITG8o^$?Re V>Y;] ]_1?77{;?sAC+9Goqw"_JHguƿ#~r)޲_LM|Ч,}M K^ߋ{Ǿ{6X~I~_B:?<8JC$W*eSr~o<}~WOކܗν}ݓO}=Q ٽy B~="/ֽC:7v"xC< g_GIikOtk^kQkEl?@nVzct{COFlJ6} B`_Svx<|__m_뷼럼>닾~xiTq ~O6o7I[[߷oZO*1d7ʲN39]K0<֟y=[s{ |<ϣο8VO7pY^ճЫVee}Cu U:7Q|D3@UCl8z0,C}۳=1>]yK}5$HREH !RrӠ32) @;E%tQAQ֑Jzϲ {1'_|w351ʙ@]eHQ:B< 3vO@9$VTLDC1}aFKE-2<cu{@{kj%lŏx~6 z3&g0lІλ@4ǒJ2* HG3!!}t4ё@IM0ix /@|w߃(?3d,>Ƌ4t IB[ mhT$3N<2z7#ˌ*RCх~*bZBE*LjjR;y}b t_/cO6N|BQmh'EF(:Dқ^$pqDM9a魡0A(MBR# MmAꠡy3({J|/!`<wo`Hi3Bn"t!Q9)4cPAҰ ~S5|Rz&(k?JZAfO(DAOfz0':x43'Տ;V \}6d!gk+XOY= ez Ulzξ }Q+S:`㩄VUZVj(c 5ZGu]ḽN?[j._mm{GJ|ȏ|_u^pk6wx}5+;qMvf;m֚Nǭ4E-<> 9~+}~C㗘mc70X.{f8ĥyڍ=xz-7lŮvkygxoN:p t9?Cot\RO*FR؆3F5rNv#zw}ـ-׃k]Շg8Gq'&އ ! ?䁶yN{|\p /;_;bȽ\ɭØ3џ =Km?V2p lr1^t=r|pޜm|lп]WMMN1,v,#>IyW˓95Wqy蒋X'qߦ}ܨ}bS[W53FCx ? BGaLb)Ȩ'N]tgz-VńoMck1<EfF3h)asbvØ2F2s|c;ǘ8 />‹ FJj?yl){Xg]V:Dyg5D`#GAV򌛬KTʒc,e 7>3;` !Tޱ|Yn-kI c38I,px }?v`9% 'd6ˬQv`!C'| &=?9xrvg#w mml8wxz{ $< s"x+qG 0S} ?7_ K<#``>×/=]Ly<>X{z"#ɯ&$Ւ<BEHLj?#50Ҿݯ0|UxbcƓxoGh<ohόeĈU_+^EOQ(߄䣂3 ]J?駖x<6B}* ;&~?ӣ?ZJ%KGG`>wvo[8:󯝿 =чB({m/bE$ }O/⏐j`t>osP _} ?')ʟ4zKǴ̯LM$N|Moy}?~? J|/|*|9D^q>'/-=Qto3PUݣL?O{ y!h}_}fxC׾! _2#11MPe{>m큸|{E߃6[p?NpEq?$?sCuBwzOg> Q5?Q~M0B?Y=(;á}w@+B hoDiZз=i {>/T/|B9Ğk&GnAL ?YңA=տu{{=?|#z_&xx <cx=Ozy4V:ԁg~ X1 ë=]^]~xލ[a>zS-Z9j錗3[V 8h AGm†`,zzz3kqr_vo'tjWhNдVULMc`jzi%04CO}=Vo{O#}7b<&h6MFi2z >V4҅tהu0,a'$ +!M]D;K\CX7|/S}3Ǜ*-=҅ Hp$1(ӒHQaHmFRMeDuVQUEVc(šhGEYmc)55>勁>/j| (jN+EQ )ʰ" ZՆDU] GÆzSC]C uFB` Sھ3}sx_&wYD!wjF@+BV ІI8l5"}@]txk| GlzV1 u(?*$F+`|֎Bl>[гu<[czNaxtkLu.g+hWAXCeE[봭V[l;vq[ ăU _,_WU?^e<>lC8ڄsͶ![=,t[mwnZo&:p0>q>ӈ:\|7IyO{GӮിv׾q(vt';{m[=wրW-8 gpq+?gD|# 4~_PGYLv;{y7|;w-݄]ڇ؊{x%EkGGxoIy0~ 80.=O|o|pn|C|ؘ{XssNO4Dsky;\ުf_<ƹ8!S>gs.|~r&1M>)nzGX!x9?sOǢxĨz闗q?:*{[|AgXKh!|wcI;;n>)6o]\:?4Ɖߘc{L2C]'j&CdsLe_Y}˞w/Gf|87h;npkc؂`Az DYlbsAyuF݀GO`<7O7ӽl{"uFal"!,W`$_03Xb>݃GaٜONp{Ѣ/ # 1 ^˂g&8?F%ʒ!/d{}#0\̼3K3OfʼR-WR~(J#Cd\=5>l&pN9y0'/䑜Q'9Rs = m-( n#Ǜ:Oǧx.A#!s_q}C¿z^"WܽR &̉?[~gx?TJT(x?G(ңz]"ě{T?O8M?O k%V$HO$A#V1#~4Ón=)ٓ=ߏ2%^7x*#MHNQ>OQ~U>QU~_-LL׌z lZIK@Y >'"~!>o@| GOAϳVƟQ!^'=|[=fk>fc~GyW]߷оw>{2{*{/! /dCOEz??#'q_ `àQ u>a?z³?od|oE.޵W4ھ+۾^^ھ-gy܆眾~ a#^!x˘=#ğxB|[cf׻t2Kf>ߊY#}bDs b:+3\fa@{}z5 64D`pӗM=ֳ=ͽ>_m~߈3w-^g8zx3/U=FBuZg]DuY8h*.VBh+i! xV?^>o:P܋wj [Cl ckU-C*:0:Gq8Zl\ڠt 3a1}4?4<׆z/k{4_{8ş|:ϧ#r)-ƫx^,l(z,`-WGBZ*A+.VU0OSP+IaHJAST\e{~CY>Ù_!|T^*MJ*P%=dJSbRiC)4@!j a- :*J(ºGW={1!_BVƏy1<vi20CE08# Ҩ-2a'㞦Fs:̩j&5PƞJ7=>73V? y4NF)8aKS4ixF%dZqj%.lXǔR:+i몤:CIa,}T{E操~'{b:ȫt^: V[T5%$]]# kT$o"exuwG]yF_sUa"@TD"B?彔c){y?x5zNhk S"qu‚=-x I6!чĎ#qcCe 5b+!3$PCBpX$w2| ouYU;~䱚C! }b>$(A N46(Pْ@fvm>k-㶇xK;v-w->c|:\<U䧤=!ޞb vBLF&:hQ[E"m[^^h;{m;=w|SwԀ_xY /|w*TԌUnqCYnIlx?| ڂ=x؅wp#>O;tDW /);y&(eכzU[~o|p _[q7;%0\̝Ї}g_}^Ѝ/ FL(y/4͆÷17>GW.|gnqsfv]k5l:&.Zu[4Y>Vg(>.. Or'_]靃?.bG|~wF8Uz]+]cu3d #,b$&5>]uŝr;:-}oMmbx[ ?]䣓W$_I}W^Ye3=1icw\yq@&\9"}䶕lvrS޲󙟀~71iqxL!8>2:GdΡly!U.ʀcY-e\6=W>zg䄧 Ts^^1Oܹ/4f XoՆ`S'pXu.1X;`-X f< `=8o{6ߌ ?VȼP%| ]&sp= b6xKL#n&%GaSm]]}W䃿zc-d;  *wb@}/R@~y1iI_UkPͫI{c~0]~,J$:o8cwS^)OeTޑUy2*E噦)NK~%I~$;hXCm,47b||kA ?* ||P M/c|lBW }U<f;Y,~;x;)ZPa9,~-WaU0Wiz( 䣆i7M}DcO%x9OFJ_C/Q>P=_jKtKtxo:j|(bB.=1_12_-4K1hi>mDy=5<փ}ۇ(gx? (Ň#?s Ȟzɤ  ii9/Q_*=_&> /'|O|0RDHo ǐ ~;cGdW.$Lݫp=w8ބg=}ޏ}[}_{#=C{;}_-}°?T{Ew|LJo,*P*=q&ONg>{3^r9oYCMq=L%Y^+/{WT&~xծYš֞Xb-g5x\ng45?&4PiD+]Vt9lL&qY_Оo^ % I_x<k!|V DXB,$-̀aЃfCەVfadEw}Evt2ƒ>Sh>ePVk]6slogyߧߗb܎W+F!14C#:I]fayvt.l/Vz ꠯ a}ק}1^Su3z! e4gx!m #zXzp.0 3mihKꡭy:d;3W{7s|;̿;ԯ[jzNk .IәZO+jE헅Ṭ qа6Lj)Nֆ6l=W=74w8͟s&>ŷx! /Uu%&2̉%uԀWTYUfe5Y^TXgQmjQSY_hk!*s~k^?-8mP|ۋOeF*jJ2UR7:(q kA)IeAk3 [5W0tX7a D`Vnu?,&HZO!zXհ8@g4`$Æwd*A1luaTzP{kN`{p0ĎjBSEM!rR qoY߁?T>(!V_L^(?xZ'8ué+<,kJ&[.]vJfs)Kl Ii7%}P:Lv ?0苡q?'{!\[҇(#II &E"ţ}whFk0b˨m*BE1 TH+``9PyB#m@VB,m6%R[mG" {nHnC -&dw9=SPH@ c! Bg-9(,Fܶe}3wOݑ>w#ף7q<}mxNtߠSTMčr:GDPԋVC~ G}ٝEй+XyMcQw[8Nx߆}8܉ٌZ S4Esd ҷZ|SQJ_0.xsy$cU8ox'=ݔmژ7sA.h{V9/>#$叾ǝy'1[zk8{8|n}mm:اo}S~4yuO@iʟ$&qW~~bS;w8nq0ZYxj3㢃+h>x/-OsJNǪx&f]9)4Fƃ߸c{E01̤:s!*Y<3X7rm}|:EDȏ9$d厲T]oj/;-f ~~pxB6#yIV2}dGY)ʢ[s,[eȹ bƻq0vgv`%xh ߄?N(WޱZ`.RsǬ2)F,9'` 8r^| o^lv>aW{5,l efm,.-t wwpx.}pnf;Agw BoxQ)؃S#pI%bFMGaΑ`{ww ?2#*Gy~ < @QIgᏜQx <~/#Mxˀ`A|_{\-?3y%F$lA9F$d,$ˣ[O˫i,!#]*3GO~'OC|>zYσs} .:OA{0y&D^F-Eȗ,#U䓈<ѐ:A ?y;?o@ _x{}bWh~ŗVb<:+g|b4~J3H oe2л0O[*ۯ{UO#[oJR≜> D}EgN׉:P'^!csN(zFoMX7vJo $L_D*h>Ot '|/.Ч'W"}п9 F/"~Da#~GTExP\}@!97ja&_(?-P~PT^-r15|k='wRsԧ 0|)S >'/&~_%~YwHǐKoC9("3*}c?b=K=[>fgf>f[x8~O=_п@{w=/?xFG/To ~>'%r%G{H362nh8,Wٛ^oFsx;z 駷ܔQ}} 2#a'T3^ǫV gMh&kV4њkG+!ݧ\hiKc i+41=ZChQ6Wuo{?(Qoj|BB$fΐn"!FavaN3͚,s 50a,4<5&I &aY֟ٿm=ㆼ ^O5XO@8)@@)G i.LS} j-0/}i-UL>m$?H?/`o3~I`^ 0,=V5uՒ^Uz:p6|ցa]Kk \\7qu!y:'3gC8ʧ|9Ӓ]W:TCSu*"6L|Z[iq5{au>/Kf1Nj!yU*n~-O!6t6 M4ɴv$SۇKn-m. B^Jvg)MSHI~%nmB| ZuV?^K^K^h mKԶ$$} Hr]yT݁Fx-g7fE}U8V_`xQ=Fd4x-.`p ,:܁-G<ts!6 gf 0pz?{xo6?0Cp%8ld 1XY(?Xt͖0$x?aS6{NyGC pKms|2~)|b.΢Ǚn;Իѻϙ6oimi$| 7[yN0_|ؗL]hey,OKQ~'?}pσq > IM/@OQ:|%ڒZTK)!#sF"_;2Q6~p|[Ox.7~_913ߥrSGD8>o8zI/#c[f~*GExP|(L/I4TTKx@V֊ZN*I[PB*Gſ_yաO)>W l(D壊P~gkD?1OG}WρHHϓ=)ғB=/R/R/}|?JEQ|I,qo2*3dilosRg{'܃}^~Mo9]e6oi|"G ?cI:[xQh+C2}Of}e[=kfsrcJx~>B% s,ž .+CaXBk(h#B)TèGzEXooY_Yw|A/_*e|n3O 74c~}+|Fo]Ňt_.0Vz/L{D_|Ƈ|<}?>^;+:; #:]!/ ]Mk䒞waqKcZiI { !4M-ܨV6ڮj8;{<{B7$/t]w ûA==] j lQX5 SI}65riU]  hހa)δF:z;jbrtoyGh*O9$o=4@BΨ~qV5V_:l~f+zLk2ȸjQc^+}ma>cclvsg||G~ےw;%p2 : D6c,kSZgؚQk@:k.0>Ll&0v >ޫ a3x- 5'Ƶ a[?|zƇyxkǯ0<-:͡S]f?uԕP:^r/fo?7t(x7"zz>#&:%6d؅\dm-eVɴB{++m1d}!m Ѳ{K|3W|7bW;eUޭ1 [ ` _@DkZdf !ē>܀߆vbm+;\*n)6;jY觕xF|Âun8; g[uv\*mUET]Yն"ہTpM7%eSuIٽ%TQNU2?LėvC&eچ3vSmrۉ<4!n scbC݄QwE]gP|zɩ&{9x~!Ńc:D,+@Sgd']4mʤ7'ߑK}#. x. vp3%?(ΥUPI#!ETN/5L<_?+wY f |$=T#M jTxF%W]QpE=NVɳ*"|ihs C>gPU2"ZO~RSTZ0" !ne(q"Cby"(n xO *^%nq/<Ɠϸncq̷>q?L䬏%+ 塭|Oi>灿r;Xele;1u:K1@\ȓq##~dvrQ.^_-?e'c2 Ev򑃜/MFIeЧy)XN\˃w/- bZSXcaB  0@@0dCȢHmm8m\CÓ@݃:2~ t!4C8|46 Y0J  |!. 0n#j]Yl," %ywE2t? D9BxiFOO R hirt&q!QIP5$š0el'JJ|aCmb0Dx _DP)OٛP΋ c<9DtFEg}ƁsnWe$=Œf8ȱۂ7˿@@f.YB֫ga0(+0H-B͜x4(LpYKnDWHHGƴ89^=6h*(y~$8}Q{ `}Ϛw0.c!!"c䟐+ҬDZ?<]he,(815ԢԽi)hec-:`- w>F qj-^t,'_(ش2`Z(&" x罸Rz!VŁ2ڍHڗh6ag_J9tiA%E8|&˕]jq2]}19EIzџMJ!gφtE~P]2KdոC.xp\(. _'/D6(l\rȥLCZO.q 8ù0j@ P,s v0%a r9jq1 t/L> c=,DVt(;]N1H`24~5ډm0BFC콀ax@^{5j<&&"hg-qE*Hw"t ߷u]\-_3׍sz[z:4^{ :yNpIu#ء&(H ]hNӳ( D8!e !L_ 3q=x܇ŜR0u^bnCv+<)6@nozdS4WX>TڋUG&a<ѺgG^yަ?It|T,x`YjC_A Gw]vK<>2 x%|e<Kd'1m$\5B[n+>ٮdk|g|q O˸.qp'kOeΕh[f*y6#eL Z 5:pfp,}$B44t)l@QUk4gރsI$^]#GZEzAʶkz ؠci<3͙, '%GGzAZAG>=" Q=SS x!»-x>`'l-XOY 6a޼nd u6ao7($ܾE(ĉe'I K %RʝRʔRÃqR  gW=bYxțbof)9fgeirjqu8WZ-F䐮6!tfd7"|7"\[[X]KS18IL:cQK_#+R)mrt,Z`?M{ fdcHfJR 4&04EG/P=?\e&L-SL?ՈiɟiCE!XX'-D_ ڑMZH4s6d ^؍;{ `+bUŌag߯z+#WCsK2?f_Ѓ!hb"!놠\$GQvDу%~wۋ]~A  Hu$H ED&OP"UP3x #صc}d##PoHqCVyBDZ0x)-wI#I{G% 2nzA+\yjEDgYϫNj]57!Zp `Ņ3@ nX!&*(sgEpa'T0X:Ԟ~}dȔ9f9u#QN,9P 'rsBcmA/;!!;1d J|$ЙRh& udbe*fUH,eiR2xE%D}bD狓3ww^Bln 1[LgÖҏQb-;ACTP4rccs+BL$We ߟ>H"j~X-@\o٢ },o4/=ApJS;u"zPi, \>@b|jÆM7 UtjBzڌd="d^+>T} ۨ˕6ޠhTߖ='vDTQsh*قwrL9f xt<+d/y/6{."85Y5JqSxC PA˼/Hz( I KBKU_=|_*'S\ v tPհڠG]4 @j#tpnjh@xpa*og'D,WbdtޢKzU8+#Rʱ LIL*k\&}`[4T¤O*MQ#F,y"j Ag}E}U,?J /ު1os< OC sEbK'BOX1&$hJ)"mlmopF4''//]x_]yw'?E?}M,Syw,)AIpA #qT+d彉$I\u"F"Y}} @ `]sYpWxl pR6d lKD(&oyH)T2I&_Ò5?k]ۓ2~2yI"9ceVn0d*K ()C 8j%J͔>FڸY)0t6Uj 4xoe Dzlш{ P@ 2} 4!eVK˨j;kQFgLfM8 P"Uzi>{RHâ% D%iY l4]qೣ(ED{ xH83 SVd37:xC,S#YR!Eb! (Q!PHL]c-bP-ճMKTxKazŖh?k9A(NSar7Yg-]A)Xr844 X8Xhdt J#lxǰPSo6^|*`Yb{~\ r d> wJ d\DI8INѐ:]3^h0b|wzyr5a(O|D"nJz5`6kZM $IiiLk9DiEa)\jqhTo`9A@|U<}O rڔ2Jm~g\VM"Z¢Z욖ʞe[ ,o|bP@/P“O*XUFH9I m`:*'S" ˠy GtPݱR2/Tq(^"䀴91|D 5S UݵdU`Z.,YȢ0o Ct%w:ʑ*JeĀ?Pr`BWrwFb KccB) "0{<\W㰸m5 \KURtYDW!]`ԭ&7~8 CKH hyGwNd Txode'Ǧ(LY"u\;Ϙ1n5vhq[=fI2qk'[Z1YR?L&ƆԒSC?̏ɕl*mNn.^veXMhuEݮ]KpHlũ_,g@U!IXU%c u.w{d_G}_:1Xdkuf),H6`pJ Nw$jwlŕ|ڐ[;en-[N5ːste9;o3!ee!Dd!oq23O2R}Q7&8psvC.e="rֆ-N's%ȵ{ MnܴD񟳵6֜\ \8%*rNn5"ǵcsuHowN٭+< feQG_}PorǙ? R.xcڂI ާW56 |x}:QS'Щ :U:oЩ7ܲ/[C@3ob횘)FHʂ E!HРZ7تXspG}72 n !Rg-? y|3f@9r_B%LY "vr-"LN-VTMf'Ww 5DܜRɘ691jkG;dY?D$Q yl08%t'qrcnc!J2dҴ1%A̺ޡ %/oJBlF v\܍gXg8gX:!jm`zG$7%K3,@KsK r&8 F_ >(dЕ|pYb4 q-C"\Z \K-)W[@hAtp-SH,S#1-Y, ,[ QX ͓$g˲CD5XnX0-$+ʢD$#k F[# ?G!o`/ .CqIKS)QP,:I>4$H@F]y&UWKS ΢ToPlrߚuF)5i7~f44z8r a΋Q?AS 3Y YVg<79?4~l|dJ?G1ɈGr;%k͜ي @ki5&p rmYb9lѴLb bJTWA9X*QPg<6c0}pGä57~>62jS%ţ B(-4.̫ߖ%.uVcun ʮZ,꩖DMK=뢪(%J WN"4ѓ-R8dr il9T,R+ܔfzÒUҸiY\R:I [tœfQa0-YH,Ov-hx'M4P`GY)kH:d56s 2 xsy{$ 2߆)rU,ow,АǸMwBCgR+GI=hP]hdYt%CVB0-ӳ8+RVu R $UIJiY\n]u4oD^ey,P 0ȚF`iZ6-T JxU)R`  D(Qm*Z<X4媮iY g)4􍟣[&Y. oE*iՔبbHo+ T r Mj2_C*ro!(rO|&)SjXdgZUX@bZƴ$@ʲ'^9.`!(s L=7~V sB!ma$§I(iA$!rd&? d~1[yo)hZ?Q~zN.كEϓ~gG,9h؄FPK_Z.x) B,UjJFtxUX4%*42ӲU[dQ5mWt}> Ie> W-Zc^r$<"@tGҝSH*3'2UX<)Dp7~bW.3Ş7~B&Y6sc]oW%6e3$ٱ!LsL\L?@ȩeG^%.9jԌ eur5 %;D@Uq`*)2L2yOL)%2E zpTsHH/8g= ^'87~֋D)k "R7~r3^ƀu g=f/1>=WKZ]+C>!p֍4K 1XBF;`:5${U#QHS˰%1$%ˆiŕH4RIJmѴlUZF qHtFZ_?o6o|CSÌ&e(*zppAF,ɢlP!6@2/;V D!zP&(TT5n9d],lZ Zաq(T[=;&{:t!Ă;|t2 趐pg-(^tBf?I&[ד57~,/U,U_`K6qbF{uҔSA"&%. 10+p@٢ZgX Y5-O"0-uA|+nɖ~MI%IH-A(ThẖjɂExUbk,O,׵dU3-C,!Q7~qTmqZtmTXõXaZFV-O4X\yz&f36X\Ϙ 9oO!rB1q/0s-LB,AMHo,BEUA-Il#фMꍟ8F:Ţ}/F97~Bde/y gVFЈBJrD˖YkpًNVo|䄔 9 '9sprN*?i҄yr>?0uwP^6cp0|xh}Yw@ q y"W↬;lTh%KD8%Ѳ @h4YJjN2( 8g~ÎOO0 ii"D1x 0%J$Kڦe-%Ev s2)F#RfVh2L%l$Go$ゝ oC R}\TSDe+T *?'LuHo-햡L/Q). MHDW4eZ\lϴd[6XpȚ7~N&h!Pi푄 oTdVd  q9IS1Xex(?Ĥ+OdcoD =o$G^_@,]ۚ `eٶdZ4-WS DV5ElwP!IyIF@ $;FA#>)o$3!e,X9Dwy܈yu*!U!7~c[SB́QmT卟$(A%ҔJ!do#I("9҆4Z 4jt4knZ"C[o<%q퐤BBEYq 1NN zdKC7~>ō,FƯ , h0]uH,4Z3-˖kq- lQ? #~P,Aƪ?')bd0Š==KUx%TK [ThП@$1<`IJ80ǛZm"D>6“/ bxJ{\϶ @xS]Ke5ϲ-W5;@N풅rXUFt+KWe%Ӓ=gY [\*" \o%G%ܷ&+5ِ]8C)  `pA쯱W^a&R4Ob LYYj92ȦmO*8J֤i`Hyg*DQP 7ύ^s ]TPe+F$ !"tOyGAAñ4@ 欕  800D2 3 a 3 {QC̓ %'@@#Aq :/C3@_š+r֔FųLFmA.[ crD8Lo8 8ͱg ˹AhjQd"t\{1 LCR;]j7JTF ̝J U}{D&4cB٘mObD8Q+޼C ?)+Bze-L9yi]'הLJSJR5%Eq@\4tn<@<bTB3Hȩw$\Eb&:ZxTH1rP*>tS(W. %U"qd+| hE`T9hjQT/ e#=5c y{"˪Ko *;>`TZ}tKQKT>qDVPQjeEDEITG^#un {.K ZsĬ.$? UJ€~"RՂBRt "ݗZ,'PewgЬ^iřtQyV򘵬L8w]}7@}*ߠ/۶]j*R(V*-1 RL/Jз x 0'6AAVG0^%C2&^Yd JDTN*Xa'B-IƧU__g9P$T.RGi@k,AoVy=/nDOqI,"P}msyQ;`L=-4 AM䰨+b]cO˷5^%jQmuDuE@c嘜#YEZ"xEg å K{t@$2$~g{VnoSi tP)#ψIkqi(LKLib('ң! 16rtҤ\'s>#Bqsre.y$vZ;\ _0mҎ5mcވ djX|2N91'/t)jI$&)&x콒DtwARf݅r)dPy"+Φp,~x!ȥd## >j}}pr^j|2Q])bl)Qb.sH@P 9 @@ܡ3BNń~LQ,/(=ge$p)+|&XZButXzLҎEkwL:bܥFo-V)t{ F \?ę: =-̨f<Åx<.K 9%sWjJcQf9-,- 2.QʽgRV|VJ!U]%ȥ$c\w dЋJ4Bq d3e2F&3k-r/t^bqۗ޿RꤒYAGTdJ-Aᵼd!RXr\%#C7g+|L\"*Rf{E^K1caPoq*RhAcŅ\Qѵ QƫȊم pVQ)!dgLs'\SZBU$JKfL#ʕۦbԮنr"c v1ȇƵ&&[H U۔h<q {C0S&Vn?P1ɓBu%X:YNbCRV~vLaoL@m}I.=e<߅jQ J0L;2/pX ն1tq 3 nurmՃe--p~C8_=]oʀ+*dMgڅ Y<ݔ<,u]XP-Ƕ`SEqvvǹP=4Nn"dL:$@A[g$,* ˫+H[Y*^4)FM&ܦsSd @*/tnDuw]`~dYK#AliHi4DsX?OCά]I0-|c9UB06N_Y/|wVg3hNE2R;zPAj]/#~spy^eQR6~=N8RǹHG 굔 6DC[c8bQb*U*?p]2 4U=8$H~u6aD%y+r'?#$ {O-svr++Zzj )\\ \Y qC,}6n;6!r3_>@hj; Q'mE(+3~J=4YO"i ;[}Ypd "cػjs [.tGIHug8M0 !6@h˃/bqQxTo-b".NFֶ{qy6Fxїi")1~CK6?Źшa11 Vq dJkPQ |\ډh)fˬimvs:e΄.1׾9{?SPW,0X6iu}>?WSCm?C-뙷\YUSk.U=|Wf:Y,)Z@G4B^!v@QxZZլ[vxS$`{7YjbE;^FT|v i-h3/Ѽ -|jNg*EBӬd9XFU #%0lΡ:=[I2LCx);] ÐR@)t7oAOc,QbsQ{%eAb7i$f,AKC.٥{K1$kk1/* }矌di_Q[<(99g"q*wk $_)`ᡉ6k 5A@F#$値kW4j*QZfx$(E!u !J|(Y` gebM)-%v#>Xsx'{ClobQ?{;`o@wlϲI;Sl6u*^I U)8l88_gyAD0 T__bL.uS ;YT,ѐ8CҨaD" zyWB A.@`gH Ut`WJȌ4Rvcn0 t7h(0\i"ԹV'" ݭU:G[bq3G$@eAUi,Sߤ@BH%s5('2驑@!.4,W5t*$(6xc QAʃ ] JyI7x?/2%V,HO3ch1m:;$EHt.q%/wnr\WFs*U9Fk6PXr39˦xI8_S^A$ I)W$4E3$A7x7gǍo^ODAtH#!LT с$ 9 A$8yj;ΰ0C`ʸq 4"38\?`j̀!'j Ԟ7#A 3 4@8pC Zz' vb.83 GrckAFKpڍMV@yYTp8~=o>E$ۿ@,踿o]N$+ggrQrc_j`yo5q/k_yHȍoxCOey()k3?n.F0H :`=]nޛkry;&ǿk3?+< 4)X_u % G+v|A"L!q2I7B\^.5=4^h0W{} A/ 4LaƇW{m+h6SeXQf2.eML|w^99:yeĸʰ \XSd>9L@B|as 2RD(=ON{^2dDs^@C yniZ14ErQf@=𰃉ߗ]}v6M~<Veas  |hQ|e+@@8' "v *]rݣYx5,bAJ_pNՌ;8%h6tvC8ԣ : Iޑ,(6}UL԰-EGK 1Q=ts4"E}dyosIY@tỏ^K; 4A"H)2Z\tM `a.FL-(xr0(S劘L񂍠`VfL yٕ{ǛCsY}yO.sf]A@8:97f3;\q~ ݅g7E$'^|O#/,65{P\ \X޳,%_7(Zn[j|hf#i0j<+X0QjAPjPW{-%Vf0CKQ@Zx4b2"đ'%4B]jjiW])lEox9gv^K Qix<ЇTZW{Ɖ"(ᢥ:0_THBRHHL`Z]Yivfy2g^oͦoEv"FWr3t :0AÜW{-5u+8tm) 2@#[`RkҠ W;f?6 t͕{Sj\MW Fе者W'HsѺ6᫽Zz [z2M4Q}hB5fPkgP`5֐@% nrx9#o6|+ֿk"LRא9Կk): 81lѵWTjT0Ș+3\Qj!H 6aտk 0(j &_0,9T@h*-Z O)(/ZaQj´Y85-RbAP! @Zȣ DoT (PÑ"Yiz8)^4+0_f+j`X hb fG^Ƌ9=rEAv961+O=b$/;_9rncN3DT$Y|j̀`+h8baB4PkedI0=e7xG.狎ݾxۣ}+ Il@Q"-k~^WESN$yr{?|A4w(77 0?,8Xr@+O"vIr4K˗$(z"Rloçy02_reWmݐPldaF::b ' ,JYŲP9J}278JIfEN&>|0B6=?^0xY8Ayܐ(SĜM<M1ߑ;_aW(q `PDAyf[&o|s77xdþIMXr'# ⲼGux= 3wC~dgb+o>|;bϰڞuw(84n/x4CW:D EFNt8brIp<7u2Jo;QmCKюŦXQLHS7kEY ɌT7;䊚᳦IۿR6JhS,X RM,o}\L z9rd)D5r򚹒|b&%iɥ{](%O\&1&(XI#cR$.̄ k2rM2ny:K]',uff'&('>>>@ 2,Jze51͞^MZhMi' rrA15%1!5\Lh2'&(%JOMH&Ji)uZ%`O%dʕʢhW5]q@8b,&&&^xj┦ZJ\) Mr)yՄ&1ART('=PƤ-_ZS`/RL'"諢kGI CRO ےr r X!Z?gQO?JYC+VNZN%"!/dBkL7RVJe/pcEN) ANc:ZRјV)OcjE-,&'Kcr?ŖYf&bI)PB2"7:D,! BC,+sX/%(+壢P$\]W:U,/dkE-?d^ՈD4e"JUeZ.d+lahEVvB9aWdofJiSVL,mՊۿbIʊ%؎aXFju` "DH A`Jڶm< U1o\JDFUG5rX,YKcHra爀Yb7VRSm7 CnJԆ#B|x+Ou>>=Az W\u輮`%uEsZi#)vёOQS,֣ 5ɲ#0CtG9]lxoFM^hѢcvphF=`#<-Q̳cQka2o`y%Ȩ mxF$e'Y0QyOԅ kIYZUI ϏdLT翐=cJQ%Sj2F*A,& M Avq.PЬ(͘@տ$Ȉ_|Z8ؙ݅`=z]meyfЙ2#w,RKn/_s$&g>Beyz"Hw9b<wQqs}/2 1#[v]ywFE=^Q,UN&2sZp] W|?,o8,q߃8p =4M9h789 09cNcg ^x|kC 4X*Fqdx̷;مY j{,;"*aqY^񢡘fhnj&a 7pBE(Hzvp_  .s>򂧘oȑ L%Ne8^:Mb0G,'x=z.@9yL%xkwEvYvUT}b$v'q;w?ʰ"&61ʉqr&ۅw9O,_ cBrcyy/0dw#%/srh\y?vx-%PA= !wxg^bnDKygxz7Ġx=<ݾM//T xᢹ tA #$d]zP!Ih =<|ch$l /lѼ63Z(4)#͓B4,oͻdgzd'REE(}CX!lO"zͮ/X~w^3p8p̙3!b?Po!VmuFa8ഐvZMĔCmm&#f7bnNX6eXM'Amw>YdK/L"-V,v~AYSz*)-Qzؖ҃fX [)HIGϤx!Rc:{)F8ؠ-`2uJSjTSz5wh%cZPiTR%uʔMIقJ%2$ۢ'RUYONFuD-!oԝRl:)|ꔦfHRsT$U%͖3Y,I翝5yIM1&4=^J t*ܱK!-I;c1\hJϤ;9-^ o76&2&D3gΜ9AhQCrXiX H0b1B*w ?gΜ9s!~<( bP @"G ul>-jۈ<jlC#ԭ}l{1~OS1#rIR[B^<['z5፴B_#;Z s_7s g&/XixG7O> < \ѕ484Llxנ^TWvpǜoZhW<^ S8m*gtN8- UMsΜ`ay4!TOt,ng:ԁ=8!ZΝbfnޘyaWb{5#vz ư!U$V|Bcd+4iL_6 nE&j_NHOܫkY6\_\yгOڵfͼQ]y_Bb9QIҸ"Qe]OhqkOO 8!e$̇ENRI+bskWpPepKXkuvT ꨑ`[ mO ÑΝ͝T%17UƳMAFf QK1lJpfE6{]8?dl:k+zRͅ}Xg}{v\cϳ$mfUI}OUPN)7 ?8d)T /Ʒ/mlN h4]1bOZW˚Cw ->Ѻ'(]V6ً379ilH#:VKȃ^:<W [^ll3UK9)M AݐMOK6= "RyQMrVD)w Λ8~,:xLΓ&~"PcDN fY`p Vtq)0A5͈$?:{%ns`t9$árLW*;w^dJջz=q54$e u@/=H@2{|BX+ỵ; iy5|?{#qi] mJFepL=.r|R~L tDu*!F8P_0!F,$QGNXHF?f"y*85oāx!ħ$]Mt? 5S-/A%™_>nf)9;:n,~y;`q+7^_'"+0,oĘJ1&}A:\O&d&p^iG%8d6I_iAH5Jĝ9P:hBayl[,uh湬d +I&$={e?)F ,ʨȾXZ W~qӀ#PhZ?nj9$wy_t |2V;9-v%L9lXvw08J|H~{K?cz)%j/~@Ӥ1D44AA4#K9rGRi_rfB)`@nOi}36O=Z9_.=)G TAt`}zIܪhK7H7u&SxizA1C~ ʰ<]~Չig黝;%:ɡP8&ujHهΒR^V .JM $3}o$b'CVYxdǿ|E)7.稓%5L"bC94t*x̂1aH4#m5{6ToxJn%pt(d ^_1$SK5÷9J) x򬁡]Wx!ׂNa[j-m9$K Z6mU+!'g ^TbV)LL+EWPɴ@p 1:VAas0REGt?屡Ǵټi !t=h^Wg~ l|ѧ 8 nїQ>x%^0=1OƐ,xOE@'A |?OwGnlgTsv}2TbWv9 yVDjLT邥a,f|)LE;l68kF^DXmg) 5YJQgFTVE%5.i&!9H|LIrӡ:.vZ՝`In ISęGep+d}6#A&Ltmv Gvsx\nMKBHwlBC7X" R a `4s0,OxyO>^)x7SW.1*AQ+֎uiXb\ =aJYT̠M e9< *0Ρ@)id*bkdj2Sr/ow圐'!U#zyJ(}&QFfjzMm@:8ySBCw\o"r% |#&.IdU#qN Y kB?Z@h `jK^R`oE9|?ou #Bh\TM8 9(}^c`P䧰%5k̯MeEl²rJ4ۇ-qQXd-` |?}1|%2G>oh*ԸG{K텍(ނzgrZa/T^I.o i}1'9H{C\x^4 <\Zܪ ǭqty1|9,AW"Ncq7Qz7Sk[^U4+t<7ݩ eo?W?%dUjΪ>Zε/ Bcy(Jps:=CVlO$@p eeYbl@PM5 &8k>q^:>C!' z˜l8}Z!םќlw4?7~YrxSՍʕDgyFʕԧdʧAwf"^v.LUА\PA,K5OLQ^}OOȁ_8>m(94.'U7q].WKtFW)k) m!Ev`4IdɆ m/N *Q,׭m6o$u% Mq?)@|u@băs<~^KoPrqϑd-#Tb(xXȞ͒SeX"ct( 2fm-A1j{lR|jZ5S2f|ʞ ؄ ű[f"FPǀLs0/t̡vU놻u{r>7 \)(^b9b`[$,HCK\%gVZecE@kY$K (,=j蛥9}71VN]a\BjД q]K!ӹz< Fe̕? ^{aZBF4Ź &ys@ؘh.Gc+QTP>1PQrV( mY@*tdV[ >qD"_@ ])GyzP" Ƙf:61Q[(4 l3^'vq3v@/PqIo}P12*Z\u,}-cJG8%qA{1ۂ*\,ZBB*dh }F`OiPN#+)ۍF>-:Q2JCa9%-~쨘jBVWxS(PzcZOP(֥L:j tvxQNCRzb& s n4Lu&Oњf *PM*xi$BZ\tiB"}dL ftiHzX hߐh1gH-F` "gEF>>mWJR% %X5kr_XDT1L\,??&muEVDJb?22O{`DFΔȨ406 |Q/M1uiBrure0.-IO.-[bEX\aETKeOSCrAe@TJ}"Md'B:kJ]3!|:pR`\F(e7 `0/)0oa&%X)& Xk )eL@TJ}f϶:\oZr! .$ hnLK_z1>#<ߔMA$P̩?-.Q:Q"Dp8;@#8(Fk%mD.بh'O PR5H3x@iOM4%JiRjeYU+jW3єUhe.VJ~&nf,TLcNթSsiϒ>Tw$ؑ`'v 0RY/Yw;Կ~uKuƒLKe*YEw-֬J18 !3dgGN2[hDDfAC 0(bS$C @ G +ʕѸ羋;LŌk;,gS /th3/qqSB1]J0zDX 21>C8`on3 8Se'$Y7Bۖ`xnNF # E6`f? KRIHmۑ(,m{7R#i0#2r zcV6/\ZBMQF {z B2|dkc;iĘ #{M_$tvDA!=$ǹBk%)WL`'%CRs#@_x?sw)H #FilKSkclB)kniLeziS-ataDPNG  IHDRա IDATx$W't-eFKyz!F .[^`{2ӽhqt eB`[6HƒE/ނiQy%rŮ_ǓOVf|dDdFd^!=ʏ8q뜌|׽ޞ xǎ,{׎ @ @_nmuk @ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @cs눍 @ @@G V'@ @ 0f 1 @ @thu @ @c @ @\I @ @`o" @ @\@ps;k @ @uo-lL/#K;  @ @~^Ox׎Zjs  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&-IG/x8hK.~sgo\4twW߸K毚?>};;v,ǏwvӉ7ռG @I HNMH2uv/rrA`y\.رwϓI~Gϓ 4 @ @`Sl51 @ŋ^/t.Y7E$cgH2ĉ>u<)8vI @!$PU&l,<7^bN5߳`٧nb5 @ s 7 @ Ɣ[21'O~d_Gp:'@ @$w%o 0?_?N#7y\/𑇾6  @iTF,|_|_R~iR/ @ @`n? @`R%A>%!y+3e  @؅.m&k㈿f,>twuO @A$aU(D 78տ NN~Yw8| @% 8-&#S|o_̾g&Ё&xۧ⠓C+ @k$5t(rʯ^iH @ HoD@~F5kh@If^"@ @`s I yOBkiF8Ml @ &@鬮YOrO2{/nV @ P!``Y P-kzvNM!u`Y @VF0Wf-^z'>?ގY= 7.]K.͟LW\^p=vݳcǮ]?<6_>mgwӉcǎn:A @F/ 8& '$ʩ ,hw L=n~xqD/Sr:eR0 @`/^O yE`5s%!$ Ɩ\D_<1$EӅC:{ t;v}OW`~u2uF6H 0  i(  $2Bi(('DPc> Wg}o| 7M}OxS?GOkYvB2 @g AG $6Y?%%3rĉwT$-W7%$ZƥKŵ 6*9Swntz~~nu{髛L]ut2`bu,y2 @ + 8  0s{w"sz#u볜 B=gǒ벁$jJ# y.I$+6M,n#eLnޔowyz,7HeW,+h|I @ 0zZc}$WN 6b_Әy{,x$iX0ɿu#<N.]OY_t& @\WD pXjK#56#>oXFnk_Iuh\t\n>BK/-ѕ`$`ݗ`G @=U5T 9l7wF5]+0K7kh'wiuM ԓ)yX @`m; @`c4G8##\2]C_ܻ7xw^'^ߔ @d4.Fzm%6k#.Eϟ[dI03O~m^|2$ĉQ!: @* 8֖ȼo〵fIRΞ}Vॗ:O %!4ivwMw19 @fN  p`85?阛v'Oܴr\^C ԝZ @A xͮ@N zʵn:73;6?uh1˵ʩ}VS8e @`+^ry~]W0KfT)S6{+H6B HF LC4$hHb y\c[0(] Ч@~/ ډ_tL?i,N@p8[% @`tCށT6wb55H4#291LC{7H;5r7^C~ަO]Q{Ggaj4MH`keMS [::5`\w`Aۛ$quդLQKUqoر'v5yw} $UN~iGHLHog7>~7{ѯΜL"pUO, H.xM=::k_= `䝾ЇKCkhIMeTŋ uo2?1"$)-oM IDATr>ڤL>_1#vZFa= @HO[ j<7ŖQC] p[++f“Xs?ﳸ/+)~ߙ6~l(}nN >g/:CF @2@$@ =k'O~d-ɔ!cH>箘~r_{ɿ~8B`'B4 @e#E&@ $EOB닱Ly.TػiÍ@> /#'f"0@>?moTiLW\tU $ f$`WL @H s0\i3ɬ]3*] 8#$TV@*JVa?u*#@p 4* @ C[F\`ȄN$MM Z וWG]ص[  %0aztb,S}~b]O}@2ak+'@@+W6 ǵNwrM=C @#}q="  B 0~!ot?ڇٽ曕Ǩǿ?g>a|nοv҅{Tr*g?ǻ8(Ts,,EipcxM!G6i :1ӢuU&@Og~},DN _FQk>NnEK @`#:ݧ'V_|/IOy0)| @਀Q @` ЖVa^mrMz/g"/ʑ~MX>}'$`F! @0\0]  ЫWsJ3tSv^&@`;9wim$^xM%@=ܳUz@ܼq V?yCu{PE1 @@C$AȐ3 e1ءm"ƥKUvV 82cvV/&@ǎ_]!: @`6oQ`9})ݺPw~LT9Twu>r=>!h3Ne @0ø*2/GW)4YcHc>hﻮ#@`81_'?i6?\?R2 .Sh%1 @@I`CXӡh#N?M svK/d&Is`IOkj6;?}mf[Z@p-`ؒ7:%lfhO)I>Ow &`M96Y:'?q jM'Nԕ+%˨ 8x-/?>h2Ϡr,Q}B죯Ө,E$gLǏ' @6:Jo #,ҏ~_Dm }m6 @`NG;[4$%5^cHE`F n׊&@ 0n q Ы@&8dtTs=)hXCۃX Q: Hv:$pĉ5 96H6qױ6=٤^!@ 0 )  ГGOYO%UsW4֡!FmsE]wy& @1 HUDr*Qb[j3ݶjSx/m$nj~S#P3u[ @$՝ԭz'k:/\;cwNn^sEUKjE[2 @`ɭȣ_&[LOzx&w)fkPO N)8\D) x@Fa}-uo\A@?ٶV9 @Jn:St= @^c_ {٬*EI }j%Ent۶tv휡A)^[ @`xoum  0J Q6 0][>5$9xӟj/gI6ukPئ,K! l C6VwU@p-#. (SC}p5 [t龷~/f+6lzlSNK>տke  0 |w5__!@4$n&@@gs5?[f|֌9wɓ&oosLDRwΏC&8\ m{5'@vyhxѯ̓dtZpɏ2eGۻ3'~f˃)@OW\$, ŗ؉P @0mO|{g;oO=)'IreD`;/1#J={ ڲӦǏ]n <2`2}]vmU%x6M'>qV$@ kUPT $NٷO+*xWߙ  R`b z8Q-xjm1$jls?12EZॗ_u]7G7*B=p =o`#@@@X\p]ՁICDKrQ&%Y=njS,֝LM:w N"iuItegI2>;v}o5ƴ7Νe$v^ȵLwߛ0(z0GLE @`mF:T[?^`O0{Ӡ)+(<&闿-L뮉uAG'{~+tbb> $)% 2b;M~HO|[aJOumcˏ}N'z1*0pX_ @`mGJ% Xxt௕<`5ImMl+$D79.q1ɏU}nS~Og]7ڬSr${U%iZNWUuI|Ō:us5mƷ~/zbQMf9حuFc;?Em@9Y(WGyWm_\ɏo79tL?,5}6O4=r'3g>H%I+eTf9%2o:'\`|2z[>7>;l-מ+my?ȝuVZ됖Z"{1񚾟9~$۶  צjDr`iG<{G{@?y}җvp4m.Y75~):SK܇x~!kr]ha&0i̍xkmGU$o"@p a @dRSrk7yyN`v1)ѫڷH>R=yrl+5KD #-M/ @`oC5 @@oe4&OF7$q5 Sv5 9JKzkS$1u|6Be,QGD2iJP`~(ڰV#@  O  @`6ߦ n$_gwF̻~Si]lԈ+%;G[҈f}j wQu:m;U;LO@pzm&b .n`.# Ͽy"nQZzӻ.^}pHX\O/r*pȴ$z7V`\Cf(۪9]l;6aue76іc "x6_ה9HݷpzF|x[ȢѾywygu5]1C@p Vc ~0smXFE(սMz&23Ok6Nq"Uܴ97& ( 9Yێ9տ⿯,ϛr0DˡN9}؛Ss$s_'ߤI`~L*0O?7i.ϥ*L/`Hع@rlsM\,71m.PvMkٚ9E1vڔ_F5Nu.\~ ciw_ג?ǫ_/>  @`j @@(^-I_Ï~By8Y2'_ج$&2j0würk^ry<UΤ%/}*wʳ}޿vuҧo1>ݬ6y˝yk\IΦM:w>kٜ˪rZrǔ6?>O}dm^R|$YJ&?׺oL׵՛漷N;V-vͼ$ϖC_/g3?%Q׬ڎ @`{o?&/#v8$ O9tMKugNL0 !@ @ MH,wh\tU @ܖ @@|: ͈H 8 @ H  p6'6;ӂ龩QՒ @$*b"@ ӂz7,ukJi_V- @SRk{"p;ȸndZ;v}Zرwgꔻ& @A@p @f?suw 3$d\w3ĕs-l @6xצ+Z%P~q=٥K$[_oR7;v~0{f9SM @uoz ۑ^ @ @+z/vN1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7 /t IDAT @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T HVK @ @`n> @ @1 @ @$'|'@ @ P- Xc. @ @I HNO @ @Z@\ @ @t  @ @@` @ @&- 8< @ @j js  @ @LZ@p'x @ @> @ @छO @ @$}%@ @ 0i I7  @ @T z @Sxfoj{kAy晵տg7I@pL! 0H 70nMxg?.ƌ $' D8C #eF˴2 Weث:Ko]OC;p;Fa\%< @!  e @`r-s^.#<Ɉoh^V gξM-`F/r ?>>t鷯 @`[n-i!@(\O(۽=Ԣ9:Ň~xLxF5HMNYOsZi/S̩Їzq9 /0??n%RWߘ&SW1I˗/Ξ=j2 kKg'}]OAOoҟWV;c~Y.)ovc}b+oJ^kV>~c>>\ɩC^h^o:mRlI~ 3~񻢏شnm[O\JRN1OTV琺f>\uOxo o/m"@`/N>l6[wvٳgז_o7|oFV|vl],s3w 7TamÕ/GyG̼)_|jP^g[m_pa1󺘮Ya$oOR|>܊c}h~Xs=k_\짪JyMwܱ~M=smON)M,fybLה(Z5Ŵj8iղދEtn$1tNggz=#Pw{}A2m&|sMڲiml>j*t_XS> i쥟:K]ĖeJ[V~v&},eģ*mR6Zil[X' NXiSmD0m[jO!]Z.N9Ȯ:m,@v:+-[nq{7G2V$R@G ϥ|FIRK]P>WIfc,3MXO4iRnbݤYXNu>PIe_P>XvKڭ~Q>}w}Sإ.m= "}NIjlzYGdDrdzr]Cڭfy?ۮ|ZJrSuy=:.uS>_}5_}ƙܴ߷v}sXI籏>$]E>C|l򼯺,o++?yC~l)6l*  Qϑ9/rúf!9 q@ T*}D긋ܬj¹AǺ&#_ӟ1fH<}%bJ賮}{r_P}Ӯ}392ϤRm7[eC|c>*.Sm9q 0y& &wAEuSFfN)3I9KfYϺ2?ɈDI%69pB;p!ULٖJ{dW~ls@|̙E{ĭ'{ fBڤ?M,%z7uNM&ll?vԵgiU^ܚ Sm@>-vF }-O/4uSNZ׷J9%e[%6ͺIDeX祼!Km#m؊iw{ԽIo+~:>>_0 J@pWK $V5Oo]9Ν[yg ?$R޺).uW(',00N/bel);gOdj22/uM\IwJKJIl]+%.(s?/$鵪-.[se~Uɵ[˗/GF/oyL3X.2k+I2/ugfM,c]5ٯKfS~ }n=S^[,7$&}j9R^P[Rždb<떫zվ,e7!5x]X>Ӌ*ˤ}~>1Z5{[n92+T}pd?XrI0WMuĸO=[pvMӻ@of{ok.m{olɔ|ǸMMVʾ~Wꊜpel)3曵;w6v?xmykchS+cM1_.cC=Ԉ2Ɵ}ҴWY.ƜcO /T^p&MMB-)~M?d_V7cRV>MLgΜiRM)wO6XlV=6mϪ}HXک֪X{Mc*۫5_XW>_Mr^ 0k1o9kN.e+`._~OK}Tu B,W_5`]vrnQ"%ӢH,uvnB~Z5R1$VMuU=/;rzW6IuU٤?*Ʋګ'mtgDM-c>-|fҎUHK;WMO63u#plnW5R*uR׺mu쏳lFok6,_՞UV}v2bjjʪWU @`Sm9q 0IsZuR[K 岭,_u4Xmzr@.ICմ]AYWw0T!K$nf_ʫ yԵ_Y7Ho3]bj).[/H1uK<\ުuul]0˧7֝bZ֯Kf<6I8.6Jf|nkI~F]X/t)۪s@>6ZF˱ĺ)| @`SoA K"/?%R&0'yl.7:9x]V\RV]/.uru^SCQlgʽj4oKܤ.MUq&xǛG51o~PJB*嘲loMbrܺDlǫTeMu?mڮ]7}׭}!: л@2/_F~%fJI59Jmz?Uv}su1:TY|w.y1-zz]K֓MFpI]bzz~վ .ѪZ}$oPV_}"Ե{rc-foDQ¨KբKUKE]r5D@pOR5S$WUkEwq[ꮑ;n:%yZwJS~gaDT'yU5KW>u.UU#E]ٙ_U~Q6_]yhZߪӦu?ww{ՏSA~kܥ>u-I2uQUUXw?ۈUUJ5I1FUҡ&~köI{6O_OdQUI]OH.FC{L*XUcUMuu5Y..wu1cn]u]]>_b] I HNM`.is^AKunMzc~ϺK}VWF] %s`6)nT|<M2P׺>Qߛ1rI&k:净CIuuN]?( }XNg3}"_פUw\z[uuB @`@k{@q4*m]WZw=uU!^] ,]oðWv3me#yMDHAyreCOSiîɿԳnbHl3wMl$u{Si#@`n? 0q\eU&>)3ɏ)pGO0wq*, ݘ_n &О @$FmC@@w̙*eg?er +T.c&6u1'6%@@O}?%\ΠnMkc%U @`7qUD 'WuMF=~7"pBeԭ{禱xL+w]7C1ǘTڡQU_U2.eF>_}|n prеWylAHf(i9zp]wPW5B`$ƪluEmX IŪA٪un^fkmo՝V7rYQê}Gk߯~hRd?CٯS/zgWk/ ؿ  pD %dwsdSM]EY꠮@+tJ;V%btI uM,-o.XSiESu@Ky=WK> UJo8XCJwh^UoRۺy.0T|g?mmj.U.A@ZY ع@E9怳%u]:omk5r0Uu@UuPԕSrIky;M^7~YFO~ڄbu)5U#+G~ u彺}A~ h(v1Jpls_Pbcu݈74H&>GķIz;"G<ȁd]ȸ$ AB躊~xaJ$ѦU7b'uK2Ŭ`2=I"n; [&ןA7ܺ%a_R\X^|گ*j]Wo[ VOL`9_ ʾp8X{\ cuɼV\ަnԩŦ̪|V}nҮe;|" x(-\ u&6)/$WJ\Ibu+}(ŕmd4GGne!kbe8C6)+9mQڽI[cԫ0Qw#$ǫe+,7iQکCڲ|&ۈn_~6DU}lSFIkgIbmѝm.]8j2U&,/S߉y@F i6;eW= @Z kMCl~]yg9ar'n$XMJms0|`>pqA 21I&ɿlh&ݪD`9(JuI%ژY6guS["%uJ~{'i4=pMd֍Ɖw[5#q&Iдԙl{~>:-QO+LZɾ nEթl{$p3/dl|ӷc>)MKI6* VkRu"s{; uYLK:LLEືS v qxǎqok/]L?^_)?'dnF$Ӓr}\L|1VU믿^{P j IDATTP9p[&&5gƒT]&dr.$>K1%ƺ,Ae9L[Դ+U6[\6VU>}aq]OR~ >4wNm/i/Hw>i]$i6qRWKU=)… e+OywjZ55+Mk)wbe~Xڤ/ns/z$/vnRc.zȨ$?9Xhʬx6?]h$ 4id{9p;[.'uy'^Um\~ye/q%aQۜPhK\cMgW72m&enӥj[i6骲ʼ&Q,cH6I4v/(Lys뻬iLMSV_iY..IBWn[Y.r`Y1?I|s$vU @`NZ ɪ&5t?C6S&ͺr?Emk^&MM\U)cUB!푾T›>i7)I>?}5rOﭪ[F6m:j|Uy^<|}QuHY}~)/CMC];r  Ї` @@K$GmG%l9p"w]8_tʺ=Xm$vmO]ֶۜ,_̦a6cuN|]]b{Itu_[]vu_ɣ|?]=>}S9}.Y>_|ߵ}KB@pUe @F Mڌ&v_$r5"]/딲(R֦ u[?x>T_{%ص$ 󱪮_tM.*{ K6~ӒNZ-}7/h'ۥKh\[Jl? MY'>J<ˏe~^짻NcZ? @` !TIG@ ^'h';W5=A5kI'总]R׌,{^{- d%g4ZV)3M[,imy\":166}AI6/le:}A}쏋&DxqQsmJhLɏI6R\sl/ dUI;,eh_*  ts`'@`x.\&1p#1u0ofrguSrnJ3.p#F֕q&O믿~z&9f\q=rP1~e-~?i˶bu6J[; 7O]ӧnZ?/.S+>[,wSC7X5DR&pn*O% 88$$l^>o[R6C#궺  @}ȨƩl׶Q/ @m\e  @ @LL@pb &\ @ @m$hY @ @$'`%@ @ F@e  @ @LL@pb &\ @ @m$hY @ @$'`%@ @ F]m,fΝ[؍7޸v p8gΜ[++ П`J"@l{w;*I',D @ @`tk @ @O@?K% @ @D@ @ @RI @ @F' 8& @ @$T @ @ HID @ @? ,D @ @`tk @ @O@?K% @ @D@ @ @RI @ @F' 8& @ @$T @ @ HID @ @? ,D @ @`tk @ @O@?K% @ @D@ @ @RI @ @F' 8& @ @$T @ @ HID @ @? ,D @ @`tk @ @O@?K% @ @F @xyo=ܳO?={뭷f7pܨ)vv}|ܹه?)i~6{WUw귮^xٯs1h#3?c[C;hNw|?XlS߿u/#!;ڋ DA"oOMncz"&Пu]7/W^٨|\y7*cJ+ _Ûo9'k9H/Ar-զSw3wIMC, ӧOϫo1$Ӷ9j1wUoY~<”}gtHRgYHoڑ^T XcWr v׎)oɁM/C%O]$" y4l6HgqJI,7eW//?|}0+#'֭8?# M[ B.\6`oݫֲok@u3"%i2*<.ajTK ?t" - 8tiq4_Yn{e^F.xNi$y]$X&k%QS|WNx*q~8ZhJ$@[Gl[(92X A\IV-A\_uз^m /unb+۬^vYrJ٥~Dz\ueRN2m*鶪({QFSL}%6xGm]wmS/#[|M>.wl,W\a&lolW>q鳼RVz6z쳼RVʼl_(v},$m:2VU)j^Vߦ4]CN)Sץ/W}XwU8 CiiX< Isy|$JҰ|-/MG`=ok2%RnIXl'f̪)Wb[uߋ6?ϾcΪKRcyޤoYOZ_:g[e*/.ibZ]|Q^_UN?T깸ĕ-U__2T~%X^{iǺx֕Ӫ_S#ur1).~excii딓i]{.[N{6]gy\o66~^loU[emG&(5Y7,ݒ$cyywϲKrg{{"Z@XY@!tKZpxVzϗ[>OE'~{NmVN6jhбgG9>jؕ6?]SV{)SWdyvm9\,`CL-'D/F҆zmʉ\'_delnoeD)m׸6ʡ,~~Kl|_Ԙl/e/:^imCʶ8l\KKb?zcs vK}ysa͹oa;)7Ie;s?J'׿=YgP?W={_tm/^xn1?7<?|xeAFmKul5KWw}>OGl~xo;pKG}@,nLgVol^gK^t2PGF߹)b;J[>%|mOJok=׫/Rw]a{RlJ1]Oyxz<}Vov;bw<5>{}Y뽔S':h/_M\6ʩmeږ#c tUV#1[Rw}cC~9jSGydOM5.~ugoΣ\m+5O<= F>bߖcVQlY+kh[q6U&w׈k>w+:Q鷿Y[xu-1[t<=vyB&w{Ŕ|u:dʩFrwWYӟ^zɓy!??Av)f~Gza/Oؽ%qU`;r~瞘cpBwN 㣀M$tb-w}tMEؖvK> RM6.ߩ+ x=<Ƨ|߹ ?:6̟UمMU^U×|xd+Ws3YpLBJJL^lW_OjĄ~|c[gU:z;Fo}WWo-,%^$tCկ~z?u9S]o[]%Rʶ2N!篕60l~z'38)q~߲3=YI ?ԅ/WM䝳eDŖ1H83F|`!:ҟ#Rl^XtZYy܌%c'mիWVP>B| o]cWG^ u-q=dzpܑBVLtt@}w>wԔnf/g}!wzuR(u k/*.H?˻믿H}5_nf=GNˈ^=Y>|]jd{Go_S_{=ʡw'ڗ'zr#y>ޑ* >GYȫmv,R~V9zWgS|vf6:zD̟UӣÎx?ף'0^2c;Wr񎼚r%U{{p/b~l2çW_W`d^۠GU.#;bgY\O<QO6x}JoLrkĤ\oq'6{Ąa<7y{j%_FIOA>s7hX'jp\-\-kEM[6y2 vL_g` 攭m@zQAW{\'[*7F&uU{: ^'G|U^\ՋǙ9 V~A'1{zӋVR|WfKϿNȨ/qc|ǤT/?ןγmwQmڧ;N=%_VٽH(k{Xձx>ͷVu۲ueD|fMՎ5^#sk{6V9l1C-&qOiSRo:6jCo+k~;_ŖH1fxzm;#Vuf}J>HW)7Z;m3#DzKd=+a2>WF6ZǸVˣתUO _}wQO$y=P^aH-5ZgR-cɏ s%tzUQ5QΣ䟥ӭ6{Eʅiy5oo5J; 9J, (+xm~|2Jԟ@Y^{=(_cׅjBp/3jWޯyԋm~pS ^<ֶHYzWlN֟?b [uv<Y9^ϊ{^'Wu\;zRk[乭i|nZefZ"7p.߫o;8f:ڗ~M8F>s=AdT{mX%3[^hTχ,sNվ^};Wokܫ+76|g7uu[{a\>3Fsm[z~{[7y! T/: 2Yx `k>f *=zA*GW8ؙID[oض{:l//z+ezyV嫋mzak9'ۭgoqѹP:;Uft&_Oٯ^K-ٳa1 ).ޣV٭|mwm%LNIܳ7ZYUʸCcak+LZ]n}q#j7:>)u:^lAme&R6lk޿/E/dkLm+X}Eo5nj̵m{|s=tN <%-u:[܁ jDz$d 2i'mRkn+c)[NaU^eRUW?Wʷx5?z>W9ߋge$fS.gu }G7Y[wgu\\@J\\ҟp>Dʶ?~Oj#3[o5^/h՞UڇQGm+U[k*sf7eoU.7\;6\m橌k_Ry^k85vaCбygyZHrs86[+eW`1,zV+gdQVo_;} %QLK, H` یÒ FݥX.y'^E@Lٯ߷pG|8n:L<b#=ޥu&^߯mԃ<1v o#{`OέrdAs?3Ǟzj?_fOt朼{[++`5QfXsolv.kV_qyz-Wm-1^鶚W;%h*C'["L0и(1Am ~ IDATԧBx甋:i^JKM/%Z$WXjKoNډ6O:䂞sh~8$#>&œgϪ9#OQ>I<.nW.BΩ2艟Hc{ 䋿C8ɹԧfGrprZѧZ9uoOC6|{xSv9Y}[2=mw3?b;E׌\dDp*W8 '2A x.8wz y!B.)ÂB.)3(RޯŎG7lcrP':ā9ЕjO5_jׅ^9Qlj$h+[$}m[eԧϓ,^nO9?k3|T^c'9[8'Л6x>ۣ[!C?UfݒWoH=.n.gHkK{*k^zT9۾|wn7Weyz'i%m%:6l핿Vږh3ўyJ]GγÌ5RWOUcVnnl[e=d3(< 'c\Rcd `.i/~ 6%:b=F.b\ȄSs2kϥ-?$_GeNP׶.rg;suMAy.F==Ng|C.XB\F.~m/?m/1^ef5v"mE +:~ps32{WncUOLf PӞۈQ_[d*6†K15|KR Fg|Ν#o6O$ӗVȶܥ︔-6 14k_?X&.r3N G5֒ߵܜUC33[ S? ~V={Vzc~~[E^(W_^/_>7t}W._~-}ݒ>]G mݽ|襪G5f*/U;:lzmE/M~ 9OwjE't)/^,qBy{i%&S'{1ej\I3V\֘vT^{ϩ pj1&)ͫl7K=ݨ;OM5֩we#>gmaG߶EǴVSr[v}E[3G+uS'me+N6cEF^tJFそ+E/yޓMLٸnGԵeWTfq`k޼7qWLj_o?lRO7/kv9՞Oj5mtVk1컖+|{5vwrj,zlV/wyzz ݽ^]1[\Dͭ>w;/TεDgxO,{tw9 SAfN׋!Fiԋ%.GmߴSnh/4-yEy {㗊؉F}:~>>aG1&KA7CEi#)h=+fct_m=Z K 6ubvيsjԗz]}m [ Ts`4/)[f8WΥ(yу|0kwsO.%xU)ud}~P?"=<202p]J7.xk(xRr메wWwG'~9۶}^  {QcT5cm%+g5+c\ &] rˊ>e[ʯrn.]o l9 xRp뿤K@$  H@%4 H@fV<ǫOab`]{yj$  H@nA[P H@[D:ga/V8s"75')a?!*deaŹ?t2I@82 ȉ񟀜$V}%}7jh$  H@xGO@9'$  H&B$  H@88 z$  H@$  H@C=,+ H@$  H@$ pR= H@$  H@$  !z$  H@$  H@ xp$  H@$  H@pp=J@$  H@$  H\X˂-۸CTsm8 k.B}mvI|<-M裏'GiI ܅Vw1J 7y7# Hl.΂<pmy:Yb]Ih~쫯zN%k,q+;~~^n?'//5@Ks+w\hpÿ_Wƍ^`$ }\8 ^Ȏ"yS.uy$,m?p3K@ H@x/Oy. Y' H@$  H@|p o<-)sE&^} ]ʎs8R[uejߥv^RK@5=eD[ᶲL۹1pm#\[Fr{u;E何.}/.2zq6;vi>w+6ǗN:R>~{_BNl˭*|S7б0'_׏ۏ?wKO^dQZY?oM%=T-g{szV+2:#2?}EvHًC/Z[aclg{2?͏Ng? K^L~n+ߑ,Ѝcgܞع6WuBmcȊlWe8 X;UhkؐX冾nʇ^jc9fSftf;JuJsbsT "7 }z]v5gTw.§gw(l~?T㮗Gxz_2nOd%}%􎜙>iԗbKvLٓQ}q9"b_3;LQȀ9:N'v[YT_>V;fcw8f^Vimh˜=.1l6^"1E"~[-ն{0zzvǭie` V%x3| |?l ?}ëW'C_x)_ӫqT&DZ>?"y /,<fe}}=mZaI}ϟ?o>~OpJI//2|=V lG~6-9[(KuzWXߏDC5J{J5ߩOco۳Vg'w?؋K\Qָ1X7p믿ey=ppWp`'0nW?R]/o/ :[=zر'[ݶDY}ےZUQzxߊx\}mq~Cݎ+r񿖫||ߓ->UY3:l1ؐ2rۇsM+̐3ϪJ,Qh.Kq216&^򽖯#!.=Z[:ߊbйևMk1컖+_~kr]CC'>ӟqאNJ]!y<:v.(췿x)G6U> ЉwO MёK&˗][Zz);ȧ>k/w^x+wkѹ pRspϱj'/~’$es眻MHT]c۰`Oic|5Qti/FdT]7WF9?pX> ?RF1Y[[CCS籗kݪkLjeb-yyz'iWC-ue'8[tXR؍MpkSBp8I-䐗c#w/ɛ/Ɵ9/d?Qބ,VR+!Vs s)x N3>y7Iݰ3O3X6:#cXOY]Nm?so^L`{f## ckm}嚀 L2/UȓZ|_`6㰍Nؔqm,[ \1=m+_$5oR}X)g}oIz;J3'㏏uR(./w>)StsgwG6z[gB^Y[|}e]bme9Slۊ7t OkG&Q;ֽ';jdמ;;ȭYﶷ`ڦQXk߫3XWmJ %'~K9ϖzZ ?=Q0[WmY|'bzT~_~eN=n۸qW#F\QϨNܨ^~#9 ~a R1R4"^jzȫGcg#ckg}bʂ{'v[=~v̶ټk;Y~c_2=U7җlͻY_[YTz}\epN0L iWz}7GyTY<?Kyd<:0K iuĦׅj=WI;?Ʊ[혘H_Ps^ꃶ=\W5kvqgS٭WkmeWۖ#Jo5?$tr.Yܖ}Y1#fzS9>~zg 2+ IDATKtI[v7m{>Tye!ϖeKNߊs:zs+1YcM]&{ԇrU956o$ ?31GziLL&3Vb,m`&~R|Uʌέ85,Ge7)s-OtUVNY;k00;_^iջ;1'jjկ"BF@5ʳu:g~>[Uʖy GۮvWۭyVT;fc%kٜەҪV=-loIIwt jw_e-: S;jdk#yɟ()Ǚ<(]tbcd== FÇI lW&=9Vme=*w1;&wÊ}؀LVZZ"3`Ƈe˯ʯZ5_+KO;̔61yZU.{l&xdO8,H+~'ߥl:&Wl&(1_O+5xl=Նje܍>ٮUDN>q9~]qsOʵ![NCsS3 mi✘9F+y"-&uw^?O#S)eV<6aOĖB9dx:t9eR9m;i#$B?PRwMsb(R兎S&}7-h<1xt VmZVyRrRm[},{ܔm~0؞ߴu]U+}޺h8z|Uy#jΘqJ<8?5%>N-g~ UϩO+[|| *EHֿwX$.уy.ց/f򱕷Wc]_ΓY i ˪j=6&U3j/@\eZ ~'c)Y{=WjjG|gEʞ7i_-n/")؉Gmo~eQli,c4%{q}O9l8>B#< XtZ< kRېڼ::vR/oVi۷n;cc7sF~)r-r⛶+zk[y|G25qk5_井S'?y|YʓzN9Ǡ'N7:.NMu"3DFnS5yꅋ@`lm5fNjO'hژ6xYǩl>UÖ >nJ?mfqy>G/3tVsӷ<3ώ`n|`5WB>ms[:lCzwcY1VZUox?ø^T6S_DϱQ}OލȮSe[maMT=io}&s=K[_[jܝIsv#EKlWc nYLѡd31Qt-{?.BFu,.Ω;ep9-o6:ڶq⢧cms[x4|q7udKYw(W5qA097s궝f fwǣ7i 38nU}Gk_Zvl&#FrY([%H6NGr|ktKN \Ŭm|}WyF;.Hʔv=b :mՋ^}+m%Ǫn9o ?NW]e\N˫WW"7Gz%[ _gCy>i7Ա5)?oXbwdRW;.U;j>ttNL|Ib{XuE'SpVXO~3fg4DIdK#/*űV5:qݐ[㻍kcgįSbYcYq,Lކ@]aߵ,X=i~psk`@` fJ>Qj;#fcL9?1l|bcj|Q>z >{SʰeQ^/&Gur3bV8A^Oږ=_U]zLzucVݩsғ]UzW[cdz<,z ~=VDfۋ6&I駟>{MLWGGgdq)'V}uѳ)F jW}v':Y_XƕY:؊y8DMm77kmY_/c|EP_>3*E<:^X8οxzcɞ<$^]uK?gx#8e]+mȇ ;^ )h}J[Յra8zd%T gĉׅ&5om{&̖WxSdFhK̽lj=61x׊j9W|ݳ=NVh| 6^>EOB^s}==^{ >>k2Gx[_y]g5EX?kp̰>l=1T?Alm1:{1׫'>2Ȧ;'FΨO>tydlVKՕzRȄV_Q_9⃑?n+D| #ce^|!2؎N׏]6w[ tlMU2AT]BSu߹.ڸ )sʾ_+=Xu~oOT9{շWVR~mW'W=z^=cCʖ|YYWjcU:$XL>ÿY+:m7Zc߇O9|~q_ ZLܰ·X $  HXdHbd̞9JdQdkackz:F XP]iB^X_GY zn=-x"f$ b%&515$  H@&n8 Ibs>,է8?;z]x3ƢbcA.@rC43$ px>Q# HTz?@EU$  H-#XxW,ԝE=^+΂_˜|#"D'E{7yzm/$ 🀜 ȉ.'F 'fJ@$p0,d_O};?Ǣ#R'<կ^7VG6 >>% ]>& H@$pO܌ I==nKr]J$  H@$  H@] xWV. H@$  H@$ p|. H@$  H@$ p\$  H@$  H@%u*]$  H@$  H@w%][$  H@$  H@KU$  H@$  H@Jr H@$  H@$  \ t H@$  H@$  ܕ wo$  H@$  H@.W$  H@$  H@+%  H@$  H@$p].^%  H@$  H@$pW.K@$  H@$  H\._K@$  H@$  H\+~+$  H@$  H@u x]J$  H@$  H@] xWV. H@$  H@$ p|. H@$  H@$ p\$  H@$  H@%u*]$  H@$  H@w%][$  H@$  H@KU$  H@$  H@Jr H@$  H@$  \ t H@$  H@$  ܕ wo$  H@$  H@.W$  H@$  H@+%  H@$  H@$p].^%  H@$  H@$pW.K@$  H@$  H\._K@$  H@$  H\+~+$  H@$  H@u x]J$  H@$  H@] xWV. H@$  H@$ p|. H@$  H@$ p\$  H@$  H@%u*]$  H@$  H@w%][$  H@$  H@KU$  H@$  H@Jr H@$  H@$  \ t H@$  H@$  ܕ wo$  H@$  H@.W$  H@$  H@+%  H@$  H@$p].^%  H@$  H@$pW.K@$  H@$  H\._K@$  H@$  H\+~+$  H@$  H@u x]J$  H@$  H@] xWV. H@$  H@$ p|. H@$  H@$ p\$  H@$  H@%u*]$  H@$  H@w%][$  H@$  H@KU$  H@$  H@Jr H@$  H@$  \ t H@$  H@$  ܕ wo$  H@$  H@.W$  H@$  H@+%  H@$  H@$p].^%  H@$  H@$pW.K@$  H@$  H|nJ$  H@$  H@E'Ez%  H@$  H@$p.UH@$  H@$  H^\y땀$  H@$  H@ ׿'~#/~$  H@$  H@,c },- H@$  H@$ CpQ9 H@$  H@$  #>~$  H@$  H@ xh$  H@$  H@pp?KK@$  H@$  H\<{TN$  H@$  H@%  H@$  AIDATH@$ph.=*' H@$  H@$ }\$  H@$  H@84$  H@$  H@>.gi H@$  H@$   vI@$  H@$  H`$  H@$  H@MCG$  H@$  H@$ YZ$  H@$  H@&ݣr$  H@$  H@G},- H@$  H@$ CpQ9 H@$  H@$  #>~$  H@$  H@ xh$  H@$  H@pp?KK@$  H@$  H\<{TN$  H@$  H@%  H@$  H@$ph.=*' H@$  H@$ }\$  H@$  H@84$  H@$  H@>.gi H@$  H@$   vI@$  H@$  H`$  H@$  H@MCG$  H@$  H@$ YZ$  H@$  H@&ݣr$  H@$  H@G},- H@$  H@$ CpQ9 H@$  H@$  #>~$  H@$  H@ xh$  H@$  H@pp?KK@$  H@$  H\<{TN$  H@$  H@%  H@$  H@$ph.=*' H@$  H@$ }\$  H@$  H@84$  H@$  H@>.gi H@$  H@$   vI@$  H@$  H`$  H@$  H@MCG$  H@$  H@$ YZ$  H@$  H@&ݣr$  H@$  H@G},- H@$  H@$ CpQ9 H@$  H@$  #>~$  H@$  H@ xh$  H@$  H@pp?KK@$  H@$  H\<{TN$  H@$  H@%  H@$  H@$ph.=*' H@$  H@$ }\$  H@$  H@84$  H@$  H@>.gi H@$  H@$   vI@$  H@$  H`$  H@$  H@MCG$  H@$  H@$ YZ$  H@$  H@&ݣr$  H@$  H@G},- H@$  H@$ CpQ9 H@$  H@$  #>~$  H@$  H@ xh$  H@$  H@pp?KK@$  H@$  H\<{TN$  H@$  H@%  H@$  H@$ph.=*' H@$  H@$ }\$  H@$  H@84$  H@$  H@>.gi H@$  H@$  ޮIc y1IENDB`1crSDSD00crSDOfiT#Fil(/Hm/Q@RʒćW||vy /@\k՟o)Ye*=h!+/OB8RJc6#"|jS>*pa«*M})BkGR~C8gnzKjb"~>mعdܕ; I~9=sor1yB=KƯ "(e:c$5u!f!aF̰ULb(=2y*]X$&;fW81SfV#s 3AP"Eq%RU3q_2&'p12*@I~$Rl L'򍵢%1Js熟&x%p.,02H&h\꙾KEaLoK3DdhhA8+ $>npܣDqsMHy HPCtg y!4`?!-%M"Bļ+ZjYSqÑUI-pB_?FJ &T0\T(*r_ %)K7ׂ!1lK*9Q#`&"׀ØKˆI(<04NyrDMrPS۲劗'mGaHA^Fq=zzߗk<{ȊFT}|HA) +Aw ,\8)IJe\Tw¹S`- 'eǥ-t zЏ?TAՏ?\ Abk &Q 153Qi)N:H˪Қ4F'fZ߇ kA+odLeH;(+!JҚ#ȰXbhY}jؒiN3Ӳޣ]\D?eUnךB4Kw e (̕ȊٚU {@P~Ι&PI&ZA;]؏ӁZ_t$NzV;嶚U+}#8܇ibϝ^אbbjb(;Ѷ->V++c{erτ8Ѽ'N !x%9vq,ۤ!oQͧe9#}~]JC|~ҐB qC>,fnZ66/v3_|~t spP 0Mg:?c2^#IzL۽2^/W4R(/^t_C=d0Tcç~>2kW PRW;G|_C}vy8[_O!|P(D-wTݿъ~ 5`$|!r wU(dž5o>y&̠$]aXԿRm,:xD83-̑{ÚOYluXQ+CV$ `̧ B@s,P&xSninT7RwiL[ "XҁS$܈,{R-HI6$AI7IDE+_мS*2Xmk'FER)^`[HsYCK]-Y*d$ H.ܔXt ħ Aۖ?A3ź \4Ecյgd))qi#*|X42FcKsB{A{274bE#m0QP}^Y8}Gjox ~("]I\đ^,c߬6GwiA]Z8 ^]ZmWf#WփnW\?R@pk~gN=e'#Fldit,?[u[bD>BG27\zu^>b6y= N7*VRe #BռT7V5֣^ iysVc. d ۮ(:Սeߗ XpPBGlT7f,уIJ<"'X2U1"_Ej]zmwܵz%.$"k;E}]BԼc!~v G_x#SoIz}BtWP8 'HԉFHutݬZ8(ϖ#Rƌ1ybUȋ9RvP%%nh#:7$/]=j.؉>(Hv,E鄬Vx|YU#eԼaB4BCt`n"%.G o^Υ<{M߳\t 3Yg}(bcXѪIFj8ۯHG2 X|Q⦬ H!ex>MUe)tQ\4y"Zp?,)c^]'J;! GӤnkE?0~:9ؼkB)z[u.T6@wi,4Jh2UZ] ;y9iO0ʎMoTwiEsJh%rWw.QIuKR $(w(RLЃ>ߴw(7vГdUsBdY5SQmE̓@G6W <5c6f⦨k ժ wNUۊ_M"u|bB6!jEPwiA]Zs T$u#2H  K`(%I( !cP'6 Ref^Kx/Gڹ"]>Goq4M& ƞ鎂CQqh1a9$?7t!n興^xp;ZH;rAqe4,j-F ‰i:} (SXv3=Emu4jwe]+O8I8R}hYP|E ݂:}RB'X&|VMD;mfG,-l$09/rdW8xp:];ScT'~38RM` 3?/9X3h505ߘղFn)?̀60@~CcI{k@7FGwQZ $,wO"Hy I7PS뼂QRo}@tf>>Խ^뎒ޖjU>1~'ه"5:Y}=i]x5>&X rFgl޼B za*Tqu9|`LD^`A[9\FbMw9#?RP0y_ 2~ZZ߻tfCRf$ f3` 01h/! 2 `\cأWP:_L{4YCe 2pݍ aJkL @-t_5r} +@"f%lyqp.&: Mnm6K6@N (>(39Ȁ_l= q9,lW p+V6ލ+aϠ+{eGkLUAQWFaOP8'.Un@H (3P I /`[wqz%I\%AՈfj-1{IPġ ( K81kANr  ##j&jpyb}M8pp8 k[3#Fil(/H%j]HZ@HYW@?oXln(Q^4}Imeb0\ڑc|ޱ+~y 6EŇ`Vs5/FY#I·ѯ"u*< zrDMkNLXKO-ooFcA:J¸(Ȓ>$^n\a\`vc[gWpSu t۲&ſ3+B;35[, P@3M +Rhg s+TAh߿k>*}po۠_U?a'BIҐv~/MqE ԥl-V74GjHeﱐQܗ'י::Z.oNc;;+}~̦ά3r;:S]wS儖x>!dUJ;]W*[m{Zٙ}C8V}aϗLzdTAE|R$m[aEw cŒpG`Ğ1ccc8%!% SʞIN{zˀ!=0ZaH&(%Z̸`DLD2*J, @f$AT%$Ђ0(8p4eU '%c+l0@u`̕8FJf3gP Q@ j@1-bp9yda @T6iq06?1"IXk7*Hy jLY 9`< 8x!)-XrBk4v ;n0 O$b(p <'tЁ #cnftA p10@Yt6+(C!33e%5pI4j0@xXc(Ne AEqiN 4 X 7IpA I/p8" afVc*! k p¸%lX ܀]0D#S2){3fq|2D 1 IAAX({uD=8#LD貰10bZ!g&e7*h(6mn`rB+`q 17e#[L)h&~vYlZ =t$5UZ@) t0…8F|A1'X tYjt4`Ϭ`dI<@nlj b& (D%*\Mrp\;Y rϒ ?BIז :h)d-  d "~ ˎ2If /#d&RQBRbE;ʼ^Z݌V1G a?)cA RBxB-S +ӡ(kv0[(L+8"wCXazocNdB&^<R(50oD1~2"ٮFRx.d(3qxE*ŝ79luDR^frVeTR}ypmG8g6/B7mEOWEkh,.E͵X1/1ZgGtW[gO_$ 'U!\Ԃ uҸW)~1CpbBSiKf\inMcAzx~p j?E`YGۿ]3p]< ""-\TYz2% jJKEs,;~o]^w/̀zK{ا=z3:?+FԂtv IqŒ}i0r2N LmRsKNQW3-4uh~p/DV-oK*qBoknk\'oz >]n4#EU %Ύ ֐6-Sn HlgrFgOU&!dHvMp]FwۉR"[}#f)"ѣ(}GTs_wn%#)oFv,/5:%|T.%ۀ5. R'5:?X5ZɼG|I*r\ 'Cn`3DS合2SeCodU`B6~:rR}Ѐ"@PWk?س%?#d"4F-LjEW#7Qﯡ#[Ad0, 5{2bPE@Q(>% 2cd&At"ƨBB?E,t-+߮u_1((Yo28/ "8K/?Ȕ{I^;$-!ÏKquO[RⅴăfHChm#}'$d7XEeI~,U~81 folcEbʠ}2YLLuN .9ĈD-y] 6Zr3%ױm*|IGE ITA4 N۶_^?Jd\A5V!yo¸ EsSZa^5,ti[S5!T`1zU!ˆ0}"+ѳ=GB %Y 5TĊѫ735nJ>hk;͕N}?}=ר$u&JћS817??ޏaN7X\E**i ߈?+ɥ/?3LTXKO#T[0u3t$^FE`ᘹ7ld6E)F2mb=5]%i>~R#vl):4>3P g:=3*xXA \9Ua|'@[l\9N4-#F2lqxpJm uU2:oF gBjC~oY"k qAPh㜾=y^BWԼO(xAM:hӲkY(}M׹R"KE虒PMʌf`nD=uZ~_J~Bo ]3$m6@rg!gh]gK I&u)9fxA81ܩ'ʂ?&DckK$<@d,Mʌ~=mK&v6Q[_ u-bE0l~ 1 wM^YKS9M-B޶B,ޒ["ž_̄V,4J@5ykyAPrOiY0/B&NKi6}߱& (N2j1!!45ˎGJTK ^_TUĉd }I`^cPl+Y7DU$sa#bkrl}})]GL$݈_YPovLH5Bb0i*OW,Ae0"vaPɂ`8N+ aɩ`Z2=w(\t+lg:r'stRwMC"-`X 3XXdH` }`z~ed 々g y_@P{vmޞ-@Jg90̛ x]K'ҋCVQ0 %+B>t Y!r,(80VdNƑϨ*RȈȔ I A! qX8!C@Q5TUY8``b h 'qn|pZQ C1#)i:V#9iVSEz̨dݍsH#UAEɰB("(]poM¢`W3m%p2z ȃo&9@Y8 O]cOWډ"9`Y8OBܓab?z/ܲ4@8$;k} \88۶Նx- Ds K.Ho~_gXyFYn ?Z(mIb~e:Pa KihksqסX]Vz@5ǵ1IS4=WmT$3b5tV*02S667#qti4;ٹrFUR TwZ' vGrr9 H:`AПZsw?Jb+:p6 vm`24~0wr=="^QCsF'v*,SЦM.vW-GW.Cge{gQٺ*/ ;Yy6V<|]w'GmE{!!EIUB&9osAAaIo7+!"(f^RÑo1 K^zQ(T$KTL4H0qRB5JbrF9׆#T#_ -D<@T^4v1]z@܊<`)$<&R30cPOF ¡(Ly̥U^LבqB  L#ĪbɐAOuk8h s+pGzuTVB f NɀT 5UBtEȴEȴ٢g9OQw|@89Li.QKqQFZKc@x`a!MPf,i. dSaKqӕ| 'L #Fil(/H@O8 [m,J\B%T)XBJR*KRP U P` U *J,JA%T)XB%T)*KR*P` U s8?6m| s6y$#Fil(/H@OP?}#Fil(/HxOP7@`>)eLWj&A#Fil(/H0QPiI{7T+ClG BD\39址w8(MIB(p37_Xx 'oTOhՀ鿌,%0X&C} 2#&v"F}#~ *ީiW{ݽRu&_IelWЯ71y񲊺ZS5&7SJ32V󩐿) z* Tm:3RlʻNkkkEWoP{NwJ ETLKSy"ޟd^kek^P?{{Z#H=t?eHwBamTSqAUqq7 [a ;pjU a)Ƴ/+qnC6XH|%H%|(p<g#FDN:Pb!`C?zVzCg wv &Pg_,F ه*A-urC"!x&3cڇ)B 'qC6x`rLpst) tZUt4z?(\ ]}yy/BP;,prčen?SH`Tx̂:8*ѱI/JB&2DrcY 'KxYP~T|c1{i~XTP9{σF\diQzC K@RtwH"49 AJ *o "*1T "՝iN"W%::bLքu9ҭz#H );PMO[߲r(RV}?]֣4H-m!]7<- ^ƸS[)OeͿ-A^ctѧeԖD -V uV9sm ݲ?1uXޕ=8 '2>ʕ+S#W pz&QGM1);y3P?Noś=m`Þ {[ת&iFOa7K;,&! G` [bb\LnV6l8Wb;5;8Q^$V+5!a{)ۣ/O/h":S9?ٿҍWt㳊x7 g%xX6n oz'0Mc)rW^]tTW2_%Ja >&1O5z?Kf2E]{=%3-Q5[b,STH{J B8(P!]I)ڶ5UTQd<4և {6VfDη q0k6RRJjq_"<*|uؙ6J4"}Wo'{Bg?j׎]"oxU-JrEVR*-^茑^^/7 c.U- #RZ">]Ah胀DZæ=I┩ ֦DkXqp1혺g?-yba BD2)IINbEz3¡4`TQ{!-;lAZ Jv$#FS+bZ: BOId[:A(QD&L nb2FNDXP thg YYs@b̈nϦ':jѴ] BuRXN=lrB.M@5Zwkzmw޵**=ɗn1lZ`_ Dׅp~R܅'>i7 D胃z0]8A oZ`RD/kUjk15aPQ Ҁ\Kxe%G(|b`Jg4acDUX@!<4B5.B1d ̘p(;⅚ W 7(JXm jy޹]>qZ4PՊuGuk<}rlHs4umL>dirV2Vߠ٨|-q Q+@<ݼj@`mxFslC. 1B)A3Ӱ!S|8:ryp_-yF^n^aU JS E;Q|~v:~AMV$ЏuU7,f8H<d9ȴb. 7 n(ʟgJIEoA5N.·LB]EF[C#+V'YO"7ؑ+&!4kup!,[{Y.V [6_.=jH{9#K*}w]8% F oiuAC#x=Ynᜒ3JN]~-( \*p@BbK C6kMɀ عY| ; & UZ[<KȖIbba%0{ir/wX#M5Q#k ~mZN@td%q3լ~y 9_Bc l FQk4G8Rh|{l~B?/=G90@Dq"]TԭI V-98=hUiSZ!kE0݁J# I~ @Z*h<+#mA#{ּ~pg=c%WH D\4*3)K؁$kA:$H^30#mC<cΉsiaF~(J?.:*p0)A (36Խ"8hCmpoqng$N&)\F HoaZ^Bsp7g"u~r;0S|>( @ ȖOdk08M% lUĖHk ȰHi(']u$:R"Nb(YH@GgB vh)Vs6Wm3ILx/Gql9v gzb|ثlRF+etI~QAxA0@+QHyzÜH +jmxIy%o8i4xHϼ'? (x3(r` lkne) ~;sJ fH-᷍6ǿd q#Fil(/HJ\HZPHQW@'C P3\%.P M˽@BBgifF3rRXxb3܉JWAPF4 _WpLhK^qU5N !RzQ>n,UkHETDQh2@z+BkP q_!rgD6VMghWY **[t۲GVWep˦>K`KmŃZw4u†-9q|I)M %2D4y%E,˻vh}"7wuRK\ X%w!swK[fK8Q梅zqhnkc rW+Œnj-g>|ond\V[YznfRƦ}ljGRQfDc~9C:dwJ8ۚs r3wNA7rGs o(\HSXmop@ H332l" a`Q-[T2͂sQBF̉0AŒ?cJXs@**[D z8-@:F"\tBh,Ą# ,@)b| x/Ƅ#ȱaT 9a)Pط!)0O,`l,xlwb犱i.p92?h34Jb[hr# OCE hمx3&4`  2eA f-h Lg$f, 0.Q\@153 % .%!(4H!Pԇ1) 2``@ǔQTL *@%XjG#p%cH !̀!%` OgTK20TlBTRF~01T FSb 1[TvAr3a8?%hbM~82^+!Aj``?US֛aBJ<&iFs#3fW"L)!,"8iXe/d|ٻmhd{i1 C-1<.3EB8 ƣyL䎰Ül32],U$"qU"b{tU_H4wÄt3eQJғuK-rbP<-=4au=,/8h[+)^ڠ qB5ddʍՋj(JIM+M8ygX=|Y6Jf"Im;͕&V(>+Pi0jhbDaTyZ7˖Pf,?H\yI" :]c($R`SASc^uO.͙Ls^dZ5s|^4htY%+dzw>l{wYK,d{ANeVKw:=%f!zrޫ)!6z[ ՂDH1G|[Nc}|qP^#RpA6Z3Z$,XZçᾪ":ܣ8lq,vȟ(*>?S;mKБmXSk.ٝ넚] UNV֬otQ^nnt*դR$aqk)!b.P*6;lqRdr-X}:rt~0%#ц*^<]WJBa* ]=ћ1Z=ϺdE_7E&eԧ \EDe B;\;"r I?k  78qe$FQMcPe~ E[[>&I,Z٨7juUWKu'jdC8jk 3rVѩ斆/ ]DV10I"q#BbuE `gDC:XׇL+=1|$Ek4Kx׺b"\C}2K0ΖF|ʸZ]yRr d8rgL)1},ɐR|:cpX9T袊.iPG  ⳏcIbE[ ikIV5~˲9"> ߳Yj,9P <5z46 Uh1q_D{ 'T' on-tf禚MX`0>[LR֓BSUg0@3!M@|XY)Y?. YTB" (12LEDžiwx"9ݿ{p&_~+h2,vX{aV{Y,Ʊ Ǥ˱3=+1*8=fq? bpǰHE` {rс#tڪ$j~T'蟢%DLڡ /#G-VND~ }LO2EK跪1ן- shU`xiƅ#Ĝ']!a,3?zF~j ʸJL0a.9O>l%ysJ/x{+fW%;M*$AFm,%w]7j$m{: z+wmHlEAD!Y$a4;R[SE}C=hdֶ7Z3CT3-XW24ro(==i k&t\DoG rJAճ ֕ MeL*dSEH\4]TAUR JWx.[c{HQ3 8IE.5F}Hzw͚QyH:>Cla|v;^@?e,>'mx@͚a( ~nNE.jRyd8٬`T9Ųi mpRi(uN/6 %H푌\w+A~X:R)⪧8ju&vVX$&{8HUA/&pЪc=Kbdk봦MWRU1 ^j 8 (,]1|*@ <X&%@St7C8@7}LP2 IȲ)}ɍ)X=HLk=17S>lb;*XYy~l(&l%#h24'/ &ݓ"=[huWv{fj AUmۍy,-jvQ BbQJ#鿲y6a,E+"o I S&0Aß%pL>RɱEe5U */)q_T?Eb0:#@HZD<1E$L*"ġg8xVy2:\٪nZ'yB(V8WYMH 8).\Bp҇5sE},| W)n@z56U2c_O|D ~o؏=_݉pP+0-dۂv>N2!'%c'wʂ5B+[pŪ8`Ҕl0m'3xހ>w锨ڽ#/`2-st7l+ 6д6KQ_a!\ʣrLZOw@+oJ)Iɂz(*gҐ,dւ l%7V日/ftrC2- T<ΨbJLUK]@ u$`B@<adü-,*U] dm+:FUP^Xx$Z7pIF u@3}:GzTåt|hhdGW>%M@koSd@J޵ʈ&+N3*P<[[ y/Og"+n$0=UV^V@3/ꁗlMIL3+?2 0a?i<@%8= mxS G`Б eh |ϞP2Á)` 1/"JDBރחp +(8/Pkz˟ L<v(E \ĻuH[~ ݅dug b*9ݗsB vU5@pfb*qj #ш-D9atB3nL  [AtOpj1tF{7\U6Itk.'H?!Nx-Ped)ܠGPNCo` b+ѧQõ,TN(P\0y-G8Ј-a{ B&+xv+!ު7Oc_VkcN;aaKpv_j>ҽbFGi`׊9 0¿ϔ+jInD;)g(IT\l@!\tb g@IA7u=a(s%˱,X pdmFk<&8aưQ7(w1\] hpIЏ QFʓ[X;LolB{*n<U-YOAo|W("L =8%BnW261@[Àn0 0q_Q wm(eP i"xqOt9ze|܆39ܔp1N4,sH6:+ >n…:>w^QfEEo\laFԄ(8,Če ͍bjo͏y-م.l(Rw:m칤V3HVpgߒ"a!yY-)$yb,O^ ӹ^/.0#Fil(/H=cH.  l$l fJxs;Q{#W9{;cd+b9XMQ/[EbStzj@% A?~]R);Ƥ1D׊ aIL-n>/l:k5VI1nH -zs=)I,P4(9/jANzѢY! ((oiQFT$e M :Z,LںVh U.IhDe&+\Ѿ*l04H f_é{Ni  ,A1JPN'>ZfN׎4}0)HфnXc7YR{3n>e1t~!K+Kcl@jD'i#R"=3N+]^ ],׳ٛ ĔeW2.\1`ƴXL"rנ D{SL:jc+sųd> f< NɀT 5UBtEȴEȴ٢g9OQw|@89Li.QKqQFZKc@x`a!MPf,i. :O-eOWJa&S$3q,#Fil(/Hh1 1 ?x ,>K7 ՇS@$#Fil(/H@ P?}#Fil(/H 1'ҀL Š Jm`a7Hq@#Fil(/HM0Q@Pʒ [.|#'y| y, 4+97AXDSלNVbN|lXSjVS{VLeiJ}"vw[σ}⣠voshU+zioT{PC05i㍒z"-%o|T{/kB+>md[}U/"+$魎;Rg6יw]]N֖|#řo*ZF\zC5WVļ~0Ɖ d (B d6򠡦A-:%吨I@@5 Id;_?vJ_lL@A;21JMc^wm`Y7ޓ>q2pgh"~ x~,z!2K?~˅^ hp$JJYJl:arYi nG%odѡ7X J]$)#$FœL :xT@j8Y#!2 ]@?)XDr(vL@˞) aEk 1}+zʢ:BNTz&xmDLe@`JA'! (1$Pt^11(*D 8-)DfL`s2$ZoB)18F8$/pS2r@WB.D &OˆY.ǐ@=&B  n6+&,Y }w,Kr`= DWJ22C8g5)8ymt` }J!Q;HqB槨x&lhX·1`p'- HNDT[h!,,F'>Wc~|SEΕ(c 5W.}֓> rj$>JU ;PT4QhʌQ|>d[֔ܒڛlМ?]@G%[.S9KJXg|Ob3\qǹTRR?8d^Nyk^?XeOy$tǚi6Id yp4^̨JKjG[.qQH[7A#;l0C&HN&^ Cٿy*jz֮GRמnרP,߭|)?q 'U ?MVǥ$M]XT]\_NO&zju柆zT/QPMaG4AAu$#uow ?@w!d^7&˄IN,衞>heat{S<ljĬ#~shY!h*>_,"+PW @N0M!'O!bFe췱Á TLo| (ou|+L@yl*̓=ou&#I3Uy?btY/5wT~n!m}sHi=boZ'fupW)>}vtǚSTG&?Z{\1wI\QΈ1P6~G5vFK:; f7ꐰd&PG– Ďhd 5(=IHU֜Q"yP|<Jԥ<1MzDj[m2:ӍNsR b.G>)RrfOeˑDrGą۶vlT-!!B>υې(N_zk[ l KWSS(?c͗hisޢʞ?>3:@ DfCs9f~D*xxdO= %4?f[[ +:U֎KӉ>V.CZ$4m{>D~j8jĿ_YNɶPޔckU`t?g6H\O!N로琖l DLj' ?U@|3$~մDI>|~'lv_Ep~e9z gXra4 n#:O!Nu^Cg1YN$6}\ $͊~W}ձtCB2̜/5zځCMtdK)$CZ;:!-5)͚I?bu42i|Yiv¤|o)[>d8QlQ o ZewY^:ƗzDa Oet_5-{uc}(UOf9r$n O2}I)F]FF|gnOYnDB]JרG$B^*2oѿ,GAh?5X+hTHRKӾX 7B2*虜1SMEHUhhldΝ1@#i5.FTR8i^Դ;DFY q iLHn:РNDk b2*OWNË O&3m?˂PPx"f6}U'|93 Wƭ 5|*ՍK>"釺oǃ7+]ÌӤLqE+@6i ]uT7,\= HͅNF&6O 'ZL{ewWF6#S۔ONT'EPvh jZ0YR?W˸!5NpZō{CZ3Fi{rsLI!fy =M`t!Y .j. åhc琖k OiSYJA -É_5$WPۢ.[bDM/fB>8t-V0VSo\rk^tiY&0HJ1Jtm{j4 [e9K1 .G͘9GX p"& 3H#" C(Ic 0 F ZH. 3<*۟(g vd8pTBhV6^ &H pC@iZ; A;K@"OP@|Q v!Ca:|r%i"~ Gok:?J:ZBU v./@4;ZZ 2C]xxUr[:[Y^{UZH;vs@2IhZ'TY)"D]jIJճZY G\lO'Mky$/Mu<<cyܘ?ߺNqȀoj^GըBC@1KHk@Ȓ^ >JoY#i E]MW;U 8fOu8d(&׃7 T`j`-X`SOaJ@TQ}j T66dk Ƴ5y k7k/q]||(v 1pTE'm@itf#H;vEwɴLh.aFEd4\xtT2VJE}T24ꀙ+0+Lμ<~@ST^9r;`?hΨ}KG["BBےFn)1 zCNԝ+w gqߒ>w4xX|+vڡ;99wH3&a'0 '8lrcwSOV$=p:(B;Pf Ǻ)C 8I) ˀ VfKC#3__\eHQ?ǁΏq2Yh]" ajĚ)1ӡXx)|7; `9HH N1x_ N%HaX4B,8] xUB4u:v@5_Qed_ R>%S ݪK;5k} ;`1p P(Kp6=WZ3)zWsP7; EKd(i&hӼm+ &NB#Fil(/H5J^eIV + |P(DI0d/ַ4ʭh@ۈYE=+_! 5[%Oh62D1D'>Yx9d/Rr^N 1$]<#q~GCĩ}EHtՕvx,۲OdWitH?PZ6bwEDpv/uŢWgtҩ "⻵є*iE\ S&ßo#.1E"7]ƥ黵fKT|$wk[v[#O-Lc M $]&宽zIq;K[P5ߝ{mvժЭWSz;WݎA(Y2ˆ[))+i俋E8}ok4;zD~S./|tG+"*q6?T>Ոe6g̙TJ3-Q2ۓ)f:1 abf &A !HXUcBjacV5@F0t2N\P@J|,̋ɂ 1P?@#)=`~mf3k8"j%"F6#s̜ 04llIPUq/reGf[gO^\ JWq%ZL:ESQUcz\kɧǥSE P*^NU?(7,8׎e M,p WR-n*?qjCu sj EDoEH/CZ.4o.0zZQjtxt Y%kA9 ͊f"KIdT;rP rL"ENyt\qE:(Ukb؋"ۙ^dqE:ӆ=PYA`H] ҡ'.$]NA`!zaU\R]ABhE#p |~HhR$jE>UQOgeh W ޚKrQծߪNw4UŪ9 cshT[a> rrY}_Nɿ9P{(=-șAEZ.Yr8eNǤ$(dEwh0']0'/Bћ8&0ϻNVV @ohO rۿ]:ܰ 6Ѫak4PJ$}lQ vG@#a ǣW}C5.n(wUWb4- Գsa5M$P"zUD:'TH<0m{ix*B8j}lABi& +8VkܫCt!&0I_Cgݣi< U75nTb "y$Hdj!I?XՍ`_JbWsEDHL!e[B\I(?coms"A$CxR.WQ.$ a¨R[%g|:/np{O)tEZ}W-0tŞWӶ6GudcnCqv|V{t'E㇮jc5u~t)_ UDG9q\zr5?UyȤ@p*:q ьӲGԐuEDE9$j-nX>U"3P wI [ZFȖ<׌tbH/K9BB"PQoP\K#*?9"d;RaN+Z{V(7$å5D"5 ⭄!W͖h2z\fl=s\b1хIYtBdmJ-'8<\4E5MFo+4梸U dZݽբKUVWZuŹԢ_}Vn>֎W<';AMIqL%|G(b$ M6I:x:KP HBנdoQn?]th9J/U quqՏc)N& ,nlKLyg(U|O¥xRnR9'T(3BOJ\2<~%MIƇ_('ڡ4AbDYY+ZοlSJ i1!9&EO%hFu2^(>%I3_T1}N\V4jHT~XL[[T=VE"ѿv 2" >̄?ٳ&V`WBsiB@Ab2_cA0͆Qi7_(kI(jב ]Yɪt[NQ[[N}&#g=8DǿBUȬ.[~ud?M s¸P~Rgڱ؃|X)/b@.J;:1ˈ` 8v; ePƆ,rFGvu"تCsч8.eqiKщS(z`5ѲĨd|uiTP.ٝz4s^)KwҹL%[()r6ݭUEE+)(ܚ;R#lHRĩB+~}X^$"6ivkۜKRv.FEiOGR7=td!}v0[dbڇ1RȉST>Y]$KnWN IX4EŠ%RCQ7cUiO4hK)FmBE/" `=5mRq"gِALxhlp4 DHV!fPTDX0TP#k9 P6 ZtoUMJ X]B3B۔㇋}^+' P4j,s:(7E{0K9vV=KfӒL[BYՁڵQ‘N/:M,~U!K@lU3B/0ro4~ ֪D Fb6hY7V=0"{n! ?Z f }]L@x;Ip@&Tǎ4e,ɲ)3%LӊHޒNTg0SϹ*1RTy290+l9>>,#iɖi|yc?M+sd3¡ kضG8]օRGmjv ā+(1ɞ,=qf(jI{LJUE{cfP>Lyc(^hekr$ jZu2͆TF? bS>H=™_E"Ǟ0= 7B#=i>8.T5v#ꑎ „!\J tF5PҁL[iVpj%"EӚ:M-~a-YQ,tmRxڍG?3JvuPM^@U~ϏG+KEݝvud痝NJ8Y梏Չ=F7-G /(AC}Qe;N`| ݊FDYGJ½Cy%.*4g ;%F$a`x4dy%*>0xpw\t>˔k.2T(bYML;xmv%Âr`Ųz4~ \ 4Xߛ Ǩhj'@ O&xTݐCN1y@^R歄m_X9 ?⑌ @~N|02pN|/O& MzpGX=2F,B9UיִL& q: /jƳ')=2W.!*oCp"` -,Xm 3 `[r 5tmu%l ?*,k*r5T8 ZJ ȥ:9s˶{S=LԵtUlf7qq!Y0}r)@yT1RA肨d5S,/{,3%2z Mxz:`dnܼz ހI6p?n])$?ĉ$/5DcaZaIP4`™N)L}D`P3\X+%kjGv4* *#e/eY K[SpN "1GP^kLe"MHl7 c>~ÙuX=ÙMG#\9߸h`Fu 52q x9 XpxcзbhK_#gJ!x,(: B+ T8C8{:@U'^"NB*2'B<*Q+3A:``:f2- v}1p>d8<t1Ǯzu@|\ (.L` !E l yP<hˁP}<2џoOT`hfCs@ũ,N)ڜE<Yg.ARim m!j=8BjjUDc87_Ɋcabì.!)NP_S T֬R0e SꨪŎ 뢉Ld m,ZhZk14Qa-R~@q!hQs־t#Fil(/H%H R;jHR6\lFsq 7*+yŠɐ8AC+"mz2ڳ Nuzszi$9^f +z [J-j>z|/- FH vNIJ=[dvD!JMnZL 73EFFLHҡ!My4=K[saJf U]|TuFvrmhYLPಬ j&) *?EPoΌ&edzp:Qo/4LdLI ;f*UV? U$Qvj%7v| O!Qk(c[کxG֌\izaL@_trF}> f< NɀT 5UBtEȴEȴ٢g9OQw|@89Li.QKqQFZKc@x`a!MPf,i. :O-eOWJa&S$3q,#Fil(/Hh<-< ?x ,>K7 ՇS@$#Fil(/H@-P?}#Fil(/H-,<'ҀL Š Jm`a7Hq@#Fil(/H0} RAw #Fil(/H} ?,#Fil(/H0?С,l RS#Fil(/H* EB7l;Yy"vëU@ N v*ё:C0(H#?!5iXzddMS( pr1jlKhe8=D3n!J -&mZ0noZSщ{}4ԛ(faKJv PoUcnw$4W$b\(EOb>bӈ5y%KgzVR߱cCtEac|f(2 )?惣D$jw-Q_n_tB/ `6M~L Tisɒ!0rC$S_Cmm)d7>Scf~&SI$ipf>:FLIYj\5%B(>"*҇TapE;]KAs|n鍸+tuF kg Ҩ6#e4Hłl%:b쫯yi-o#FL?,n R.39uɈ6g%{3PmNc>5#3PDS,|%01Ei$-ORX#ȼ>ʋ[&=u^?rF76gEEUмBRJ ο190E]~pؽ Tyj.DmN2 S.oaU&0_5Kߺ;x2sҼEaA"muE7m5\e}'`ۥ04Q)p,2JKU Sn[skxx }4Ipir??!#QmDos2 ;{ M<]zxܮ1 U<^@E]\FP53vm{ϕ62[yiѹ֒*|HAя1%8;/`[D jUz3 ʑ@ 96D,^Gf p&bm^; B˿ 1)5O$w?)֜>Rl~j23D"3&-D8ByPAKIUn2%iU!} :(䮫H*X< kYjĕ*KF`G5z"b4Nb[-Sި´-D*(eBucV 9zV績O#ĺ8}Eb}ZI1PxlBQ@`*,$z*>C[Pf膗(tiș.SjZ܆)H}EH8gmOĊ+OltLiHtVa,vRoZVZ' vTA**fyiM^(n@c/ &_d,Ҿehr\-MǛNyEMc6R|% 8ۙSssm3R/ X.uPؘVT8<3zse_LV ݨu5!GYi<:4 bA@W R[Iɱ6Q ݟ,6{`/ŭ7YF,2rUOe3U.wbvin6)"nY 'TD$7 >{dPj$s 1։PLGsQ)ƣ,:8<|7A\̕}̰Ѹd+dlAq6Z>,IbwXQcAJ&tcd^lRa7<:22%R]F@D 0v{H|" 1H)Tr_`H4?T#5lǵrlpC<1?f2桎?|5&r2WSw(C|=-1B@%Mʦ|H 퐌eGA{~ca|b|^Yg&+Ga?i.u9lwc;gꕫG@9 dib[hVZm8 `I4ڮ/Z UuLT(H 4GB' $ʵ#t#Fil(/H]J!B6l;ɝ Y ({ im+FR!Ubn8aܦ4CF/z'qx0beQE MUhg݅,#/fg Пh2'88I&$C>_쀢ߖBE-]8Ts{=Kir[HN+kܐS3c'T䦹7%[G#'SĞrJXZN7`WRɃO0 gգVP"Tel44_17ד?rrr&iHҢbڶ, ~7b5w\޿O R:4/x(GZlŒ?5;O/O_.KZSU=VAfm&g w.Ǣo"ȟWUU{*F4?SKJ8wbHW7W\4(*α%˪F/NosXTrs~(\lP/'$vt01dԖf$ǻŒaIh^p@!YF& T҈A'Wb 9hh܏:u.?XUv8>I ?og,yCq#x'UK(2>[C8uL`#QZߵ]Ĥ5GԎ4;f RP1ڒX [EUg\ nDTaW}Gg\jZɼc:1J jMMljf/snt5C2A>3"xr9M H5{}?&t}`4D{r&n( gD`;N0kAx|>l"kA[3Mu3(rj팼Ue~] ;re,qb!6n i@kA˰ЋصX\ NzBaiCW XdzH@I͡P@-BP 6=K?>#`[UZ_7uH#+Fj#*2U7߉v"YGN3dDd70S&l)AZjh!'n!CS$X.ɨ-H5jHŅ7./Y/bOWkŸ߀i׈hAr7f|z6%X_ҍ? 7u2N[TWXnt$ ? ?1h.s YȩW`Q EKV 9O.ЊZ)P mwBzw07 O=|(S»U";m-IM=`]'qp^ *ML/ue)PㅻsKiA#b]i [o M2=qk%Cm*6ƴ5G[ 1gj-tyPW_ l0/TYk:T,Eh@p *Dx0bVIqGfbi67:LgR-tS:<:YI  |za;.y.VL )忌imn֝ѝʂw;ME8BPZE@ÎZ8vi6vӤXЏGAV'-s'j).dEJe$t@+} *ArL[TQ@N,0|C ?h1JQO0 x*!Be66an majbmomboaP"oe]Xueǵ^wjgbfY5s N\`6&I2(вĔLswNe]RHqF+ _K4<@'I+]nK?d_3LB*=rZGo <*Klr@,52&+ח.bBv~^(6'ս( ܜhbbLb+: @fq|;j9^M 7܅|;τjh/f舄-4k3A{[DZGY[3Y 8Yd:6N\aXjg2cKBadح_ٸLޣ#p<  t3f6AőCI<D.dEguHvY.0O:i1TfqnX>"z'622bOvj@&pR*br_(L]25AB XΦ⑋FUr%%#'78<<.7>^ #h x 3R8T*]]5&qId@ tcp H*ЄL8~μ/A{b?Yc"ڹO3ڇMI@%5j.!ɑ I2GJ6V(#@ < XtmR8*Dc?OS+ӝ9ɴwVs~ ڼ.&$4M'"<2=-x29s ;^V /[UuL[ |SL+WeʳP_UQDZtf>/JYYxP]~ؚYY!!ϝkw`=HT".F00fI65+ y9U"um'H%9*29fPrT5zQl,HkQO UQ8Ox3$* 77阂P@?f+-1Q( 0V*?Y< hwǿV(EL ƘUAj?THv-t}2yY"!p|ƌ7&ʲ*!a404T_#4hXX_,(md8DJ?.<"@/'Vk!LýqOԺL^ً͢,;mxŽGar>TkB{ރ;&wfYm#ϹY=g'gmH}1Iɠ,.`'9He'f#XC/<.mWZc!CY8QJ2hT™ԄD.c`y!F#`) VA(  :0ַ^k 1Îƹ0#IʬVF&eq S g?:r'@WT-']^8{q4dMs8Rp{XN8A67% Cs H̲mud,Ⱦ [hwȠi0O)$tN3,? <=_6dԚr.O,%nHj\pHM/t Z s`mWʺsv\_Fbmj=~h,̏o.!,,MECi(5zJںQC.|S R΢ GںOίf`H?#k쁙 s!ݺɨڴy[lZ5#hUC:o-EUUz:$g57f5!Z)́p5MKlDo~3̀'颺(QiKf/ED`CjhCr#Qsەe0F:vf-)(L4= e@7ڲa̒gjղ5 U(-&9j xןfۗF41&V06770o 4M P23P0 j̜_Ǖ^sjibfYuB [^Y`kP*#eXN e\V-xxw3DcK* zR0Dܖ' 3 ˱uϚzQEjQtȬB,zfƅ |!=Ѡl-Q&KSs S11$[dU]lFj`2X ,'g%7ȘUEgE.;X{NC+@mvƌ?wfmoFheyFւO>Z!wb3k"|Y\W涡S&4+B`}'36,ÁuqYxʙ6@)t?0r}#C=g6pU 2l &@ 'u]fodA@jə5Ŭ'IfZ9Y/f1Ξ\`f`GEHs!;uqCPyd9 ]":I8 ,CuJ)N#O``d7Z{BkPs DmO$Jr]p8,wWTA?| \#S5//nbal V sa[HWqHE=(bF l K5{'aԪBȄ^;`,t IZ?'>mbq`ʕԆD64Ut6 {tAF#ڲGk*7GlZ(M )|xZ|vсS3; @ *Zp #T9 ',z^m@]d W.V5<ǽu(!kmcF0q\}z@q4b&/PJd.ptsU,1`Oebm{g w'a>k"'lCY7{'%r9~x˖AMRk%b|I;o1{$czTJL VM8!N1J "4(c (`0 _ -c_n$, `кFȔV5Z18V#QcگUiTy}Kt!(u3“.MYQ12`Ѝ  ԝׄu:Wudߥӽ9Wrh*ZYE)(Gs[_g-3Mp詒gƑ^Dv P+Ik_3upܙv*5a )۽pV0I:(-W%L=T$)1erI⪘ʬ.l4/?#Ϡĕ(:~WD"6$z Î aGG9WƊ).+<10+P O0 9.0@0`@'h'%D$YhڇJ/X ¦M #{fWKޝ}ї e%Ėhf"ldP( 1  t8{ &0f@O50[5?R $,`\!1 Ι`uFixT챬,'vn^KU4|S ݀t 2hnʼn γ"/34݋{~v] - Fhp`d CW}bjza 1ЬG8#Fil(/H<,-------------------5e(7 8e(k=;[WwODJ?YyGx\ݵ /t,uUi$]t}R`jo;(w6OtD4tK.2qqǂsZ}w%gYnZ0~Y8cC} 5b1j>rrR}jwL=MAW 0=<:z U\xaS w9P 8KL)6W% a @ ۾۾۾(SѾ۾۾۾;.x]7F4Ұ~"U*!&]@0OCFEõprEŋfla51qYD 'Hl;{&֩"s)Jbu5 JXI ׃5r DՔ8dlM0Q( S#ź* 1ڤ5-*RKɹ%mhlpȉѐ v"YZ*w3/+[F!$#2;qJɞRGߣ!eisժҹ#9s`OrAѬpДP+T3..%ap}i9B!t?ҵk 0Ϟ, z+}?V?L}=_;0G, ?:cCUV|QKh Ԫ1; ~M2+DeϣS8s-"y)e G SUҖ.NL(,gTE oh_'/k(7ڱ W:d ><B9npSGa=|O:JsGKkT JLk9 JW{SvRL 黅Il& kfN2*eנOa47CCKR%3ױmZ3۬ A(Ӷ9eŴ4kڎ?;R. :,:iZ\w`ꔃ>0I-h=CIkN-@QivHgPR0`Odj~H3[1i2j{hGD:Ͽ֫Frh #85`eKCsAseuV͍Jk]\E0pmmmm xmWϷmU1A֘'Q}pG+9BV"0K+ϟ~RyIm7ATrVSIh:'J6Hbm3SX[e_ʢNʤ 6 T\Qƪـb d.}, 4XS? y6= D!hFl|'XDy"6 zυ`ӣXfs ol65u ҭȉ䩡gꉶq"b+Zn9YCMYD:AF6]QaA.fJbBAyhCXK̆IDkɚ=i/zFd4Y8%åRveQ3Avs;P)ٹrwEAtZ[=qCL 6G=7 nA'?|0 @b>Dڿvяʦ:y "r0`1n#IΘ쟖y&_Ij5Z ]=9BU7FVm-' Hzx:;4lCB7D2( ۱0aO[BI/"N%N-%-I]pK [p8 ['lC?8QDf#Fil(/HMDs5s(X@Hk32CfIyhP끞*=xKS,I D٥3 E\ǃG%Wċ8uA;zdo?a,B 횘5vzm3}~TeJH8%*j|٧PgbCA@ȣUXaB^os5F:'7g 6]\߃HvUivoy\ 1kooce\Us/ 8(&WoE% ,=ď Q~1Kt'RiJ|<82ap@-#CDNܼk̽c;EN%avM)60\r7[Xp -]Ql=dءm[Eo[EofwtjZwy]D*t;{vTxuQ$0EHuǎy]8K2b%  `&Pt {hJu$gikb` HhAWR"H,eZG53")el 'E(vgx=>+ `8>ZI!ӴFJ -ՍkgplMX$#DxCeFMAPGk|i.KIŀxItdnGHΚcuuC#SmA:9/BdB+h+^UZlY =:jvZ'vWW!EM1f 2$|\7`K8sM˒eۿ93>2],P9'属nݦ@ dXo#~&;uUb*L)0V}PIpaiUUt3P2'NB8Q&OwYT29N(2{mŧT:)i0ʅ 9N^l89דUY[=PE Fe0ᨩ2 ~5>qבcv{I}3^-l!h-98=ܞ\Dփkb wju AS,MaAƄDKKP55n" 'c|~D4yi:LIZfqlFW٭N ѱms ~ݾ6۪^VvfV@0+2F,{JiTդfv X]\M)%Z &VyB1va9dzh蜇5Z"N/e#F'QldEmG%>@zBnH[q%hb~ bA U^J6TG4֟eӀ3 Pq"0̾+&C''mR5Bڪ模BfF`P@( ! C8B" # @xg teV D '#Lz,|8!G"^H5O_(yl] %5q7{gr &~ Ϣnzj:WRurQf5W:.bߕH…A+9Bnz%/6>ט$BVu Fv`l|m UfoZB>0Ec\tԅlف<iP Ga0vMĭX>z |zlz#AB1$Y0(C64LI,a۴۾۾۾۾퓸Yּ`*X鳻SCGŁFaҕW2. G)\̌Y&H%3GLL5,xN'$;Yv8b/;l"{FdIQ/O=jog; F$ %|BȺJr`φc74PX.l+8e3m HP6B NpYk)&~p(DjL&xF +uFwMȊDq$%<ܪEDѥ%iHؖA/$XO55[d+'q-!Z~e>Ί)g{s)V~7EъqK~% ovQ4\MJ,z|FUWnȯV 131jBCWPâ,bߔk85ɭD_Cg"thUP(FN1Y9阉kCbF%/ u`#qe@_b}<`.b`,Q%Stʨ'(etDo?:BC!`'aS[ХBSf̌dE.x4ȵ0)ViGG\55f6} %or@ܭ0 #؄ .ET,O@YDₛjn.|&"}_ޞM9 uy5)bgZwC1SYcUPxjmɒY2U{gh xy B&z_luR##S_r?-. 1=u#i789.L XFDPE-^W o=mmmmz5Vڎ_YT^?ឳAuD!JWn!oOVХ_VS*N"&& ԤBaCO7}"<3&(U>}W8j IzK`^uRZfft%mVUF/PP{tF {|p̫N Xٌ(V9%5.Ѫk,=k3Y=gҷO-3JSP\L^HT*! j:KuH`S h9AJ. L:*e݋L09 ˩ē8DkIi'? DEqw Y~_5~j;5tHAiw03`+P aR,U|jZ368-cӁu$)23 A @(HI@L//ʇq)6Ǯh;nl|캱6Ǯh;nl^NhZ;r(yǵMq@iÓmD}@\Ul"ne,Cx.513^89A,{LWM"H 32zU@A:-H}B㊚eZ:cdžYU͎쟷Čf505> &駊vv46CuUu,еΝ%KS?Cx)[֖Œ=̱ 7/wNPpZ_ƪ&+t/8jD)vID8b'kD)vCxn#Fil(/H0/СX; /X#Fil(/HD)Lw3 ;|Ճjw2W3Vz&|f0\U~)ɨsU=\L<WNض Bg:?pSIJQs4X0t@#u;+SL晞}g1s:M*ɨqm?%x2XX* tJnԝy1b~ӧw   |#/K8Q=';.< %:M9f!{-&: @";*)cK;lͲ/,K0 6غ>,A0:      ./ $,  7-44D!    )   /*<,4/ @) 16 Pl BWG@~X0Y[5oS$79X(  :FY0&hA2)IR  (3 N5QrqI/8D*nҥmaK=uSO[MzPU$o`V(0#Fil(/H)44''FEJu2 ;zցhz1U7Tx*ye4[S|-ʥuX<[O;YQԸ @f9>mRGMTv}7V/r@!x?*QK曠{e5q9P}.ʥtl>${1W[) rNl՚|0`}|Ԩu   |#/K8Q=';.< %:M|9f!{-&: @";*)cK;lͲ/,K0 6غ>,A0:      ./ $,  7-44D!    )   /*<,4/ @) 14< @ WV(l|+N ߪ*X@ 2p&`K PHp}iDLP@*F``S\5R%Tu)5.cE%!Uz⢞6E=mw(pB]U7+ #Fil(/H)11%%A@Is1 :xg|0T;Su-xc8YQz0ˢx[;YR:\Uй @d7=lPSFPWx{;¯U.o>!{B*PI枡yc9o8S{2ˢvi=#~0V^( pQi֘~/_zzթs   |#/K8Q=';.< %:M|9f!{-&: @";*)cK;lͲ/,K0 6غ>,A0:      ./ $,  7-44D!    )   /*<,4/ @) 11< @ WV(l|+N ߪ*X@ 2p&`K PHp}iDLP@*F``S\5R%Tu)5.cE%!Uz⢞6E=mw(pB]U7+ #Fil(/H0WС ,,@ PxQ#Fil(/H   5:#78r^}PUH`h -@5'.Z +&I-Q0/ \  /       kP[ aX@An{09 $P,P,T-PPPP8THE)4#Fil(/H d,#49"57t`OSG^k큕,?4'.Y +&I-Q0/ \  /       rTRpPn/\_@PAŢ@eY@Aa0*XJ@ #Fil(/H t) 38"56vcMSF\n탒+>3&-W +&I-Q0/ \  /       rTRpPn/\_@PAŢ@eeY@Aa0*XJ@ #Fil(/H0kСZVO? #Fil(/H +0>d/51Br2d/618IoBYKtd/71fz BU&8ld/81E)Bid/91/'B7d/a1WB/d/b1 NBvN-d/c1# Bd/d1 Bod/e1! %fBL,d/f1"%SB֠,(d/02#"#aBBmd/12$",֬vBld/22%%FʫBFd/32&4)HÖB d/42',#Q뚑BQsd/52(,;$+pBEd/62)-CvKB[md/72*=/Dl2Bg~d/82+0"'+7BQAd/92,0MvBd/a2-C1KB']d/b2.1hxBx8d/c2/2"MGOBod/d202Ht8BzYmd/e212H$BUd/f22K3HpBI=d/0333!pBK7nd/13N3 I23B^d/b5^96!PwVB&Rd/c5c/\d/ThmbӊƊPNG  IHDRŐg pHYs+ IDATxw|dW}mӋꨗ^].88 !I$^y!'!l ලjݕVNown1Zj#mtޯjs={H8 W  \y"A%H @A  $AAXD  KAa   ,A"A%H @A  $AAXD  KAa   ,A"A%H @A  $AAXD  KAa   ,A"A%H @A  $AAXD  KAa   ,A"A%H @A  $AAXD  KAa   ,A"A%H @A  $AAXD  Kz y,|4<<9r 8o?I,_oWUl޼ۍPU s'9\B,Tr9N8}bdd]P$(2.Hd8gQ$W˛&iӦ`98,˄B!ZZZظq#۷o@ @mm----466i Akޒ 8=wf||??a:V&:tn(XnyAD<*.*SRu+|;SD*Ʉn2lrpΠ+Y` k+XVkSeZD(뮻m۶:l6~^/[l{u^ pmY@WW_/ DL&ir |>߬0 CݻwC2$͒f) 8HWi VŧV@$܊KP$i{|cRyT~Ka;Cڴ9s0;] [veMx<|>|>HuqswЀ2L{Oo}+?,z TUUqM7ܹStA ,P20 ??&NO$dI²m}p-# ,~~q(eM4I¥He"QQXtڰ:p˛8$bKn;l.v%x^*++y;}7-2 O|G'?CUg߿9YAzۉ'8|TTj>*\UI2w|>EKAx2@" HeI"mřu4YfMM5++{y;hڵ{wݻwT$>VRgqN&a/~7M:t5UloGe b6G1Fr9!*9-AET^[[W)L|l"c1nvNvtSxg{Zlh}қ5X&?5TYQ&2C2;hh;9/vUmCAy=XE:& â 5IIPdVz;O2M^UŶm*]2eM@%KHIJ˪ØaGsX ( 1Φ [#]n^as Yh Oz U2̕=)A,0 qEʲ9`LaWf?1.zy 4W,1<%.W81[|cñ<.UoĶ:Һ[i2aM:/q'zS,KU&}-CCC ^3A.EyN8@t,Aβi X[S=m[qx4,XzaUnY.C~r&96ɔ~Nl2_H,[Un`{KDNv| :{V 2\GOwyI:;a9GA$FFF=Akע t]ȒDw.ϸaV6E ŏ;NqM> LO… & &2nޝ 6>g#1 p\qp$u۷&RI0980iUdb< oӶYVQAc n<O$bhJ p ,0 # $%ɛ& қL%xZ:o> Tl$S(> <.s&5T?<3 ?"8dOc4oR{7ckKi3DZ|yWd%y -^UƦF|Ơ^YAXX]`&X q9+;˶966q,nq߿*b;0BgRCw`sSʹm+*Y 6]ɇ8tOpptӶlk.q|Iw pZtӧRlC4>NPgmŽ^ r)FS,{kA?6Dwx'3S\)I;LLhy6^:oۧNܯp,*7k!K{`Smb[CTѣG^ µmwip\+d:O¯qo,O(Th2y$oZpk 븾E ~6&k3ivp,O-+ua[duryrV7.Yܙm7O`Ϟ=b -[LQp:͓g{n7?n&Ebu@<խ>wrw|0}E(! lnyedò i2!%Kæ z{9>68š<;vL$ \]p]wo}zNOcyL7nY}+3~/%K}g xf:'}>Qf ۷:RYr_vPQh(3i2onU)XOy~`Kx?yE pe,@UU}Vyh._!\W|Plw+,1R-H{W$/qqx~,%e:oHcx'T:"q+LlYUЇ>Ŀ˿PYY:A=.8gΝvm@qnoVSThm3}WSyUbU3"8?I4?0htFI6U{7qߋEJ/H.dOVX/flll]z״AaQl/~G?«He]H+kL= b7}ܾM[VqumÏ6I~g?{j87Nʙlm;7KnY:/\56YW> pu,, F+ܨU$3O[-[W6lMS;Q?ſ23usqr ge@KѫfaMG#}. µA& ˳>{t]ltD@Q*y %ne6!MB U(`;Ep.ĭ*f2,'GP_òUc/vxt0tmm-yZwh&i3Ym1Ҧ*޸w?%- pmӣDomV\P]w{*-PiH[XrܯU$DWae@qݰ83(Y] ǁupYx `?H⪆7;; .9 /~&+0-Ènq0^`BQpKዟy{ˊ+. W_ )r#g9]} $i•aΞ=Kí+4zT d \`覍aYi;XeZ,11E*Uxߥ j*w'Ә{h $&,^n]E'si&g0 -ݛa$o1lgz%/KxUPeMq .E)x5K5c{Q}A\ q}Ң <<.EaEMZlR(w̻WS,S).d; oZ=D̯,sfwdx?4=sn;|;}I,^ھ{~ H,5 y$jYHusV"@WWGaƍ*RdF"dϲN@\hz1^\ Ӽy_$.A:ir0.,U-;9'&O?N$-m+AzS t:9͕VTMO|]̤H>NK|Lߙ]t%2V_`fӳn8_W΢;Ʈ]m7oY=cST1ɏam_oٺ ő6.Y\ߺ=\D%Z7@S旧`YuL} `r,Ua24Y`r98kh wm~z={x6[ACʸ׈9΂4O<)yCcȲ̺n\[ׯ%;:F8t(f9xv<~}C>U!,Iܼ#U>o9Wp6?;3ijnjĭi~Y^_Go|'aNM ˹2T^q9e޽߿oyN  \9 >bFC0D4%ϳw}T<\W_EC-˛GSMK7-9=g Әvq@Wh I47w1qZjjx+^κf~}8G'7W7sc{|TrGrM7!ˋ.۴ ¢| ^@s9=ȷu؎Êz𞻩 Q'P f8l) )K;Vbmk+>ûpxpScq7yӖUOG?o~󛩪Dg|idž($8d #HƱYW W-\+TW:q :e")*!ɋ3cYFH:f0sil]UWU-PŢ,C~|c#=z>?q֗׭1bGbfik{,m>RU0g6UtdQ=OGG紿$+W.xH#]'O=|#[vY_A.q>64Ib_3Po3c8cdIӨO WG/ {We5}Vm(<&=M??w[YER˻9N:FC"vtd셈\*jo;@h9Mջ֮kؘ3;zlDpWRZV HĈ?B"ZU 7CՖ8?cd>9RZ3aX:6NA/VT/ f@o";xo| \>)$`yukO3Cֵpǖlhm=]M[++pT^']ЉesR |wo^ƚH%-~{ہ$w11#qb}G}0WӨz– ʝ7=]t=3HO4Z~kۚic4EfeK_2wUQ~/IHZ`?Dzz'n+-:Uj(n/pc܇//$ص0C=>s5,X,޽{u[7ac18ˮ!$$qܳ}ۜg3"+3 ҺaYry3)ɲV1йTzTEWKr_Ɔ6(,#KZ( q\^'NS(H:)]G$ITY596.r ٶr~)tv\0G| 8@42SB"V8rToe&CIU5?`W4A$B+JO1uU !MM-M5Qvkc_F:IyjY/j┊Ab7Ev]I^ҽKV6Uo5H׉(h{>EOg^23S l}.zX⮮uIVPA*m]SG~lϞcYOq;d9mY/yɵ! Z oZ'䱮 .F|[¶m)$I"pƶmh?Ǟv,ʲۥ eHH2*@qC6'JQ0 °,rA\c{s#2>oйy3_}1]cѳ?MSd$RIlۙfwrlgYUլy.˶ wٷX~tt={W/$+jQ\ZM7kl`_jEuJ{ $Vs{>f槭q($3c#1mm%;t׬|ʰHK)?:cܢo^Nh9e[t'׌d#9ԾB2V2'- $oK({+m###㙖s':8G]E׋KU ְ % _ eqbtL˶iin dzKE8rstPJ6tw7قH-Ͳc2e Ft 2O+Lh*h"A:S8-\=?Уct}5l_M̈́Wo*FKf4ogXHɁ㍔U ?2E].Ysh[y H\gg=P._^pղk"2E!ruA.c8Vn]ǥ ~ƪ*Xƶy%.&>ƵkinjDUU"55g~tl6KeE_MJ0 hBqEa㺵rm*߳D>!6Q3wCqvwuqzhD6aZL}+yrPLZwtM7݄=_3'H9x|(>?멽^;چ%y7x1m!qlk]es%U%|KQ%Hq2@m6(%[lPuf̗ƹW}@H磢#u׭*VB(}dqec:6l˶M8t̉b-UQ|,pմG"(<85s]056aY].6]KC}T3(^qtٳDL6GeEWAeY3Yd_S]ͺ5,S]Y+^v#ɰճN0(ԀD=]8D:aӺ?hK4J ZBu~xqp[SR'/8W6 t# ?:賏!k.B7Rwr\5v[/7-0ØEqYӿ+\zt?yK ++\ :c#II/f;^I)]'5:iTìZHdʲ̊˨z@E(A!l|>O"B/$jhmn.9c:Ofhxht*y8'yL"gtX&aZ*{Y-n.EF/u~?A?Y*lP(D8|*Ii};('z9cG?Mhzjnx9"|m&tB~)IQV }3-.5ubkՂ |>7ofd ,K=Կ!U(Lm A,&JsL7'Cm(45\F ^  Q[SC L].?I=$SihkiϋΧ5k8Iax_iMI§i4]ZqdwK=L7MSWwei V/|O=_bfRs>m;:賏Qw=4f<5otfA-e:ƥEpUFaJYۘl`-yQ+a^M6ЛHP(_Y$Bn7 tLm;,]QRi2AEeՊhoYiA::  1L&KmM5Dd@]$ƵxvJ(bXHb+lYsசy07/8tlkE EZ[[imm-NY>kNvLl$.J^ ^\_47=O2UTȓ}c':'xGǧͪXmSl^/6ldǽ$YSSOՖɯSn[ӽ]Wt Rb0msZqltr^Kˊ(zno.,9̤0ss{v\< ۃ஬)9B;̤Jf,xZEO4OvTY9cMMD oPj|n75 ]'OpY^3ܱD!lۦ*觮2aZ GxVguuIGO7V|.d1lǶm+KH8M_K^EdU[uflJӌHuWܼj9%J=u3TOm؂0)e0iYR^;klR>FdU{i]{ܰ% ??kZW~ˍl?qcsZcg_4*[-L%NϼDa430kOI4Qd;UTXE ,ǥb&g{{I.47 nMvq򹹾MGsj"~7lӲ TW_٩UQH'gmxJf!zQ}APfhACTϣwlرzNλZd+3Gn]ȏ2uO~lh+kB.&<7Oؾ[b133H\pLYvYHŏjY@KK Tc0:CxXV`mm lx@m )$ kqk*Tݘ 2w(oRFe5DTh)cM 2bY] Մj ]3=@yq]޺ybkqs]G˒O4cx=O^0@41YS _ la^/Ӛ6=\<7x6WSXE[Bψiیel/Pr]Kq0X&o63L+W>Dgm\[MKF*dC"c24L(;r3_b+֭WvrYu0: 2}/G>Uf%ױ-v?3snQcst56zbt~JfmM_=c.[V<%dž8;uռxṲ̊Уm4MG.xRKK;J?}^xd86ÿzc+=3ϑTЊu%)^?,G~|+N~z}.{:x3.ykłuV|>$, -YMw,uY!eeU Mnyưm. u^M5 Gswvs\q s1`Z4MB>5yc$I*`<"~BnEfmm*S4)ONbc]ZjYc 5Յpw<.ro4]x >՛DqUT V86HIt&{zf銵n\r#MT)ȏ1M@ Gn{eR|mӠ2ÄVmSۀ$7Gnd-x}Tm]mgca\5xZ5cä{O])>>J]V>GǗ~egg.-_)ZoX;MvCg(aqWףO&7w?4m욑5k W:ztJ_@^xt^Sl2c,Q a!F}KsWb;-VPgeI6"Β9p۷lFSh'/2-C a6=gd2LCU)["U'S s}S#/^U?a$shxX.Ǧ:VV_81QRIdYf+[^RB+ᩩ'?6Tr;Dz8'}d/o1 ._D$g\PUzH'8̬QAܕ5d4{";d?vcO=ம#jK7ZZ,N=s~mQ~Aɡr3#YYRl3w•H ]N @$ZZZd~0q86:c304| 2Ko;q@%|nwT~*E&?0su}r,eۤ(O_TX_UAM~mre3K_aƚ0kx5Dt^2d @("Pf̥Z7_ִ[n&j -Tm'ܴU;8ӷ,)!BKJSP*ן""MQ}ib@(H$R l6پ3So;?nv7ٔMaJvfnssCis-?h7A߀7gUorW>s7-tb ]A]$[u#2Hx^rr٧|O^f:,bמ=4w4aTU0al߹h,f`aWQDj>pЕKU( 0(PTSn RX_P6jrN\ IDATz0 =q c`]+^EK't K$YWXS[K]C#.Mc쐁hdڿ~UQhMiJr*Ԇ#aw# <묳x_TƤ1srN$g$KrO9LEe E9Ɍ7_м Uaм}ȇO3ufZlE9;;P13RxxrQ]_pӹ\.v5 uܱmYYVJI2Ӡi4&Ў 9>\LFD+K.p:zh_ÝMZPrN`G,&:= &;-caH ¶;|~4CdzCθNCN9j:#.O+0Nh3ƶ TO_:S3ϧz ;n26k( 8'OҼq5+ލlD5acSOe+Twz郆;qUS?JS(PpzzkdGB[Rm»771cQi`"BuP\νf#mp ]D)[ʆ0G m]KفlJİMa(.\x w&ygLǓd<5+8I'$afƐ,v|P]þV,!(J10;@sHv6Fp4N1m64x8Idd8f`+Dz,w|$:zYQ5+fs?w( 9YY2d3>YC!(/+K `{u-!2n,;43Wܴ ǩ1mA|&c6mNM4͛>V+Eoir:DKc'Ώ0P45/ׇƝէ $tHeF݋x|h~?.z8!l jQ]bFk0թ4Q=4-Fx NJınTwZC8;e9 }U}~\LY}sgχACn+tOMCuQ''W~aYԠah ߤî1&z48-^i]hj&nhآl^\ݬt  gu %yٴ"Q֬3'm.*R~yyWԑ#yNSr8}xYf M VcSSPK nMcPA^;p,i>/0-b:\yYSL(gj641|x#\$/'NCJ3zXߣx ƝsV%GGE;=C>{>WZ kfVwһ\Ӳh!BvA~"I/9V\w&WlS2(0ŝHA u)$jiaEXx4 \EUUH.]U4Q477h&ggvRQNU ɾr-Z_UnS5J}4sw! 9D"!lVNl1SO)Ə%'5k?ee&iOoNKL;QF\7PUUL˲4^Jni%:IH_ !ť $aZ4$xnB(~A;*~a5L,!z< #2H؊n!8q"~WD[E PmlC6$»_Hf-NYFCa9s7Mq-99 `WM=۪jض4Q ٣&ѡpһ4McȐ!<|[nwߥ{) 0$;ɯ#1´mXi[쪩%rAx4lt=t) kS%,I ÏRo!p+ Y &TF&x-TԺ^ZLσ[Ӻ ]4 >^LM]$fg@zz:g#{\?>>a9Yd ?|^ݞnW4Ztؽ`ԦeY,\H$ŽݻQ!?y~>/aàU7D%tvx '=[S{D Zu@eSSL\.f͞_̙3),<Dr"y]t%uE||ŔͺI>D"Y "Wg`VZ'0d(/+qc6kijfrBGizu}zB;!bqTUNtP[g킼I*N!-h5t*Bt.M#apa>iT56 %j&fXXB0p@~_qszJ$Ɉ-ꖽ?f.!zT3.xІ_u]TVVDTFgRTVT5M 3\ϞB)( pEn ▉nɎ8rQ̻Ϗ_Pui)!6Lj#ºǥ1*7'PضRM$FcK#&&r( uu8j༡E lil5ax())~ H$5̯ ꤅>R0p7p8ܹsy饗X|9z"AC~ˮx\.LYЦ MQ\XB[h ˅'''> oV,%'1D a(.9"-U;\_j:pD"H~-:RQQC=ĬY1c99}c B<쳼Kڵ`0H4`WUGVVEEE\r%r- 6O `ӦMK_F%Dr"q^{իW}v6n=z4cǎ.;ӕH$')!ibFQ{eIBP[[ˆ ,/2aj2uYxp fD"[NJyW;ٻw&uۑu|l3(흧(@]yv%-!0-iMͩ b.bf̘ĉ9r$iiݧD"lٲ5kְh",X@mR4 rGFT53)s`H 'HOtQt\Si~i۔rUWyh+K$8̝;Ӵh`h ) 0|pƎә5k;CTTT`}]֯_ώ;Fϫi DdEG"Hz%{[zGD"9,N:̍7'; }Uٮ&?S'^{߶}]  6Lw*u44&cQQ9[ZZF=QCan.mBy ls>KNm8n |. :[..2{>^)H$Cb& 5^-NG}$t7w NGlb n$ n @za`#PU#HeTU[؄Z `wģGS >ktrs@'Ǒ p)%1;hBD";N:رcM!˭DzqD8xc7l0l!荻@ 4 CˬYB`9W< i=;x-ip)xr+ nܪ[Z[Gs[D ι]s4;wz $Drl8@UUE_['Mʧoܞ4}[I-M{DQ)MSq*.M\x\.4MŴ,vz6p5 D֩/|[B?3?\p_hll촿ض O "n` AUC8d%Hz,nCSuoT](޷Uϟg}oH$ɱmEu:$=wIҹf߶x-Nq~> }~s=yQ\\X̙3Yn7t/Nm[0ҢBvUe_}C긄q>bXlm latH$ƍ`H^EN|=XOtL8K: if20- #TEvwB7--i|q3@Qʸ[ws%%%<L84nMCdIŜ r D"8lA( 'tbtvgB2 ^!LmAmmh.Iv=qW裏RRR$%iii\~L4~⋝IP+Y>G :,!M;y@UQQU'£i<.=n2^>Ƕ{'H$I'Fճq_}mm ~>Q \iif&l VG;Оr\r|>@Yfӟjƀ3g̟?h4iE@A{b GdTr*.U!j$,"i%3ٶi9 -D"8twgAss3~M:#xUQPJ2 c¶@C6mtwa SUD~())3zhFͨQ?W-[i&6mDEEc߾}vJܶXw"q(8y\ .5uݪiC¶ѓ9,!aڂ-Z\>'B"H$Ig(,,Yn~ ELgTj f ^4=꘶-[$GB)//OuÇL-2ԗ瓟gVjjj;wyf6mĖ-[hmmM@nCU<*dӶIm u'*"̓E^:nޘE̶?Oaaa_D" [naϮHsӠ[DL!)PydĻKvqiX 4;ogddp3m4:,ƌCFFFx⇁aqbp͛7|r.]ի 4UrܚF4y@¶l$)yTvEM9s>}1D"HzI'8_י7o~ᵙ;*^B⦕ 3,'IfĉuYL6cǦ|RH$l޼K|rV^Mccc* H \.|n>3=йve$mA7O<񉻆Dr2sR p2q_۶QǍ혳5UE$snYɩnCqq1%%%1bƌèQ6lXt]"OuVطo555455a&. ќi.MEKL,ɱMf&4 &C1s]ED"tBV\3gUU_Moi)ȑ#4h%%%SXXxša@MM c޽݋i)) .U6qbA|+_aٜ~$&B"H$m$ ֬YýoeYʠA6mӦMcɔ|xC/۶I$ZV^ҥKYl;v0 4MK.gܸqRXI$ I/ڈ̟?۶S?Pr!ep IDAT>/vRI$@|bD"H$#D"CH$"D"H$)$D"H H$I?D D"H!RH$D@"H$~D"CH$"D"H$)$D"H H$I?D D"H!RH$D@"H$~D"CH$"D"H$)$D"H H$I?D D"H!RH$D@"H$~D"CH$"D"H$)$D"H H$I?D D"H!RH$D@"H$~D"CH$"D"H$.D"l!D"D1E!??8L"=RH>q444r*~ ?]O mr*vsgRVVzKXŚ5mv‘=EܹVa8ӏsNlN8PUUMSS#P h4(.)&/7EQw%'8UUhoY3/v1f\vIƝʌ#++8oQU3mTt]'mv֭@<g]O2h@xԿ?ǒ. à:߿ ۍiA0 0 ***I$x^I,tx<!a`6xPXÇγow,jv܉Д,  Q[[KkkbJ_}TF#H4g=wkrp\XE0$*^l<=ƎUUtݵmSUUEEE‚BJOk8455k?{(*(JEvo)~Oxᅿh4*Bu#?ؿW^y˹ 7~I N$*+E+&6ӱ?{N麞6Ib߾}]]]]-Ɯ:>eY!V\%>{E!];p0կ}]_^X^չl/u:g>$ *JoﷻK!SӟivھpѢzZD1>3vX.W-Μ<2x<`0(zj^>q3g]"^~sfKuurhu_ť:wwW+>}(*(%VUu:YFt+>{gv~3vbe:\bnkYԾ5^ץ7~&aÆ.u]Z*U9wxs!C/ݱb Q\ڻvdr#BrժԾۋU{b>w`Rg!B⦛oE%E'}N4%F /!O7Y1tHQT2PpM^q/~쩬W_Nǔ"nbӦ]eXvevCŗnXfMpΜS䁟 qŧ_bݺ[nũOyQHOOO‹//D;v(Buu=uG=| oQ[WM1۶oByzxj܃:<ߒH$9HKK#b7n[#/|S9u{{1bF>0YaOHeYWf޼0UUI 㘦I(½;vpם? _l9_465>4 EVs?nmm>^H$oGML;Gy. M}]==;dD/vKaHx>e?~sͺqYa^ۋ3iӦEKK+K.套ሾʵW_̈́ NxwOu#sUWRVZ-l6m_BeY#~?3g^Ĕ)S΢7X^oe/,,⶯}XI|']`(o* @6k׭o عk7?\ͬ38SYY _:iܵ_|>Z|>onyܭ흅(.*N6m %SQQ!bعzx";ٱsol.=_YFş暫S={*S5W_m_ʿ_{;oPٶmK$借}NBA"`֭,yis.Ο1pz˗BGؾ}GR5b3`#oirvQTMUH'`{4d ~{)**BQ?+ؾ}7mNIw}CQU'~>WM$LQYs]O]J$fȢER;ظfyy|{KfvSJnnW],Sp߽wSTTX3^G]iII1[SC/C;]xAjTXWWGmm,\[ڿz;ƌ󦧎dggCH&^{ W&K6nL 1cF;7v$;4\v٥x<^EҎYZ455M~^{!C3bp֭@$aUZ[ض};3}pr4h4ÿ%cǎMݛϟA]}ˀ3Ro;gss3Cs>;ui?뮻4+GwݙtDQy8mxjz{!h}5L0˱ǒb-l~tם<;i9{yx<0{ӓso=MѴ*\,rJ9?z '//kk0M.>FLèv۹nSt:bY?|7RZRBAA>yytjm&jI˜u4rrruDBG 222S"Hm{I)uew:zz:SN&?Kʲk.wӦN%??/h*Bzz:W_uUjͩkV^>&NHu8u}mNkk+B|>=3iRy쪪vy'LfF3ΛEUUTUz4o!H+n2.8F*QQ~?ӦMaذ^^xN˅Hm ]ʎ~wGjܺu|xߑEVV& =8B**PP߭(+)mWzj OFFFgh%)O"H5lʺ pJcG.Y5MÓmFP{,ѣ232OGz#==ʭ}.;"r^*M4&-ĠA:m:e2y9a*TB8fŊ3𛑴h3Vgc\\\Ys\)7,8; }V;xĒHONІfq{*u074yV{/\DŽ ꔚh3;zAoS:x#l6xtr.[^Edr )?ޝhڃPíavw+n7Y45|̊ji3ƒQt/xxNiUI~wDŽ:L db\FFfdeeqc:u #Ft񃗔0cy{yvM9;vaF>u%Ii:>kzjFcBG/?s 6TUb>,Ï>F,gǎ4444O3Lp~RݤqB(@^ UUzÇcoO9۶46ߜ6}"rRL0octwzl+%:bvϚi)S>0+U]co˲NTGzx4i*W\~ηI MSSŞLr5W3Y'P2{L.|Dmq=}Vv;xzr8aȎ1]455q~HscJy7wW4f̘|X,NkkkCUz}|mIRkB}gTAnO$4I r~NEdmT 58/g fL~i_21u~99{F"Q"hێ1x *T9jtU}+y bh5כJe& ]]#B9 TVvyN'~wOaO?1GqXISS/6qԪmɛoz_Z.\ڽ{77o;1M7x 8 ==n˺.kjjXgxr]5kt,kSKH$*7\֭]ǼgsRR9^\8y~~>ꨬ׬eϞ=]ٻ?Z8|np܎_u7 ƓNcS#{* S"?^!+3jAu P2d0Ɏ駟!0x`b-^sx#1l(wbst)+-HiOQ%twYĨ#2d H 1fT`J^n.Z-[x1<>+__ѿu$(By֭غu;<8SP_y&//ٳf2d0Qjnx9lu=&.>Zo8Ǐ{y^ IOO# p< 6"=(uu 0۶Yv-ߺ-fҤ3x?Fb~19,a ؎TUUQgOJtD" xNѡCRZzZzz:SLaoaY6ܵ 6ӲزeF>c0}PWWO;ma^^.8Wq:Dz=嬳dR}A(N?>IM9ynLw]%KD"Ï>GSULDu 'ǹa=_*.zU|!lb~Gm=ٳgt 6/]A6HiS R`:n*}?ۃx<.))qiqb\7n lW\)?~<p8̾}5<驧Q]OPTXaCؼeQN=jS&Ɏ;<3Y^z\.L$r~wjE\tх<3YItrfm2ILO| g,_ں:yfbY6pkS1}z2999|aY|!6mIZRX#bQٳfo~K4g> {M0 08m.Xx Ē~x!T[]}=Ý\ f^|!ᄋD"?dMx=^N,JJO8ƑO EEŜsY㟯SG`Qje{v׿ҥ˰me+ذqsjpeA7.P9.MӘ={_(.."'犇hlj"j0L^/yyy8o:_/g2kEE3BD80q{<}>|>!E_~L:4@$ .d6?dgw Z HIDAT4mD444H<QTX׿n1{LN=uLC!ill$tKӏ466R=wŤIgf~.>JǷrW&P C؄iddd0bp~_t{}xL?ӝ`0H}C--;ϩcF;;SwAQgݳv Yaq]40 #jNҦ!ִ6QcK&34^Ҵ͇N$MGkI3CD[k wz8r.{9jc :DQrc4~#Δ8?!%- A³v'?kp8t8sR0Oa {j5 !(žv#"":]C~GDD#|R퇷^OS9汻 h40hJ5toa= .>K.':+ϵ'ߘĘ#"*jZ-z{{ށ`|-"ql x8̺A&LPH2\EzuvXd ?xIv;FM2bf\͛p$a˖LTj={7wgUۍ/r455 }EbsFČA _*ܱZ!u:z{x-22C:]V+T uvnRSSdVǬ9=}B]DQDauܪDN ÐƄ yW_7C-|tDFV'}V\lAyEŸEZjV+ M{.t: H}DI]mZZZEDGE!66F)hnił`\l@k`XҊ6!:: O/> }~8od2Vлz* ** =!PxVC[hhh@kk+$ HtػE\VX~s"ڑۏ;3---8oۢ= P(0LHKK#(--Þ/ .x[dϫ_HHl](@R iiiCRƦ&B$cu">>~Pii|7%%e2žuoؐ25^&|S__<T*դ(*B;V$6Oe](BI'oo!רI['0vvT*' n$њ0I bSۍfCOO4j5f^ݎD=,7F\G%UUq!?=xؽ޶r M_y{V ejjjg~!,l q4bcgcs4A!"N;8NƬƹ&U06N1JN@ԙ2V_ꉦT*'߁prOT7+T xرG~83="3hS*xxM,#Ho=ϋ`J2!$8Uo|ˮZ A#+W W#}F\6 STU0QQRV!LQoR)`%NWmsWoT*dem-3;EuMEQJ%L xW|I].:z 9zGAǏw{~YY9y;) XnĻ|g;=/\wd7̇eqc` CJJJqR.***ށή.H}} ŋm[")4dڝ;wE\Vtuuj!$8;vi6e1fUxP^^y]$, Œ%0%& sf$&=z7)o׮ؾY"hq\?j7xJJBPPШ3%IBoo/N'DBFVM\.zv#@` V:n?7a ""DDD4dHdHdHdHdHdHdHdHdHdHdHdHdHdHdHn3JDDD4xHdHdHdHyi#IENDB`structlog-24.4.0/docs/_static/structlog_logo.afdesign0000644000000000000000000033262514645734712017733 0ustar00KA nsrP#Inf/oʅHeProt0#Fil(/H tG=0DUxuTuDTj0C,& + 4)}UII" Lǃ % jw W5E 82 J~ۢHgh% ET[ ط%N^l;3"$Qxl:BXX mU)d7h]Zq ð DK ˑtŐ}s5$~Y3oٲgicRQKV,xxph@QPZux/,Y9@VٍM;Tc47-T=zz*tAI,MJ+HglQuu~I+&%# ' ݰԡvETl3Jh~Ǖ}J9?=퉝l2) K68F闋 JcJatFVb#T/e#)fpɪ*ɲ+]ݞyeI[3؞نfݔ4Byr?D&Hb e_Wͥ̚&MI|(Q&2_K򥴥%K3 F~ ` DvfPIQdon4atD5͗ Y|KhKRd\:"&gE @8*fO }[]u3mif FvlP}`%Kg'1|L+`L~z&v% 1cRB~y+Ku TY4qTCtͥ%UElIs F9nxd&l(jVIRk|Ρ!iQfh1EX\)" !._#65^qeQ慈 ah+sqc $hU5AEy 5.3Z7[ X6@_bʢ&fQ<׬P 솣Hj͈JA^"/|m 6'ʖ$0* z NيAD @斦ELOR^C ]CP<ْ\Q7h+jtqF|Lq+G pE Euή{Y4[}R!Pmf<1_ p񈠄DH^hXVDo\&m ЍԕCjS pW7,P 6E5s9:!늬+> Ǝa P~9/AT ]U殺l+֜r!o W( `T&Xx78Ѐpz2\M*2fcmIޤLE2Tβ TV%K]:ohflOdOa0Bu)ҌePC /IhTsjەt3劊?IV+FX}+)!ӣmmekn,:5REZXJg7iֱƔt J0u=zUg}lxXk)#uTLP@0V&q3V3˚:VIBA%,ilf\U2Ewjppj(w(8{ :2Bĵ00U @wE +8Z{"3Z(Ot}r\_2u.GͥX,Y M *6M̜r4(W}_uA͠ьΧn<:[!@N K>?SZnv ,kKꬄHCζB|RkrC3IgfWU0$ m%DL='CR}@-]5OruK6=ECe<A}[1d%È 3Ô``v+ضY5ݝE{c3Lݝ={dglZcLY {c ܪ5#ݽ]CIϚZ^A=cdX65 cMȫ)Z?ng·-3rwo;o&5Ce~eW;CtKL˽uL˿]̴|Ud0,th !c†U ^_lSxv {axw^g{rjܱeffow com-f{חU-l0 x34xvRDa'HPQ)-hJ}jmY:"*KԞB#!4٨K˜be&b 1,W Zm#^5n3w.cݽ‡IY ?{Ctpgo(Yc h߳Uskjr=I Qsmk݃0rqdݱ15-wwhjT>ْ'Z}B۶f݈bHGXA6PȂ`@Nv*򡞑AԵ=wwwu-kZIS4%c[ba(bpwQvVv_gi7) 5%:^edL?λ S525Ϋ{=BhJԔOxw=Ċ l;UdkȊȐ//3%u^5:`l`p´UKXibR,=.2-FƆؚX  R[p j-(zgYy)swB#ߢpwߢ|ULa+;+[Du4!k<>b UpZ0_=}30J|P;#+[k]54%1-1v)rwL-&jY4!k=ǘښ/byIq/}KCG63JM 1iG (c]t,__(krH9U= x`lE`~Au@ )TOv5fUѬAUgw4(/}˅XGF)jPH!lMe; AiiHV!=ҽgȪ7XيIvYk+gV5+!^Дyȱ=7rCmU[vI0ovǾX2AX8c ]qb8*f=\⫲=U}ɑ N.m򀋙߯H a{4l5dW]U{˕SV/E4g}e&.-H jEi*S+NKsҽ' ca#ENjH3=sF)멶"xy-Y5k5y}Y1)mE@ It/ +3i6]0;t̶D#JIYlC5*f^MslWcE! Y]X#- ҢFt/Ң)lZTVP B{4$*pJ"3ҽ#!cF#Ⱥ"D^G743"Z RcN)%!ߚʚc)Ȗn.f|rto.&t(P(L t c\Rѡ@ADkĖgR̀:eRW+B#AW*TWkJ88^z@'"ꤱz%궣 f}̆gyUK%]IQAT܀b",T([l:? Y0a[jHUP hhej\,ye0k*w)],r.8{m@\bɂ41YNǂLJdR?j$eQȱ71TO,A=z!iiL0veS5b%N 4< hꫳinFCQ4Op,0DAȸ%j<~3o3a coD,Ԧ)"!Njz @aJ'3r&ve E&>IH-rB'e6Mͪ+lI3Ej!<0FM7D E|uWEs:&fuu˚?-YV(l @cn^qPEI4 LPʺ5{(~N֮MJ*tY)We_uԦI5$u6VUeђ]j+:5舥%NE]5˞>:jPgSnVItEǾݲɫ}9_ZG,g^uB u! ҏI%nlIPᢐZ{]eM%aߥ&\h %G+km̯LGWu!M \T;@ģcmKV`9 =j:sTY?N ^HGc84#!9RgVmuQl{KLQ3J5a!'A4Ւf[yT[RYzEǾԠ|-cǦ`Ϡ:xǾֺ$қUG#۰fi*'d$S 42@YH(U!N%=(U%e̮#K j੪'/f)hKv]K]{edXy<ۓ,MنwM 6D ii!S_pJMPږ%G=ɕUӻ?f]P C;M%!"Ǿp{}lxrS?k ʶ/U/RJ& !c[{l+!Tu+ab׍3}tج2dһ?dJm9Ra޹Jxe\l$$wHp#ju=]5t]̯9TE߷=˾OWDRm'TPط[c'mWGrRO]8v% `yǾ߶2cׅ'Dnl6Ys͢u̧:hj}]È&NScqv}. j!eG>7\$Ak9m"~.У]GXMw B H< exǾDK}GbGl.;m"4zZCժ4ko.!}\{T"ۆ"{Ky 5ddFY=EǾ-ҀDP)ޢ_>¨l7"Jg1[ ވ3gZbcq`Nv2Xdqz"?~4D|q6 ;ST)VD_Q O0T܆C K6D]UKfW.\xǾ蠁"%XN+XvxIj}wI.FOH. P%dV}Ǿ0 Զ'*zǾ@5h{2XxǾ(/i2dAՋ@< l48zǾ4Xrj!UN yط܅bz1Ͱ 2d˚#A!47X}Y䉅u+V$fBlط\V6c̈́$dc_HAmfA H!E]1~BbP&'!Qh6MSg<üMH"fߕ ϒ?'F[Шˍ'=v8C޸0|ѵ4L!I҉oW&d3(lF}_ܞOw%J(PN}yeDcߑ(6ūw[ ]pV8'mĥی1 csx>THiTszܴ#(#+-Д c4YqOS3j,u.Y`8!~Ǿ "QVw X5kmf {-&o*nB&0d​w[m & 2x}pƈl"gcy #:e6Uu.S#Jlk^[HVMׇDo*jGR gMRc޻?6ž |pNzA+FMG&4;vN%34L!I+ݝϑ>ebd TuEjQ$Ti"Xq* U*b% P2ffV;|yn|۔w6ێ{w+)pU;:fuUM68ӺC$uj@UF: `DC|ѼQ=3"Wul3'`j "ef͚ 6Nl!J*q0K1=8fa|": YD53]vEQ? ^"J_;l!ؽqIsYDq̣Y=ENp/WvP-M̺*X4E?6lF3X!c21yǾ&I!#le%ɧzMݝlɏm JPYS'Ⱥ5O]\Q4W݂^3 UkhcPLgـ :B`cT4ikӽQ#ǡw $.}=GD,]\u `I}7ԡ9dW5&Hj4lZwJe_Y6|}SE o͍һ?݀ȊXE2@?%&fmߜ1ږTǾiaU 3; , :cbbhHQfԮSNu,ca/t0v$- (yǾŠQ䀥kSꉝ-Bw0@l6zryǾhA '=3,(]zfÛhߝ8;3yN5$!/<TԽc`RVMq!EeLw7uh^S5$iCgҤiNG٪(lvw͡|juk}U}!e")2{A̫w;0f5["vE^4}{ah.{ M.T:q9(LG kU7`<>1/~B@ǪjuEv,E3#yf4u&Eh.6ui&HBdɒ{I;Dm_&Q{Wl, dC\4L!Ixukh,fV**aFQf%u]qK?nE }iJQ%]$ ܈cn/h㡪iM'w Q܀b\E/xw0}WRNjO qs$cm,׾so5 PVWV0ntÅp(]oF.Kj3w<3:#$M}eJٮA*:E}I&ޮ48v1Wsl/`c Ph@O;{fV,_V [}vzm|x˱L;ѥʻ? e6ުmS طܜ ڰ"!ʻ?̝NWMb ϫ]ʁݦ)! a'HmF_t̤ YEWW-EUcj{ ƵѬs)zǾdFBhVT.~d఑BUzǾ@DǥaE}}jt$oѩŁe7O/(;۾8RN(w͈/(y}0#(!3N؏w[e:6- o>ZB 6,ȄQBP竲KU :fRIӇ ,Y}0dЦ}'7F޼chhg2}sHxU˰-falӔ[[.yy3ytw=C E+,Ke3jgfMWQ-ߌ{": D9'>]@ٺհ!,.KE}#Q' xǾᄨ ȍsIȤ ƻ? F!! xǾιd}n0ȓ»?RqmUǩ7bw<`{fTh**,طZVR 6ͺ&yǾ(Q2J,/ {Ǿ09#NPe1Ǿ˲Dw׵D".8xhB_*ޕ}At7`fw]ufU̧k&z7W]yǾ,17>",yǾ&x! -TwZ@UNGB9jlY]'SC፱NӣoK,govPͯ꼂"M)-Z ďBű Ւ5G1xf u>wQTϜsè\$[4fSuA쪮؆,Xxx UWvͭv쾧hy\O%cYA'v tN' aRtN/}}( }HHm\κ\wI ~L:Xھ5~ή" Pp-I55{W5u.SS|CW'ON<ߠ!J≂a֪f :hfrCWTDLAZC  =OpOmɼqb\h튾g7\C/D}qT!ƈwHItzmEaR!pf1YS\_)؆cȆ&sh~[Sgl,Y wZ~DUǻWp|2}qT^-Dcq]ԲbT' L1ǻ?=QBiӾ˩#?s1݈2ۦhuw",X8aO]F-JG|G$`!.*g{f ufGSq5Ǘ q+(!@ FжQ d!n F& 1A \g"BP;ޢ:=B|̛e;^ E"UBڋ#~ml !_OoCi^h `$,t1`ShHiGPx-O&*bXPD=TQL=, uQywiE@9\<*(G:75M(F5m "C )vR76#4z"CL!EXtbpU kῸYBVU`{1r*AM^oIjVL7 v`Ō侄:>&n 92}eCR bz?6& (vv$xJh$KY"D1O%jhFmV!e-zEjڟJ*G}2Wp CTe^ "4}!O 9aG!>lZcĦU8@+C= ZBE7611oY􌉪圕?vb"Z?U(-ϒ 5 :KP."""[Igb#WodE vF ;f \-jt+0HcӝOWc@ҰTGbƽLia[ `0NKb*xJOǮyl 4PRЫk=ϕ]R.ב q (ioT7 2,CTLTt r yb$XHJTM9yfΠb^!H(vQzRY9e)28B1C僵MV@a;VYBAD!ʫlKN0rkA HBC!Ù^@,, h1[g b.Nia@C1;ͦe,f3+WdB 41'1("+3hlbv7ƫ\B9HUX;ԒզϢQxH|MR,< PKQ EZ!A>3jGVirZ@ *:!PN+hut=;ۆ8a!Kcؿv峰 Ͳ.tA4Mt*3% Bs+tbmZPӂ ET7 86y ߂xY|? %ޠЍ?m&9δh|Q' e|D.M?fY|xn ОnAiLm F` ynF 7R:.7KD_IM{3G ENMBrL5gZ,{9`Pru|g@gW,߁*IqsA4pTˇf0.;$!78%UM4}c,i&|uO'YN?wRc8Ny7 " ʀІR!snU apz`PD JzY{ <_ kf+EzoL)GYqSoP5/18 w!kCš3MQIvIBg9zgg e|I;$`|$9.*4 _zuDKITʲ,SdM;^(#7H~KZo$D0oy= TSG|,c=(=x_IE,꽉: fòy|B*8 S`߅I- ʲ\*2_-@7>vgL{o[ֿKWdԄ}fȼw# &P(kQ <$_ԛU1ʿoJJ=X7 ?kh%[&PAoxaP-r#Aݐ]@JoܞZnaAg?_$Ӽ߫tHMw<^o,DpuQPo|f ʟsr{t!Q4[ITx{?1| ,)U]x[E|A2L4;W8twI2eg (&2H.S[ ޼-i݂, N@ :[ |ɝY9 (>xI ?@h8&=+ :;˟,'Qtx ? [/z)J ޴[ ۟801(/f|Vi>#PDkv⠏xtP,@ss,T$^?G|;BRuw:0KY+ fT <=ߟ 40cg˕ }O\oYTXiXH([pkňz)w+)M*T1 ?szEŒE *K J'-bg|LTN;9@I=|+.MP!+s:#*Y.`(|zym|uBZda$*kBi|uhߣDzT,w (z# ߟ +y\Q`hV Sn?YB@ '5xBo*VY2/=!H_:1z4@e/wVd|g hfRl3*4?>p(W "RP=IG\w&>kLi|-p_({@qϽm?>'@:H1(! Ȋ. u? (nxWbAٶgq7o>(8^(Wqॏbe+W;ifQ 聧Jw ϝZ$Ne^$= E yմ(ҶQ`L[ezb0Q=ANǿ} xzpx U?v@gYk{k?Ě:|?3F iY өTK]ԊH J LB@9Z$$F@O f}%H6x̱$Al]$/8!&9d2P`<2Qsq,q XSj #:3%2y1eÛ7~x:f-3J1T4!:՝]|awguom9<R Xk $q8Y1`X2+DZjQ}&1[4jH!@ p}z:f*";)X R,lq eVc;b=tn0p:] !0*I6:>k@2F,( .۶n~[%}*}n,AqQ\$tB=I%@u !?}޶9mڏVTҏv۪մR$T40+q ߍ(4k$ ڡ{|yfg]ɸmAj@/+ a Al lzBCEY[֘YgyǨZX6YSj s \@Ȋ%HC@б6\R҃p|Ӝ`D} Am4Qx3<cC@AN6X5х?X =V鰞ejأ%6sfe}UTwq:9dxZ= r9 ?x F[d G s.ͦVaQF159AN./4xz:^s TMu4> &XC)8zz`Aک\V_շruۤcԓ#!q?踌 K3~B)-0AeD;~I +x:&;7nX#h M,fkm1IFk_f\ֺ? GTa#$sU`-qt\ " ܄&(R#@x:&fAykaZ兓:?̔I2+6?>)B:&%PxNnܟl`ki%* ܪ2&5kZo.˶L ¯)pZ)AVR @γ^3tL!~a;\7tlQ4<[91t`3"dPK Yv'/-׮YGwo0UK)%*| g jS+V$岭Je~flV[I`B?8M-VR g*d ˁ.od9j,ˊѧ|60L@n䶶g2oIjS{. rGࡢ95p@w1--Xͧ[v_w3RmB `QMls_3}e3㺪fҀ>Uw6_>a9cE# +30 ,nZٿټk_c{lG$oՕUsORk4۲itń-c %sб 8\`evYR*!R<2cȒ% q.@v)DP&#CP0<2k@eÌ$h":TjJ),$X]`sڍ;-qot:RJG4t-2-CCF28:Q Y-. Iy:<\yTf 4`y o?l$p?XL PH 9D[}FP$U0WXtxT0$AtM",pxT3X2y:eYM9ORbۺ͎/'go?K) C2N7٤d@B4 3À R@tl'1(a;n8c:б clGҜb *=k"/;@P:9J%1G\0W>#0m?81X}`;a((8AZCPWD?XK:lDYw*Dy: DlLxx!xM^.#ń!\qZkFm-o{7kx4%xPGhk1фn xsj -;atIYs;st|$‘K8JQ\=g$KdUU(RYdm&w/ԟat|D)J,wy?XppBF<3$EnFg&6?j6}l[*b=DpNP<\ݷ6v㲖p075gRg!T NMDN"Wcrvg1LϿܝt<' v \,Ac9pϨX dL#BJ$Df'k:R,s˹\0a>]"cCz:CH[YpE#2źΰ{g7}q-9c9pb"/z:CM`ƣjOɈKŮۙm>;]AWLi&%h'cKx@sXҤ+V^2 ңCwc}k-wqˑtDŽ!< s ~$ځgQLJ‘崊m۾~n7ټTP$k9GSb&Gk:S ѢhJX|j)Ku(it<xZ:AX!j`-e]hp4h9%lE`;C@5-tF&SfSOwF> "P_N c"ޤA,F{pZ<ciH CCa[BrhμͻeWuҧQ]%?7:=@sG2c hD:vU`j=oŒciB"G(7@ f`qukwzqgܽXItBD*62Xz:v-Kh؀ۉ?5Ü2hc @](J:zR%gScW%<$ e5eu RD]H*uҀSP=yă$0H(𡂋 cհ,_3fޙ1fiAǮEDA"J):0a::(+Pʥ`p! ~%$j = _"G R!%𫑖.JiA.-!|) ĴU? 4'1%WP:cIL 4(ВżcȈURIƒ9tle"S` 25xPx:R #:*ha,{Ie"L VPSWRI`(6bsxHG8 (P+ȡHaՕ R[ViK_m~-gX1(O*4L1opYvDs6n^Z9j_ט_G~7i:CD T'jPd th6I5 {kndi9DnRK4U!%F\Q_l5 Pܶ]qegܕAfsUϗ 6A<7Ms\iFJla{ewwΟM_R[PrQo\muI_%fɿZߴ~{ֲÌb\Ƥn='}e-eV Ħt~6Zrj6<oH\|Rl KⅠ2295mܰMurYev2bFص 8LF&LF\6syflӗܼ_9f5,UTDEn؎~v}*]i! ]KRE!E<0 Z7Ql1j$&iկ]yĨyc8`P]Xtt: ~MYKj4=KҨ"X"]QAHO6p/8p! Z$@ A$.,+HrwvMKkإݙ?w45 =іrvmkF4"fc6|hhvjsFjި!:T5ްn;vg޴uE:A NKfάî+Etl s S+DƌQVoLҿ]쾳Vϫ?F<#M H E֘{{s/qY/?~tRi|Ym5mֶƖϖirgW?&u+EmgAX<O7+td ahAA? z4eEPC֞(p4( ~5gy|$(xB/:U( Rbu=Sֺ̪,UlkǢ AX$'=?@+h/I] tL*-8]z%s0 .a !tC1Kvi4f1;))n4p~)zDփ?ΐ訉 8 %#V6x:K&8*"isq%d)'Ǚtt\FBpsq|Ms'z踲B(Rcjѩhjb O;N95Mfs1˟5~Xj[Ѓ_5@%1PC . Lڇ4I_P,pB t$$Pzti,KioיϬ,7 ]<ѥȨ' Rp V@_Z Cti/9_zH%6Hy:*j`;a0 <c3<bW1E?+ҦR`z ib,xC0>SPNTSw1ξ4[o>0cv sqiR) = Bj !$d$$ HB` $< Lk'Aax"`2&m.h^Ap7cLp:#7NƁ2scRO$1(k|4`?r»3(T/_3(NpYuw1Sedm#x / ƺsilcK[| A`]v2Zj8- 1aWAvCI "KAJ(1_1qB Pȹ 'm4Y<60:'`zBu_R8t5 wg{ s8C\S}/!C,Ha8_|r4֫e(?488Pk>!PR9H\oř<݇4;<2ҩ^_)h"pت“(j A)׻˵_y=0`t&*~ʾJ_ hځmh fUjBedJ(j"g$^N-zd?} ŐIB6\gE}w g̒>p/<"FddXH|gme+h\<4K+kӟ>T,eDS ,C=İHGEp}'Z UhUE,2o&٠I')[>s v 0iq!^K=DC!OALnԐ/Ȉ?)H:Tު3$8X<`[)Zw$uP5Qi ^Jh$lfGad ]]--鵬nrBƧȊ`ݶr۶FQ̦e N}YS8į{ssO>z"4|X~մCEFnȦJ%E*Lp>"]Z43(rG9P@8%[α#! 9DʯZ&&1Z:gB'; B'!Uα#zߘs$bU'<r1(0}z ͹vQa֞ &n%H(D`V&p,, *]Дr65%'j,"pP9bn`Yk{}{=V[iRdВla'Ren;y$#to*F|+oޙNESk2I]#4rZH"^B񣒼m)ݏ{'`zp] =IADg{+yq*)WqDD`td$T-*~.{@cl1xjN$UaMiܶ_B]Ԝr_*GQN㡝颒hOMz}ݼ=یqS\ ̈́r4"ezVVhiSp{T|_vmÁe]\P$MTFMm\O͌%>*~aZ3 ,zuqJ],n8eW\6U–Ud!?{";f^{QIfuv%D,7rqeJ' FFBOuRC%FPP; +K/((K2PAbAqȂ%NcP͞b@KCƜK!k =h37ŘfAC|at"k)ݏ=*p11o\bzmbX P\D/CLz^$R1rgK]ƭuDc  ~[G֘|I^øc̄ P܁$Jd(r(51FP=kOh*bBr9ރPL$(sh21[aR2fy] >0 y&€o `Mzq92CPܢljRB32k1nXb}A?ถA:**`H([Mτk@c 6&Y*@o=RIUdHf ,:K:pkKe| 3J)% kDpyT1[GO[9Pf4_5"0 TL_.4hn*G(&Qdaȴʻ$FLP1W܋⫌||vLhx%W)-PLp9HH6 :!7zL?1:nИ(|'}-sFÕ u"awPlLCpP=`{*S:ǩr^jb(P,= K nHU 橋*{pXJZv=驇iücCW/Lˡ$|Dp$VH^php.1ON[j&ƍP<IQUf bL N!='[OiW /'qL?B}BQ~9\f#> qQQ*;#bcQ yW`(kRہk-5|OR db[ qEpX[(%C@wO(n" ;C=pVV#:*X#g^*1TS";|PtL+%\P!s=YBTRmRNxEA0(A(V ͘#l܈RrX[$ C!I3*&dc'dDvg8WH "D)M_) E,$G$k0?1FBNWa<P̩1E8]+L5H] yyL0w9L^Zcc=Aa"Џ^ Xk('8pC ʁCTZXpk؉5C$8gYK ,J Ѧ|7Nr&D"F U y^,cu3]؉DЏ 8IZXک{*,,+,8V" n,o 1dȐZqjLc"(:nfy}+ 5|ԙƃ&VQ$y6+$GMVQ6xz~jpqbI&#f++55|bRpj c79pF#xdu)siqTV}`pr.w;HN:I#_ I 7ޱ7f%b#wO>_XKpBլgPkDP>+3'mvFLV e_P6;zf*ǎ2tc8pt<_bK`Rb )ϛ_TxWl*żR*-|aIy3?HUCO_h=B]܆㿠k<7s7VnFMUCc|4Oi_Yl*٪EFNAXA_d |O/4G_h'O zz_ 'O @N)!]iFf_M du bY½_b#6IOXq+YIJ n f45A>ܔQ>]NhUuS%Y=c[D) G(-,"Qd (:O4O#'(3<QUcȅfGU7!UXc!ROX۩Oп!']5W*=GX:#420Xȅ7'^l9oU1RGoLYYtFګ4Xk)3#Zg*lGļh?ƓUaTdhAiΌ5+Ooz)o)빙FnF肙aDל.*aSW9@ozqoyG#QQ7ܦ6ralשĿ#"7K#IYJHmZKm^l9=#rP/6\oP.Dұ*rGlL޿ob2_'OuBP&ߖ7ONxߖWۖ'R7∔tc"t-*1/#D4A}8N&DP4@@/Sb-XN/;5S7&h!MfrNXSkpʱ}Zr*<7PW QֶBKFPjpl<7P%V*d&e*+,MU@?e`DjOà-3$?>AU0BNI*F*ŊQ}M?XsaFijLe0tI/蓎 r xV23s~Ώ_d>~CWd1 M0m@3n 4eWЃ៑op Tù}&, >\K79  6L4*onA(0 DT열Xh&@CY%5OhDb/~%ȚⓉ/k-ϿdKTJI`MPL%,J @fa'%a$dd< TLPD(AiA aC XTv N~׉s*;qvXAr觭LD>-kmO[`k{Q`5YŪ3g'%Sy@9ZJg#h4R|F#=ӈj1ѝ4EFӠhiLk4- J'6Z8Vtb5}'YIL43OўNl/ju,d] I/ra}_*DghN`ghN$^d8SJ&rL!g~/ Nc Ic$B Dh6P քCE!A d `  cVJ:K5%M6t(}lp{RcD:E`7F"a[t?$ǣD3\D+'[#] >c\U7@pdM0 \kIʠɒN롥.48maYNkHBCL\Yrc3+<_XDOtVw*#M܍/'&>W iO-? ips,9aXeFXmpR"bB=>9',k *UW Y*Y'31Ypi1GE@$WxL*~ūjVU{`mYA.jk4 -}|`Da pAKÈ "8ڴQKu*CE!?t= !9_`}dn[)|D)h! '<,,aI\->Lv9~; C|7aif?Qo:?Ӎ|;jkNJR@Ó7*&>;a)ak <ȐNM s֮Hty[:_{N aKykP'8rZM}E%9=6OI̱ ~Î/XxLgxe`0uOYtϗA--h  ¨hFm[L,Jȴ@ЬdQy+z)!q)EaOg?_\vNIݜ}(VZ9-hyNZ=%#G$˸s 'g=6޳BUu)̑uܞFUaRsDTLjAPΡPl~R:( HY.мT4cq.1,G?X-"e 񔺰5W h#;|3U " sN>̪)cEfӒ2Z|^Fs豹[p[T5 N*Z͡Խ۔^:T.ibi7 B%* n$$LYgJ(Ԛ5^Ҵ]ٙ1PX( Ԗ"''O~ܯ<-7mٿ45/%h+撣jikFG(ek5$~D U^]̛Z.}S#U pksV[?k-XC$a䔐]ʝPOM6',%18(kE$iZr00ZŻl[tA4C ((AE}%;lgTP\) L=}[DG*њ~jyGppUTwk]4]遣@럯ʩQ7w.RH"i64)ʿ1ؕʠ!mؚb[)[#Fil(/H040@pX#Fil(/H0 RL)#Fil(/H} ?,#Fil(/H0?`HbL#Fil(/H0x ,$|#Fil(/H0X;H0pN #Fil(/H0O ,LX#Fil(/H0gСVǀ #Fil(/H0s!^7xX#FT4ʅHe/o H~xJdbdoc.dat 7B{,d/c {[Bed/dUhB%jd/e #rB7_d/f4#K@Brqd/01_#l B d/11"n|B@zd/21"41pBǸCd/31ޒ!?aBd/41d/ThmbV I PNG  IHDRq pHYs+ IDATxw|\W>L/Zq勇4 %BPX:ˆ˲/쏶 ,aЖ$ݖ%[hQ=Q$ݙ{sD"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$D"H$G*<@3f7@ q_#H8b H /X u4EY{} {q }D2H@"7/U8wRcCJ9͊n!3y/#I:F k?3oxO@l>D"v ]\@.(V%@د+T{4= F_SęG}0mO2[kqҔaqt4xI090M,rg< ` $EH$q5qv5y7(<J=/"&)@W"d{5u֕zl'LlLf8c@'RܚaɿYSO) K"L3HfٽW nDW)\, ixTe- MQºҐe^Vܔ:|B5`<^^dX6`R9f iXq"cg4 !Ã@SyΤpyqT&8FGH`R܁ExN䙔WKUPYrќїT*=*enmM2P, Yr ɚ$ "Yiq"@$NDzr^͙2n1S#2 QGj/p 8O2/^cU E!0-kl;Rkiɒ^l(8˃^܆7)W^')]lCW 0 ݂Rׅ=86pʑXt(cf-{uƴ1l3R8a_?k(,&\ض]i6q6+|9Ÿ.881J@EȥQUFM]0@O7e2pGqnr=8 %n(qH9h6Kֲ xSq5;gm@zf AK78RՐ"PqRVlLۦ+WÃǟ|$~5)pvjk9(*aU~6yh4|BB5^Ƭe$'Lu4gVWY9[4 ]1i4w>4^r>2B$ey8D=~`4IT°ݶm>?,>Oʬ"7Ec p]V%2@U˅_j*a]ǭ^Ey2c4_׹UxdL9ʩ(pm|Vqem@BPRi4G׭өi\ĥ+<0}dCt% eLr)SmBrAHrxb(}_qRJ끯y5MmixP`Sm5<ɩx {R1lXa" GS2nǒ@* Sva6G g2S~apxf& 09S@Un+/°)y3",@YW.ǭ8IK'5gܰōU**UnkY7Gi( Heq>w ׵Z4=*w+5n SEtm]ZVʖښUu ]]`CJLʂTr\iY!;JԸ]k0OiYb-%aCI 6`{]8;3}5!xQ"𪂐XZY;Dÿ 0w/NGBխڵt ;^|m[@祍~&N9Ty5pOXFdth6P"ò8қJTU/ ֜5:^2dR# @'«(T^Jݘ' 6Pzb7 XMI?tlaeENdyɏ_;cOTUL&5X][YϭQ!cʥf.##sӶIW/"cM+y%m8p->g,%.^GWD~$O-p4M4K\A=&q%#1r-ۦc4`:CHSLN(z҃ Ҟ)\#aPRjduemslt'ò9ѓLPUv™#=8I" ¹AyJˇFc ^`8mStZ) 7QU=nD8߻D64!uő/$GʵU*|@:C vHH *Ϯ,LpzٙM҆i"D @DRvix%tr6} ΝW$ri8aO;ϨB7 MI*wFdyU)ye!~㫇V4H$a(][W ?LOX:/J\*y>캀TP]5ڿv,Gc&SUea1 n.2P8uܹ]ean%n_lZ.-͸Ttx Mh,Fo*WU4|Ice;VrqP;YsBp]iF;_"/&їrWWrO=RUU4 "ϔklbٿ.ESF3Y 6gF*^ؗQ$_IzfӆO']1rYǎ 8βmzcp[AΣ ^Dup[ |AST&78qBAjũD8u‹*tMQpq"Cv,R8e<k.畟R~U\XaWg‹CP4xURY S[dEu)Ȟ>3 YWpͻwofks;˶r*ltVPVU8؜_|h\[CkIxʟr(BқLWITJKP =B8 I pMtPpVea6Mcj %r+,N/|qEH֚v.E2ũ;Vܳk#MeALɽS$%g|xO<=;6Ǜ7o^b^CBg/pJlr {)< 1ڦ}iTFuAE_UQN4u(nU嶥mx4|熵axmKK$_*m3KW zt;K$seC<5÷lgM]9  ˩^%yMK&Ck?FKI|aߋ{ E0f(3&$BҋԦ^öh pk*[jg(q;9 M^6gltʎxe {ݼM.P$ͽOD2H6{BpϮ |C?=8޶ qLak}hBw說J`~ ŧ\:'I[BdH0^S ī$IBSnY#k|> ,`?P4C˧oU\l=zmXG~r7UB`MAtmI%('0,PPN"c?cYi1,;sdQt+9@p >G(cL,ϫkgf66Tq2O3ɍPs9?CI4E7llb"}}q ֟WY^҆/ڏofyt< 8["M y  TlF, U6:c=`AMMI'C<:\:ڽUetfy]'@pfM>o4A_zO?T i údt'm3uVUa&2Xg'4 c&DwZ{*錣``Gv属^,&)Od| ÝNW>xVl%SJ``$kwOPLaƭO6I<\eM~և]tEcs֌UC "^UfZ6)E+f:S]Yj+E0YU0-=C^?*v,'mZ|hOL ,2r,iO`4CM$~3(h*^­XO<1x4m\ dAJp%" PB(eskMS߉2Lg`gEU.˶9>\+o !ovWԖs*ipCOKb1  G@$CM?<ɅRB0m:SQy'xOo{f^K[jj},]h~>Ns")iL eq,w \ܲtɌdzY8>AlW[EI*]e;7hĴOr<~‘!p*S{8Z7oCLFAP[$!W4Q)#@;WDWDMl);rcY&%;җyo!89LJЗpl)uqGoڒ.ƚ|=eAj Ix˟99Lw2P4C}8'{55SPN+:Yƶ-pkTx&)yX[e;UAJȎn" S-δ$QQ'{eyվi ; Q7yB(՝Ne#q,kw̭[Q{@dj&20tfAk-ۦ˙Ex}lj=*w5l?aiE ie80CQ |IL8ppv}2LwÑ ?\#.)iL P q9ҟ!Ufߴ,>u=}@[ m^~iy?!z [߆*>JM{/vje̾Y-Mռ玲n{9:FW4fn]3`6l t>`EymX9+9~MTdlo6 536##< @@Sgiz->ӱi};7,9[L}z@$ޑ4~x 鳁CN%MWqPEsk %|w~ZWGhO\"!P3sBBT@hՄwTS6~mۦ}x;cX~Uf?t"enD6Gz}&ڴl^‡䴽D2QF| 9y΍ (@r5Lt ۂ\҉gsH;YsJr+}>=@0mfFpp%96BlO",n |U ANF 95D];7.#eZӁAF@by#InO?=)7W޾$Y+ND~lF}eC=ݙ,O^3]?Z:Y$-n j7oEGGhA$?%arlJn_dΧ}02c_dދ6/e#GJ͌PP[Y)às 7g2P UW$LF Xuu l\mqƫ[3^#x!*U|W_?:"=m'η#^ZnYՂk]@et 9k_ٮJ$sF@ȡ> ZY6P4[Shsw;ls2MI UD4ʟNi3?Y**lT/DKYq !>#3vnưm<— a~;fN]6+;?ggx6dd,+sFMP@ř Xs}6;Ӷ(B:P]W+^ة7?!ˡxIY (ILwN,X]SƽϽ[~|" 0w3 (`tܽ M$a8FSs6k &SxW~h[jklGPzfX`H:-'6p(?ì,bH:N =+qIVq)՝ɎW||+){CO;2*M:e'd_ 3>xD6OԬ+,Fű2E ^ZVJҖŃ6N1V q.UBֲ8pgƭKm1 ~t099!] QTAK{{51>O.} }?|\Pt- qv:̓!M[4xU ȩ?gW7tH8佴{̟lvЁ.=ُHa?:dC1FRӽw\Gu7.*>KMoXVU:ky 7 OhOOQ3&SX@)T=&4.M P AO&;t46TWm.GZgU{;/e[[ S$3x> %>zU/Oz榒e÷wFp*U\Z7kFQ2k5Y٧7CxTNhwqsUUtd;8[mMQO)*sM{* ³)߰,:ɡ!x{tbG/i>o{nE wY6_;2̏x\43/'ٕnyC.޺4aY#E4|Κ lLbSNwZKas?X͡!De<:/|GGE{p@ESY#,Ozק=CU%A}=q~x9sz mh0N'wC@XxwC6G{u~ݓ%ew|4#Bj4Xl炼}FVU?ާHύ|ciO瑁$˫Jy h({?>'K-п^֫Oǧmq۶ ߲\SB1`m [|BmND4&U6~ħ MϮ({ȭW,ݳHH°A i)]s#BDSzcQ|T\]KU888o::'0,ߟSIF6dbH`Āmdz9NF z˶y) pkwcB٧aZ6 -;ֳxOdbyǚ-Dd"c$ں y&*Ey3( bT[ڂT Do_1c}[ZOS{h۞ɰ,os7'94Us΍s{GvTYyus&y1]H<`2ef&ƺ䰁Yyt7Oѱ@Vm>t?'966T({f{Xe "e%ƃ ;!6]Ŵ,;NHIsq1HXxE3KX>J RT|3`2ů::I:o[rue="`UME, z$kӣ4uvTg{8Sp,ûa m7\q:񫂝 Ll", hX@G"=}4\g򢲦}8m_d+# Rs9~|0#ieMETUo^]VuKJ>)L#y 5~2&׶zORmw4Aδ(u);ZrecȞ]eیgF"muWe$kr'>Ú|(4g,Y›]>Oc珝t5!w,\ o pEHh>IHϧg*4S\U<=#<{y#ضe<:8<c[6{,v.m%ӄZs 4Lz5Uo^\jNJj/T{dp)h}`n 7jW%iZ|r*Lzo:O nXY23,Q[! Un׶p+:oPbSU_&J0o]MQع^ a/G-I=$LghΔ)Ev)@PS\ǀ:8-Z\/ɵ0}:gy$Υ 6|f?9K >"xɦ TT4a9( Z~6p)";pli!! $ܰ<?Ec*TOZ="B#71gzec)>w`S̥+llk]߭ce9_눢*^Õ-5=1L30DlKTW6h~v4X*?RZYb(Φ48ޫǣ|)sM c|?8}ʦƪYL5q/ HT jQQ58Y4.Os`Uen?-l:7V9'O |춫(y(82zd5|zUa"ʭiDWxY|q´ " KSH4o-uqgTN*\j^h`2#ԅu|;=i!aX3>qcnaQ^>x6VՔ@fӶ8]#k<*B4#44"+pMy.g"TH}q)I,g1Y7\+ˑ=ኚ2>tvjC~+Wx8`m mOR{vmdie bv0MQtHͰlp֢'mYQ*U͛2BKsdLMݫ*@"p)V^U>U <nEV:.AE8D!P4E 1*μn}F aqzԅmO8.+#& $A{VjC֌1 vFi Xe;ڜI);ކbRѰKgzH}I\ āAqEE=3p?͙"iի%ے;ŅffZ $oH $J*;;;.a]hvgι?<‰^6+3W .ȳI_i[XΐF2BM IDATCpRHH!}DADEdI@V^:Ϥ7lIEEQRd};$ "VY\R!836mۿN40k°h i/dID':퇻'|gq2N0L"=PQ* NZ YB"7t,4tuW8}|/--/IqfOXE;jX|r BϿe.%7pP"4 eM$yaR#[/I)%KJ4hfyB$]iOqWGcXpj>4D T5GhaYXw0M&C<@KX ("j S[| ': LPq7{90Οӫrh͗2i++7mLK$PsL@|K_E"HCziT2S/wj T=D ҹ\{2mR%wF>PZZi[QW@$T)91dDދ"*k Tiƕ';+ܜ f>7s=˝J,=0,O̰;UM[[p9y]0m̈́,L<9g4iDߡx7mY{TDjxy+}ԷiʴƊ {1- V(I*UhfK{:p8DJDS!R(wJkIC .q§v@(o?(޻0$|sa߻eCoa2NzXTD'bX &Ltb^QN(r1g ]*2.DhmZK]j4(taNz˽0SkYDuaY1XjJm^Bx*]aw1@;Os`?=27wT/D->qÚ)7a2IJՋT %ȢWq;sAݴ){r;V}73mR$7F2;Ӷ,E)ӼH/^A@u\HI-ݲ65MCCc\Pqvd;.En\7?a>ϯ-w힀dyR ??:F°PS߲!hJc,@cIzϋ"I.9_*N9gө$u{U;w m-˸IDehuS *%.7n7^EA9%.yO,Nq-Oّьx=S?p>k}~ӣfz?5,|r SpUxU1`S3.8do̴סPq4Zqa!*7>/פa- .Өvfs]I?pj^Z!\Pځ*ggۅb`F:$IKC""LB2;u2NEؒas$yp[;VȱEtPwϸYL_ وǡ;;sSk ώ2I17CDu6֔tB%bͣ(e9#Hu_r^݄VX˴-KH#T$"%.Eԑlfs.<Q$n]ɶr&53P*#(OR|(!fWK5wˆI.Rfk39U+ϱ {l)txwUYr@@[kI!A3Ӷd"OO٦%1͘rD%>soZYGʲ#\ 9Nc ' v+sH*t͵KI;V>L۲ 4;OkYa0WcehP p5&'EZ:Ȣ6nCBO^#SI9ʴnU|t tt[Lf+{ r69;=iA&vdt߃Tr<7@E~ܽˆ/sja+'ʱq5;;xB\3\5U#*MeE@ :*(Q?"+2fgO'є\(p߆Vv6W-0'B+v{;Ϗ2,rSvt3ɑ@[5-A`gK5=,)I}e i D.DAc;:xfDAF;75A´o}+0 k>12fMBjڑiV)MA ıW!@Beܪ}5v5DŽ2ග݋4\ -^Gܴb(G ܫp'b|l{wn̩Nr׊ijd*>w:P/c`rF] _8G?|223 _} $/kr/"I,[V+c~2{7ӣXR,HAw{6T曘Y!I; DMOh$e1^~y fX|^S"󼒄 Z]EWN~-n'+ˋ*I9O. 7pWE[Y&\C(P3}yؼ"#c?z0:;( kXSU.kl"1/D !, rY|~x۪:吩-F?yUmhŰm D^u/4zzi廷s}SUˁ~2~y "2ʹ-K\On'k'YILӱ4NL7H&ye[Zz]nuM̉aw_%>}M&_:z! bNTәN'Jz,(f؜ow%[13㟟]yͬ@:V$A!I8% Uem=ěѹVni!KN}nÄ456ӓ/ ՝;*ft2=H6m2MnYXv`_x'Grt`޾9߿x9/';$QMExLub6Eܺn-^ &#QbdP n(DdAD$Q@s`<ڲs9|mS'{Y_CS6P$ǦmcZmN1,ibX-YvQsq* ^"(xH'OkdY޾ 9-BkI!]#[F|m(-,4ӶdOCgJl斵k(p{"llj5]'I8``b3Cf2I/`jg=@dQ)@(Jㆦ*TYuYL{WKM$Q oFU 71m ,fZ3a[UN s  .'(e\C_57cE;?ٳ3֍ks˦+ʃtL=fڦLSo] A4 DI8d4_͊UhU/ܹBo>As3̷^8T<5*y#3= Ӊs}=(87jxQq* ~BUQp* E~?-4sF}yÈ{7scs>gM 0l7}@f6d\\]+_s+k {G '5|.]MUuA>S7M)X2IR0LhQN 025M\{Kfk};gM騲ܞ3/槝 0/iɨ2~254P\G%YtwPc_4䅓sϣU/ƆW 1ΰI!@+pjK]9yCmY44xo<(^]7D/ LÜ>CQDc6( "+nOбc] ۣJP8Ʃ);8pyQGF0-7uscC|'2mO&ȭ#0(9v)?#Ee0`e]-4H'2th/-!ҘA\.g{cB*z)\5(??zP\ߌIM @7-^!KjCW_?x1~q{Ggu#fڼEaeybh3_7F. ͧ:XY̴- N0y_(mQUvs ;p, TYFe=nJJD虜b2d8!܊̝V_A[͹NC7-&IO.cI ]e|>p\"'L{YºO>sxtk= *xɀi\Z1kuedžyyM`6ksf*ʗtRlW;-||D5H2p4x47q~?gy-eܿ4< AKII0-6G@ $~Kp~`g53S7\ V.%hؑ^ ˶9?1`8̩IN M=N>dr;kb61`<x{f"z,+Yp 56gwc<|#)ys C&oL۴(dږaA:z0$qUܼf U w2_/ E4'R LŒc/vȬ.* ΅3>oİ,~uv !1K`EU}NʛɶmgsK3<ľ3g̃rSk k0BBnk'GG2mb++3mǼNjӇ8>4x|o"Ӧ]16U(yy4 LQb1?? ׁS?7:EĬ[ eULB7m\'{JQEA:XU^N37Eccy97<~'O]TWCW}xѰ$/vA]0%oE\yL?VW/+79lcӜ@(]#X(nJ߇:`^ɊEE|^N;|obA޽5Uř6XQī*&ʴMAogjArژl`$x}cF3MAx<iՐ}+S(ᅲGN ٟ l)J!MmϟWmL۳,g %Udږ+ƶmAM+R!+l6ptc4#аlI+ P]P*Krin۰-m-{;**ʴyKٓrԫH~h-)&LqlxX'N)T[GT}L7Q2珳{%#v=JGyY΍]w!mUUWj3/~<®mh{͓\-m54gU <{_5fXN@V9սS˽DS6~|^},S[VLbIϫ # nuk}˼ \H&?8x'NG*ʅEFz=|[ov>>=7ڌUQ; Z&,'p;XSYw>4Ӣ[֮akkk_ RO uID%LY:v5%.]ml΍Osfl}t tĮϩR[y s#cs}nwmfuef*ޱ{bo 1bYN`XUQl}]fݹy3o޴O~1l2iSULF7MB!'87\Lɽ[PqYyUi^&dĮ$RTLoFf3U-r.ݱW?9]Ó|ej}`CQAX}Yf\uCAl') G摃x 'BwS;k+q,(8i. 0nY^$4iUiqJt?u7dm}=Nn?TG.A(z)zL35P$#]=|qA?lY`бh[J4o9ޞat"kF[U"y3\'r xj>lv oeA(2Lݝhu+y׎ œi~.(*tH:'FF Ͻn,H^(zXQZϳ񺜼m6_yG mh$^&SZj B02dp7I;;6^<) fCS#wmL]I)]eWfl J12HuKmc6Gp*2Aꂂ0y"b߽nO>w_<3gyfv,@"xAki!Gۀ5y  ^WJ$weSqs-Zݑ_|jl4I#(0a(H*.ל4pM׉hda(nEӉ,8d9\k0UʹUV?c336S:UY.f` ȿ~y}#YΏ֭5)/|匝/l,f:`,#҈hKN$vPUEq( ⫄"l@4t@tbq]0 R NJz'_gw}r34$x}c%?:|F?A @𾺠yRzӉ{5~5>@31mӲRMO3s(ȲG@u8 @e %$ D8$(R "K(=cy2n3ɛp|Vxh.)X&D !4L3 bnn[];\`ja 0ZF3M&b1BQ-E8D$O¡(\.EFu8ps(I24M)M??F$n7nE&rQΫO^L}t䑣:WT{6Tm{`J?{doϏř#[ ܨe79y5l;]xA))3ogsY,۶1mH 4%.,z=z% 'i+g$I&uHdS@2 |\#_Ԓ9:jk騭CHWϞ37odEyrp'dvz]^t:v_nZTMޞt!r@TӘlji:S8M(ιKUнr:*/Ƕm$:dH*ž^DAtRv5ʵR\_Ǻ~ouKXQGM pH"K:ߚwloW;8_yab7ZwDͱi+4971P8iY/|éYqr˅{&\f6a0JLt"Y, .o=.pYS(#ʛ7n/~c?3guR}E%<|1|F +0}MkU%oݼuuY.w.´,B$1M#J1DO$j:~Tլ_AP"JTJ#LT 0螜{r U)z*^J˙O&3@UQ_~}|-]q,ƲGXE4\KcǍSu"I"(.S"K\UiYXE2$4&q&qDAHO>%=jW s i64 {y}Mm^rKy`p#b=d4vsm)ktwJ&'&'韎VUn\wxnU]\k\K5вmN0' Ĵ]DQDVYrv:T~ "((^|^/eK$H$f$ioILGFe<ҒYKs)~?vmTV#?x7"\²w" &PEk_I8iRY{vH]II~j_`[K' B0єFBLb r9($K{l͕y7ȿ;nZ^nXŝ7YL 1S0856p4鄒$( Erf~6!"ágzchiIB$C Pv,?8Sv< Ϟ[\MdXP@ᆦm8?:|3Sumܹy3UE#gq 034: '/6$`@WgIpv9, 00 p$J2¶mc13sԕ͋gxwu4r%{{7sLx{ JE[<.3OG`6%~?fVf|-}ѣ˅}X@8(Ñ.QZJ!)ETa6pdfn QQ fLNO315eY8f֮ZUF (* 2ǙL$i)#QZ*e(khX2I83Ugm#r%`MϚ:hy}}Ż 'J quxPVRBMU%#cIb8ZJv=#F'tA(/+m[)*,| .JGG xϢH^,K l_ю(mgֺ'V_mszpQzƘ GIi[/9Ǚm3s˶icWpT*E!LO$ؖCu`6DiX+ jE;- !"5UUX8mSr-Hrhbe^d=mI$02ʱ^:/t3x|emW3h 9,?3TDATidKk +jk9:ϕ37>ua6cNM ekj8yQB!"%(YӢvc6#c$fJ3AښJƔ$-971A4bMeŒigi`68{^F& ۶l{)xdaz 55)/%J11H4 D/Y@"d|b4z<_?"߶sp` Uea\ d2A7 tD7͗tů:(z8$ADKx?vN%Bv~ <3*I0y{ 5_uBaFcqRN0L$&c1&&x Z*+.*pPvS(ɻ>Üðm*Xf $rt>(ӡ0ɔFϋFFUTnjkk(OJP krizKkI 500-PcX=: IRN*^)b8e!u5476T~"t\A P]g"gCuռxoaHhFF8rGF%StW%QD$,ȢxUߕiY$ `_m׶8K3S.՗ii#QbnZS)1,ӃCtR]\ĖfjJJE Auͧj I&%Ŭ_.mTWqqC!F{*4Mh,T(mȲLyY)Kjrc:g`xCä ^~ I4 H2 ±'9X8v!"NY%K Jbk$m' 3D0tqYzѦ9O ? ,rLm1/]dp`iG |dY)Tk5|>/pO bmϟ'5 \} X{LS$O&𜮫r2+L/) J\l?@/ rpl<.ikc~C90lYYӻjs~ߌ@&(ѥXta2|>OYV tY׷$nZ~.C$_| 8(;{8OѮq/T]g:%aٸ|"gSq8twuF75ͽC!.'~0zlj. b IQU,ʕ-MOip:yVLTAYQЬo6:J jhRo"]~]{K&,I"[.)0ׂ s8͏0lؘs1} A҅ \:B; |^Vc?7oBd$YRҕ7uoi%[F] +_#E3g|xK2_8wvo{oiblI+Yaq3(޳mcpˉQeГf*k'8fR۽cm>XrL2OuJLJEy07σyv;@s]$C!6'Bc_H&kn^xvc_"LQXXZ&  q/( $q!;Y1$_<GFH`!do>McqstSU376)j; Z%qmļ^/6J؎|3ڔ;]5c}H_0@_m+K()*4ә,y^d(M\w?rE[@EQLMUq\ GEEet|Erbx O(Mz:,+/ݶ x=^qc}c;\9}\8{)sEO߸<3LȚƅxH=Yfqstd)jLPth.vvcBuT}GpE%7]*9Dݡ>v!XahnX$11SdVs9V* `2Prx\Nb1>{9 =S@|.ϋ_fp`J'M<%}J jG4rrBZ%ˣo׮98;8H"?`!#_ oL4MVYe|ibՑM b2AߚB(EClֶr9<̹|A$'(Vek90g vuk HFa˸Pqkj*PK %Nn7PX s˓Vq:\x3}zqfHV)T%p0{|$Ke*Vɓϙ~Oe6ϽƃC kj UXIX)N~[0r2pm<^ΆC]!o[C{jw՗{^it,vA7 -A#0K2s]gK?rWa&EYfXbR,[=eE$;)W_sΓi̥R-nIa^ϑLӤZwT(]@4?<ׂ.IIwH¼ǖ+s(Z +~(+D<M1c3 vE ҝLZZbfn\>O\& ܷ\'HL._@*M{=\L<=U΅GA4$I~kA3}-W*-;'- `8DClY $U, yIz솨i7M3p8bD# ,S֐eñ/q'M)ʤ94Mtͧ]# v*n ο60IdU>G'jenPt}7尴Z`p1iȻ%jJߍM5D8+I\NAz;W$U  OJOWW;5b1MX䖘h1L XZ >Q9*g¡M:4 j xK+~ aiKC"i7SK|mde0aM)*vH"̿b+n<y̤sCC| N^~^x[e{#ØI4g0׋f(?aDx>m1 6?ߡ($Ejա ?~8ʭTr<ȵ;̍޸+" R*eٚ ۅ(Z?A9>2e "NY $&*?~7; qY_gy{!;fRQm ZhVMnLerz\4M>Z^?~t$/盖0@8Hp:363cT`֟u#3=;@"wޮpQX*Tv .'"wWʃ-Mz-ZSh67:PQUZB3 F|&}O M x\NhƝ3Sa~"Z)3EQ}> Q"A4=!A73}qnj?3s)4ߦk;ߑjP6h+4_?LMU r.xj~Yѐuӱid8D2DQ>x;ZY,v:Лq -՞itDn8㴉nl1Rj@S i_G=h ,(e<ӡqT&x{>EEU%~\r<=856-x7 #NhK%f1Mx3&(@Ry/g~>㰱T,SZ44)}{q#cT0T;UF }~kGg˺AQRD3r:OD1 O0N9H̭w0 x(@wIAM(h]䵁~U^`T 0MҖ%ҴjkiRZ %QtG,3q;B̻{R o  S.W}!"`F.S$.{.?P2i, y/NdP^^=Dalc4LI<1 d`TB2̅hm%4K {1q9d,vn|988Beӟ!'FqǚMwS|}t|{G0LsrpFPVޘ;3TTހkW4b3L`S쀘Py0$I;wuh1{Mluia.ǃ밓*I>\Z+{r0rhk4`X2E^ |"~8!k.S"j|xvg;O.zvߘ ,s8u(J"&vف8}A/5M%==K9=N&dPB+RgL&şޜc20M^΄xcTJÛ\BRTҙ,˫k )ӂi<^t,Zg]qCT|Df@>fwCOOgX*ܙ>$zOgTHi2p1b_c63EA.ǃG^5 [ Fo4iLLO^䵢uzh,|t.BC'h KGc&Z?z8&ip9jWȻO^2Uz^tHiD3 nHFzO:jJ#zP?K&_,2jyc77Ig2m6@o.6UUG=b3x+¥Xƽ5( c nC2Jڝ$rkyY8RWpaihKp(П! S33T;NcD`' MӸ! 8 q()*v|͘!/s(W$owDur|@`=<;|i[[dBnF9S9jW4(w;w&@VEu=Fu"~C'5 Yͱu Mq.D|c|T-LjnT~FROZ=-OfS|*aqޚO:wGu׿ kǺ\wS-o}`}s v;]ǫ3  S4=8|ʉ~+52FRO!xP}1MkQtڹa0ۗmo=(v8p;\( p+MY^Yԑ~Kd(?$IO`&=0zUA}heuXՀWz%dxr(u7j#7FZ$OPTpP #$`[ʊx\N\M>Pz6:u03lw/PUUwș[av*έ;weG1T< A|IAn4]>??襦j|noMN)Ry; ԟvInT&8cbM7 z L! TNzLgjH /lec3X\^!_(u9ݐzc@ER<N;,_gttzX[#D$U`su|B{L6(D Gv۔-_g MГnY\Z"5$fa]901=i"9AUm qJoEEQ,oLN:BM,PN$&0^US3\VznV:17VEw4iON6,f`p1͈ܺ}EQ芆E "g#cD ᲋Le>6HM֘󍍦K0fm޹j&Wp}x!jW_v/{TL ;> nl.ĩyƓ6kj>s q!kFCBd+N;+k|ud9kF-u7hԙ5f1L/$lT$t$ 4Mvu[rM44-ɍ> Az=IYnNisQ{L_AZ|gf IU7m`vJUTa8y7`ӘΖ3XCy3]q7BFsl F>0M9jD@OCz-obso66=1.6F668mV 4m;Fr[% MMkWGF<r;JIwT rhnDgz"s )V҉}$9A?kJM y=AA@E,cO8l,K7S#% :ݹO&8?=vzInkpПb&3ssfr>qL<5 iܾM莆iW$- IDATtlg-aA[+|mtbb J `(`QQüZ4 E!99iң`s<#O <8EA;=[NS015M._ us;ptةzM( |6 rT(g|69K`q&5KgwWa Lig#~l4MUAN08vVWYhtH'“Mi 5|*6Qd~a'ZkIl"{ RK|}tJMgɚb]/7:oucː$^q&Z |B*&񐿩o`zb|W I+f4M>x(„K,A `4}nKz"6RMJKUq'LB5]g2+FX):\pA`2mujt(7vS*ia6 s߭|d,LdtbrJa Cw4 j>7?E5>X\̶J Ѧ~ j(_LLYT]g0XzPQtd]vak&<ˍ4 ՚i"+m\ ,.-8ӼO4@ E h=Q;㛙-)7r@m|-Gix|aL38ab<`הb ξDDQ _RVN&7л(|*ŭwuA9(*Ӊ 6||Do4BW$f-i6znݠ )LgslT*ZM J;ΕI\phudoOrwm0 xqF0LbuV%r/W(WHLowcla:FFXZY0M6C (RdVds}>bx,3:1$,#"iz(ⴉd%See4]'td3arf(ravGAEjLiRجA;A/EE()TT.gTr̃ t|N9.cyu@*YN G4ޜOǨ*={"$}gsnD[q9IZ#bSSTZYIf- 9޻e}+6E$3kj6ފO&dMdx}36xۂrqҎ ,PyDù!ro޾fڒsnYLf}]:]Tk5ff[jT-F'jDa_%~W/^|_|_@$PIMqSirAX!d[بHm  /Ij|sr-l7n_ih LD LMU;O|L-1ɢI@.UUC n'?Ұ"{x0bqyp(Й3(sr?3Y{-'`o@_7Woq5z:lӸkd|^ *%Y%BO" WB,+_[gT3 Ӽ%c7 wݩU9,+0hΥx]]n񸜜ŧff)ˍiWLӤT*frfM8;xO?Ys.~?㋟ n0~|дUo]鎄q9[b>lE^^u٨T$ߛG7NZ҉ ҮGn[[*i8E0L! &H @_"_09=+^ntHQVcye0-~Mnagoޭ[]nݽ;|@Owϝ#l&[{j$BFT7."k:&sج'Q䵁8 s 7I7zOlR,+moҎ7 Ӥ(K'b%E+hyx.H/6@? M ie~&%tǿE/ ]|J?i~;/յu}6M5`ieU\GK Qo9_ }|n N`P/&z"]VT4yҮs&.Do'-pgu 0z D[Ĺ 0<-. ]̬353C,i71=7K/e~s={ݰl|i^"[}"bgBlP/`&_܋m]c~?(JOBTʻ f{$`:0v^Ih+"WKckިT)ʲ } Z\Lg# =PB\4~Ɖ`븜N?e~~>E{v/^8wGF K|W|K%bA?6CyX0:f-rPlPOfx=JG'x; ˑj`5)))jǗjutd,p9|4pIө:{KPw&s {m0XY[?`yuk[^3].OS׮/K}>ҙ,z{8+dy.'n{ݨAΏaվ!/&&/S+uu4AV>IzkN\4H[HTz9)|nz $dE᣻!R 7_ .;wn=!6u|?~op㥫$ow?AQ5)Ax p1f 9"3Mpwun!:bʵ5|ŌX o[V.wy+ٰԔ0Mʊ( x\E>33;wloěM=n]>+}&^'LE{\y7 OTlMza.F\sڙdG'j3燣ʸ"/v ?0o'lҮS`Ij5@yIE-5/aG{6UJ48% IKONOf SVP2ڨT_\0#'{恛0EAOEUY()|x]]<"Ͱ[?5#a(R$Mܓ_ʌnd٬ָŋ]M ߋ>`vN6BEU)0X*xcr 0pm\sQQ v:p~d\B&c>C~R)@l(kd&nIHg$#mMYY|vss8IJ%WVX][ի$4L͑d\k3]*Nq/I0'SxsvlMS=yZ* pviڳfʷgI4ðl{#fŝ|oTE(bιpKVdz&( SGy[ xO%:jFji Q~96q|,Jɝu~wj6ӷosǪMeZP0/$B6qfy\V8lڹfK}JǬi 4a`.ł';J&eQwN#p|F,pc]aU>c$I&t< "&zt Tq pwǡikm]FOf"'͒f9;xK.|4M Ip{x[ۍn$[S҃_DA w8ȓj$Wq~dhGv&V IHbI{s '|.^Y$TU Ӂ@_<&噚qjֻ!K2q{v ?z᳚E%E!zzby)c(!-x-l#{OU^tE,Å|pMzwK .э4M0}oRՉ@=&o`&W!^ocz i:aui- 䙹960Mfﰲ(\8ǙdpK_!|XJ4ztjNx@Z2NF$_Byl`hFJVwvB^>봱Z*70H63;wk#hNRZuh 5MeY"O t5-v[= plf$@LZ~8kw" 021q8A|jUK<$6owApO.DBd%7*EE᝛3ЙSS(ʙdThN쇀׃a(TqyvQ3}q5fs%n-T(@?["R2@8pjC&V.qT@G V?IIVqN $3T*U771MΕXgn+Oԧqwgu]ksykǿ#/w89viv:ɗ+Ț8( t[˱R*},S]y,aq;9 dƺ.:\$BAV29f;zT^+ds9O]Q,Zd qsYTI$r[RA$$;Iȗ+Jļh8m\jejVmp'i8ĝ̑MT]l=@ Qtʰ;O<'b7Wb}#' rZ[\cGrdRFYQAw(Eni\Κ8P\6~>/+*I(L-a # {mn'nR4S0L2_(*PZ~[ȣ9 U++?{]>-liRxNHVTf6)jȪ%'r99'NQQAbL^)V*U3Mq40;zRif%.O_XeYY)XW0LSsO9EsQ~1]fARI\ |RnhZ\PVY 5YA"nqTG@ ATM;Tm~3 @ClP0QTVU*2,N/ۋVX.7668@h!~i7 {7bH`&[%j}SL{[]iW+393>>%)pܰģM1^h17)<10qҍcο-Ka [WìoǍ MڒEnə#v\1&~) Q`cSbN9&PT88pj{ӡ*Ig`y];CUi h X6G&&0-T8IVwe=<4 " Ri>7SxY B~I IXoC>ؗ-^*ZtB#s$^ICזuOFҹpQ Ql* t]Ɗ&cfmb 8m8ernM(,;G LEҦZm dB?>8ՎMږ5znT+- ,?uyx?|Q.3 65ˢ2KkDQtUeݧz?݋X Fw,JW,J޲) IDATx%![4Mg]W'2P[962ʁTmKpL慡4%ǭR3NȌRiޯ<;4ޝ e쉛)ـ8V _`,c,c$  ?t "|2Nu/n*j.ooe,1^*6K gHer7%ih-]z{S)Ɗ%\O-gdxB*Z fL |_8sE:|b´ߞ)eMҞ\e=!+n$wh[@S5%h .O3Yѣ\tsڶ:0llRr:@ɲ@SPEQh h p<<qt+:is1th8HXbX}7/./ ɖJgq[wn ۹gw*9Áec]k*%ܥӲIde  ,Rm}NK8it CB* '" d,%b-M4ǣHi!оdi)5l! 2@,b'n"J1Cgs{2H&th<V-q8<0L&_rXs㯠Cz3¯ã) U]vs%iM&=pd"ChrӚmI˴c M5/ݎ'72X I|~x9@,hyRB%7Q՗{aVăKsh(Zd@A=2yTPXtY'!M&sJ`UQd'-:=<E$v5Td0m&9˦?5xfZqb EM(.h0-rti{> ܇|`aBO.Λ$C.hZbc#B=2e 5N,y2'  CwEӜuB x,kJCQwK{[+W_~\ץ)tnDhi (:Å"#"G uцк[9>`*% xrJGEڸ _8;߰\C#ysXm 2%ѩŭyJDH0aC%a2L2hP}}'`}[<8(,OU#,B@S,`*qvѿVSDY U2FJ[8LY PhreMN+'Zn9"#p 0a=y\}=!0mmCO zp*p-> 0`9$.^j[lҤ9% D Pʹk/ur*)@sld0hyۑilCMӮ0ޡN}%R2h< d^?o}6kqIg_J37ٰ3ڗ@b8-GƦu\A?.9KVš6[l/Bpyȴv223g?>~GU=+5#T@麈~/(2r@׈MM\Ci]mk<fnܭgG8:4Šx-g8 `/]/"K,#7# x'p*Zw,LW,D"tz*=mͬvŒX6R 4tFgeZr]LveȚymq<_ @/0!M{arb`;Фa])$ ceq<\2^*Q<#g ``  Ԉ)C+~mɘibQqZY'J=G݂q!9a8gr'Ob!!\uӬu=ҹ<+)AD7q"k[S=!N~2HodYIY #n T,Lk:l&*).n7/Ȏ)cR*ՁU[9VQT*5deƈ I *sG&3YӠ(t[NiBˇp\kV ̾ 2͊ } x/$RYoO'&]UU(]Y +HKׯq=+IG$p|_ 3ZWM> l0L.rŧsH5j*du%"uݷw?V.SswZE:nGH[hbөWa]#u MUC>Z"`Pd,,uV6 \}(.cGs,59/H?%vRG-Eg/щ,FR E'-&ͽA%D"oۘ \!>uHn)aGq&Hq;pA@~ U,fzpHXT,i%gYcl&Q~i ېCF[ʊ) H@U :q VOL%CӈGB yXsHͽF9B-JMUJ@h 8<%ףT*w_@:dC,wSA"Ғp+*؎|nQf1PTT D `bQ3KkB89IWsUUOsx`h:dȓzr$XoǑ&h9@*G@WK&3 FW4Br=M֒fJʊ??ȳH~q_0aWH0(]3&jF.=(86"yA? %&R?_BmHK^SJmA64  *|eU-mACguG i*v5a.rd-[ Շ?Os Q*ר:9`m:jؓɰ:a/N@H-"|B@j|[7aUQ 4mNxIƒń̚_D:} 8-!XH* X98# ]v]X@'oxB8V9T_!;)6z({6{^t܄4GZ" UY΄xYf0W8ȱ/ +9siQz5a!7׫'nmX/"iV܏ߏH YxubdҚ UQiI,jpåP> _Yh7:=(-`lUN d0 $L?ϲ||?HːqQEQ:@ Tr9T3֍姑Q/!Y:'9#=߈̢B*XߔWuuɸPE J>_ =ʃsv.deH/xqde~Xg>>>>>>> t#}9|y333 RCC-' >L1f9o6v=US^)ꧭwk33m}g 'N"292lsƜ_AH5RQw5 ,h@x/=D?)ty6 @(0S[Q 8(~ < .Bӗ#kUU]M-lh+HaxCj2BtSQyuehTMQ'L4rY삚c684˺l8~V:jGX(4_4O+ty~U22$]0IM>wr-7̸L}~-,5{q rLn iZm[x]s5!ty|kߠ-&61I;e 'GY[T*ufEiMqyT|q.1_VHD8k>Fo*C0;Tuva{ݷCǎ39HkwG#q"MUMa*.v!]q+:g ˛~8Ú6|#kZE\O > e X+_B,K{o5U w%umT弲g'p\y#LQٵ;cO%˽ C |oރ$q_ W]Nw{;Mp.¡9ĴCCML[;?Z4nsn4Q3yrܿ6c ENDP}_&yRHS4BYN٫9xP)r]^3uʧ䛻?9H!@Jl(DaVtsu\e3-MIA4" NdYQ 5{u ӓ_RB(IdR]^(kg\OS*r\GKN<_QiJS29Y Zߔ+x9kFYA2'sJ( ڔ$EYukp:𜮕 r8<_x d'" ֡WD] W^5joCf ;xWzeKO1KV5D==F!gK *SNj@?00en? \ɭĀ[}ƥ7W^G-US]{LW7O&,kL51ʚ5J%hQEYbB &Œ <:0͚KzΆn_Gm 4ٴ<+!-'И^aG6Gwr6Jd% ζbZ6*j1*h˝#x~m[|UO="fptBєIJm\7;ټ~-W^N0`'`<& {@5`1g4nנ:4N/5obQ:ú *Df*S|lY׀Wp IDATt(c.So:MQ n6*ڪ\D+L#JñEIϓڡStMre[nw׳cdyfa ,#E! hi9I,+{شv5M)BѤ_9cO?#GbxlD% 5D oDC_(J(7l.ڕ=D¡'x(''[(O326D6OɪiW-F8"B[s3I} }Kbǟ UEeZ^Ւ"Sjki,*uT0>`<fYNO)&+i4η6 T;e]U87W3gؗͷ oO|x楗960@&Ƕ,qH ֭KuB a:apу< MqE# g>L!/~xF8Kqwvq1-}s/ʑ^FS8uMUua`:X5=\m W_r [M= 2>kf` @26DP0ieK{Iyq'\!3\lhmQ_¡ŒI:%14bptt&GɪAs2ʮN֯ZI{K3Hd47TρIҁCU МLВLcȒSH魡:!ԽZxBk($SHWUTUkVqm4'~FkVw=g jr lazi*oJĹkk<72C)&9&9N 78fiQ>L\YxLf7?7!)qzA{k =]9(˷zE-'E¨Tb$| UAW,!/v[frtb]}"'ƷJg[ mݶmN e`x޾Ri:2 Ȣ;M^B8dVuw.֭꡻mXSg0>e6mt%rZf]RؼH&'S()%Je8tt]lTzBgs  )׵\םIUdSie-a^<ʧ7*kZaAbMMt]t4c"`ks#']g>Qu&\ӛGh3G*Ŏ'duil+yG~J.r!c4e|H$5wq*(N U-9FFuP0@$"LhN$hmn4"ohg/%?i>#_ 3 N/` )Bߴ4\uE\y+:Xr唸B8ck'WKkGɪ=ZM._ _,R,kjHsm%9P8"EV$Ͻ}DuCQ0T&Z iRSjIJ]90Tr9s=CܜS\nwvIPbs9x@tem{`{2SZ}jh3e?;-5I:ZZhx l*:a2NsB|(&BV{_G 3l Y#'mEQC4海9~9҈?K'(Ӷ* Z):3QuM%Peg2tBUզnT}6T6]uk͚iOe$5F\9[+.$?(_'*2,pd&Yx۽iF<~j7;y<.-j_tQLB$3kP_j]]uU`4 :@`@M;8‰1>[ƫXcx`|bT:Ïr \EuZjJo=%-zO hJq\xEV |'p 7&UӴjW.H0@'Ïq(é13E46YݷU+imNҜLAߓȮ=|3Um9B"v]̒iK9*bq"U Q BjMP.mzf-eϙs80hc@Kb,u5;z-V1[uJF 霢SW4M!jeӹ+e_*&Izh=SFEl6nez[pQFRr?Wz;@VVvuyZnZfi1Wu|(F$<ֆꄃ d)r(6J Wng{e˃SVcyz@  p yrZȔK3EoK$AZj,J Q5Ug79Za׏4]h T`;9>wp?oj8g?h qZKFvB_Glk垛o)s_~dN'bHoW?8 n׼ζi;\={gd|L˶QV^wM\{%D#3J2Ȯ=էkYzYT,ŒiY ί6huNby:rԄ"T&W7`ENj6|es2u[CU>-aY&V&iB={RZ;e'զ*zV ]/ySW]/kẦ-Kcc7]5Gyd r1Gq=Cٲq=7^y]m_pj<'MW\p\B$_,?CO&ˡ(  w W^1+GDTbK/}t6K8/o{3E/(_~Zb&3u+{&CsBB-+A)U&e) +? 3QjgV,yU߇eJbJ ؗH`̯°{mAPĭ LLדNoeSl4đW4q 0  k:iR17'xp_TGNo>Ͽe˗ M債sB0J|{QUVuk(M>/gpg1X{UW(& #|_p* .ܰ2dA߿̒UM0 .`#~/a*փS2Wvs!X}75(հ:^+1ԙ:? ֍Q)sיԽLqu3f-kt}LiL,gh{29:'qf<2YU WvPd8=]7^GW{Whoi沭v 6YE6}G,غiFnJ ]7ރ=!?_ (ʇmڠ[֍lv>?¾ y㫸疛Xriu=^:pGrg^zPٺq[6c`x>M p ~u"C׸ݔ8Mnh\&YurK4'Ӱ >Fu?9V5SZ_DL]^?8'!Lh!6#%WK]x#2+!WR ګyϛ@(DӴ3l/wރq]0z[UWiBQ>VƽU.>M_宛GQ\OyzGz31%׎5iӔL9ڪPCgx_Oզ\r\,,u p<|ɦ9?^ضeL.#h_?-kx7*ևSc|Gm;LT{6xɀap+}g;O1cM p-p$"b/sK4BNPz9dd'SQLM85_Jm:CjU;qmY}h^mJƓk~}˕iMC۪˧^ujil?>u1 煾0gR~+xB+dEt`8W.SIzX YӒ-aBqGƴګ+_ PUowx՚?4_8rCzथQׯZo&2YÏyζV~El<gv j &{=|c c,n I+'(d$BA! PÏc`Gk=37vm[ѾmY`2r<8eh;K6i;خ;E VZLq%c:F, 0$!Lr%=G/kKS">3./?~G~H(e[7֍Nɲ]|o+ <`逋Ohdor|R@{Y{gyK_a{)%lXf.Xè?|<,~ fܬ4j0x|= TڶÁG}dÄCAoG. ";v%ʵ+r+FsLͪ䠪P k Ww=J L%ۦVWJT†N,9&DG,M96ȱv`bg&x;[yg9xLAZr]jv1AəѻSpjZ#pMQ9ǸQtkBF KAetk-é1~|?DU=mp獯p/}?[D.g/Bxkÿηru +!cO(p_w-3gKg|cǮ4;H}]O.eR@ޯMPIJ+ LmUgGQ@W5†N4  蚌]/ 2Jiw:D8p3;#K>G^~ Ad.dN-']</tk̃-;ܓD $Ȋ `*;." v).T v9T eSML0Q H6~;ϽoVoU]^ը=s^[瘤2PףQ? bZ՚#C# 80b_A#$eG}INui;-:yv_yrˍ/ v+ʒOW vுRGQ~^_}G˕No_5+[5›Mȟգeɋyh*REIYb8T |W>Pj^%I 2 kLCqG J)c:H.r3ڣ\: N 黛H$Y',bdTD:t6TIDATҒyB94\/V9)WnXLJZߏijxHQ|Yn%-?T N#|G8 ?}Zmgy_~N>/]7og^JmㄏOoKg8| 7s5WSXv==5n9rTxjK'IIxd\1;Eƫ$_*yI\PUJrСa'v2ӬӎB~p${Qo>8K>1f9\ ZNꝑٹ'&wSo %r'.3&|/qXһDg尤Z Ma@=Ҽ{jnz1z|_s]xmrЁMS~1>g=*>>Ď 7y糏\_g<#PY< oWȋ^w o||́/ R),gft Y54+d AXPz0͕i##m_O44Z9'+J~1Kj; _q3.8 ,˲rL˒ހ~L'Nhk57Xn=]om_ʚ%ˑ>h?WN V2U<!Y N5rfzeM#J46 V1gz2zhp\{ K?ϑÛkV,{Cя>hp假T>N&# |ꁯdьN~A Ww{'QW\{xKԽTr[ XXyoבz.ГNɤd)p&s&k!}\5?Tcb8 1>$XgX>5E0$/8?gw웛<;~r,'`|i ;8| y^pCq 8y= 6IXKVjj=uZ\&Z;1q'|H}o1<}s3]8JSeIҔ8u]TIQE lny2&4}IWsE! 6kbX2۬QmB&yN?qΥfd]mZ 湮eݺ1(DE%ZˌZ< s+4.H8ϥ`1*KBӮ#U]>&uՉJ܏iU{-ߥ所{w'1"E3-AVFwTlaP}Ch]Z|ȊeW<w 9`QtnRY^dygsG;p\jCiQ(b8trCosZ ,̰saVKXǜd7'b {l>PL\ݲc+饲\s< "M) 22z}Qw߯vߵ@Dh0hGQ{NU!UHR]r*=N=o6?dQ#"80s2{e2'V{tn^T{B?vW HqAqa%h> VkF?&v-bQcՠU$w)2m,Zk;o0 YV62q9YGAMSlPykMBwhvA6=I8 _.rE'1!(BgѪ4#(Vi̓Q'Dv℞%u.hf7^匶^Aee%Y^hbQ)\;8<ryD 4<שfݍTz,U\*Aʡ {nפ]Uz ٭td9~2HYh._ϥmvSjäL.Ph:l4LN=urZ/b>_1sK61 &ˎ\sgEI7IY$t 1Jfd {;pn7. D{'N̆\YBq:-YK-na3}O5941~3JA?3X mRT}zPG{;a8HMqtw< | .pݟAz|B0 oD\ovq]ב@[ܚ$FinnҊ+ZGS:ӵi YN,<ܡ6D"xLCf+x_ _n1) `G]vRI)Ӿ܋i&,DŽtrv+[g-*vM >8֨cMY޼ x)}{yNfR@67< ځ["aXD2`rvk}hvbD k+#s/pJ8s|C'IbMd$.DmO?fj{W3R} xrOk&288#MQHKř(B_\0$ PۼA©n3:v}otse1Qj7ۧoTBк#[Ff쿬_?-avD`xJh!0/p]WwXum^|3YLѐQc2n8T?䩕.W:tL3Hd$wZB唬TN=jN1 @5na`%+քlzSMZj!9O*X m[MO>؁e(d273H ndw۵X.8<C|.2p#pTK%>mNQ*C#H6T J_PE'b;K$%ru~G D]E"{ӡCc 6-nΉX~n2t3\7Lh…ibRtR1ߪsxnV- jހ^m3rcm@K1jR9e+Dc#|;vgpkFm(h~Un#,Pe._MI˒V{_YȄH#xurŽTE>e N>}e]JUջQ=ۊB[ujA@Q8k$ѤQ:#C;L/dfDiDp8; :0q[,cD, 1#L!'?[7.+9ӚE5bB]\2mcua*C8z91)21ߧ8ivZ2Uqɩtms]Zu \J[f%GQK3&#+OO*aMr"Ͽu~ZRT s7/q.FMQٛHq9:궐,/H._+=CH9߼Nc˸@75 8TLl`h]>jtY1:8Ƥ=R%hxbTt>gn餌m[q&jo o{lHtW^RZr:"+f:/^JiTAO3Ms:f!DEdg2HET Ld-H zu_ _H= mAN{T Ҽfoh%uwawBqr#zGGg|(\$EovwfB?ƹ7.6Dfɯn`u{/2sp|b B`b fǩ8x)"Ev-+Q\;L4k2zq\܍LU~=retd}vk)BI=M*EMnknѻ.VoX8@#h#b^ xrKYTM]j헒B?AkR%- fI]:8Uz?JAҎDY1!/ n" ,MY,L^ A8]S)oԿd7X<܏d9H5>d]=Kdp=nuZl#؇Ev4ZO2ylbyB`c| 2-*TZ)(ʒ^L/(rh&z^^0ߪS~u[EK⨇Fèۇ;{2^L![ :|V O-/>bn!Kfx#^qn>XxJU1q^TP|r)߅uBN3 v'e%ěGvg%DcbmHmH#u>Ч{h:;0]8L?QJ%cGsK75{Z3[Q6DDݚ۬Hԭ)ᛨGFb\.pY ."_Ҷ0"X))$/'2av=) MWGӜAgl7q6ŲۑW"U]ch{C=ujB(Ji o3VqrY,n 9t=bd*Q"Y?ducX, %0?%ߠ/U5}ȓJf(X$R\H.08kڵs!Mnsgon@X,`1, DG xK=i>P*iQJ,K$ge0,[u%+D4^ޥX,˹q G#v=׸> $r^9s(7;bL$uh{ˢoX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,bX,˞gշIENDB`structlog-24.4.0/docs/_static/structlog_logo.png0000644000000000000000000045167614645734712016747 0ustar00PNG  IHDR(9T< pHYsodSpIDATx1 @v6SLaEhҡq)\ @H @H 2H)2H)2R) e2R) e2R@ e R@ e R@H @H @H)2H)2H) e2R) eY{s1F)%\kmZx@]ĕ}93#P*_PCzTIJ}ZuwwuwwM[O.+t+OȽO_>///))n Ç_x>{Q௕|-[wtL~'Ȳ믟>S#.ECv+<A22HK/P,mPA2I{ޞ`wkNװhW{ݥY􃓓'L <|SNjɓ'feyyիW512\-\(,ub&' pʪT1Ӹ\={ಲ2QF`y7o,Hn+,"-2?wMxTӧovy[Z2?;~<{Sz<"n"E^)u"*&F 4"\[[`uE^'**JUƥz)yoU_eQ[8n5O6->>>;;{ufG 9"Z&lr)ĎFfF熁q3;,f:7nڵ>ĉ:8+G``țdy.ER_ei-`~ڵ瞻k.yoTn%%%2\wuR֭j"j2nJ},~pAA޽{[b?FPQQ!?yGYL"}g*2hge8w m~ܹs%~$IGPUU%HFy"mJ2橸GT(Ғ%K#b߾}:P8)ʅ5+W>GPML*H$)^Wy L3w n綻׭['5?eQݴiOTQPwGNqJWڲe|d:uJ2ǏE3r݌L{쩪k1$ZW V;0CP_E`c,}:g=F*++Nz뭷I4<<ܤHyeg_- f<Ӽy~pMMު(ky⊌ 812ܓmv>$AlZ%tRbFZ"tF۵k~W佥](ciY q3 vw جJx1ԛ2EΝ;T~S fOz#(iX?xsߘ)!#]^~_JJn*N8(@#Ķmk\DQ,qV<O=_/ಲ2F)JH{-,,߿y%+"]sfy92sh6~͟?KdJQ$K766VJ ƥHa6$:3~pmmo(@ }OEH&OAF͘1o~w󛎖/͡9`|#/d c9[V  '5CfP⋏=/ՑH!G CyYv-1S y2ꫯE.b%mzfsvy-*?#7&IIhHbylhQ@؊C!#+ ,oWi?#iL6cS(OU/Jbczz@`Ijjjwԧ^{ͪbW\A"øpt=$u!L  _A R&(bi"рX z4Dn&oUWF^0˰ͅ(e C])O77eY!?}csOˣqkG#!1O榠 G>*.Eؘi/bDAr`(#CޞS1~+Uр^z8h===>/yZ &B2G]9ںiӦ/~aU"੧4-6aIlm4IE]D"7;v.ew1mnOSZIolU W^/E~a^q#/~8A2/jc,%wr%v\}}}o|goE$ǿ4jI}4sX, G}o#*egÇW4u>E`Ej$IzUWMMM% Ys7fF^^<^2"ARGWྠ1Ub9_t_$S(BiѢXT̖s.h4= (bkj˂XeqiW_OI,>8|sW~k{RR[:z@e?8.[;?W_J4 R)[)ۑQ^ GB>< I":4P 2q\4dɭnE!#DI|H& .*1~yyLRnn"=g`2JXM/ LW`2_k z!ey@6}$ hMcc?x Njm."F+'nU=~a6eL]V/2?߽{`0\`Ry_tly!JNs?0͛79r4?,Pp|I-b#CX([93gM4s__d(b,Z+b8Nnkya/c%)ms(| Ygb$I/ETs#/(e |Y؝cMѰ<|O?|N|Y?IfI4 2Tds-̻ifGmf91ygy`Aܷm˗/Nv#̥H%B2bE]t{߷800@"m}s* ͊M@$1]4(bn1>UdR]ooxqSKK -7olmnnuy!D)a~{^xK#IR< ~/]vHxl$s_:ӊ cA( Ϯ 9(e@ـ"nEi(bYL|veLzBk.߫y=O7 `Wy!D)!|pPwxMJ.ǯyO(徳) `@ȜSШBAS2EΗs#ȁO.22n Q>`7v\7>\y!D)!Q[C|pK5e[l9?\ |[JC1(nn]< MxAsС(e ϵQ hZϻ(G3m 6̚`}уv#oK"h ȋd!J/lxHb[ dDs_px#~ZUOSK"yI$2K-?hn9::#X2ŽsY֫EL;#r\6Ǐe0G?_~t2F^`HolRl 3PidF 'Jr)|}Ue $ + =J"#p&%-h.2R0fq}G(bJPtGfb|[UM}[H,v^<拫x0~liߕϊ6{40ƾY_]G"1Kt}n&S"/]AW1X2P&$ƚʬDpSI7YZUEHG&F_ \!J|dꜶTURMYYsgZUÇIĥ.蝜`K0@"~Dˣ ƣ*'x* ,Lc nE¦yhFrc$wyVu000@HA/E},ffƅ eoKӦa3a*Np׿u ˿KX[-ݝ<$s2n) X2\qQ~D,(r9/ |C*.(:v~RK ,(e M<&~U?p^[Ƕܺ˰LY曭J8TP/VzSF$IwgU.MΖF$ZQ(e ,Cj.SvCByg#D# ~_@8XFi/(p) eVa fD \z΀h|Le~SVO[ѣGctBJΠB2{'SM7g*?k׮%spLn=n"]+LNNZ2DQ+e,ML>>m-$_p`U_%IY&)X>U=VeqUW[}r9-$f|]-jPi0<֣L0A:$ͽtIVBWUl0fee] !D)!g<=a#3IdҥU)<#i'yeyB˫ ʌ#ٰ2_H3km8v+/hPG2 K|re#!rR>8mS @"W^yU?v^hegYL~ Ѥ˹W' 5ټyUi$YX?7ԓHPJG=XDz)+R'xlLC;QBLcLKF᳟UypDNN<-tB"&2,KQ(e ,oID'$i|d=jv+$ַ*s~_' J ,OI7(F&ȘT\bdK_J=C3HD>V>LC E9d`Ő(YHdQX{qO|' a{xJ{FX3OBqC&~UkSSS$R?ZϷ[HD%XY423qӖK}@)@9sl%s8P >6@k 83_?X y'Nm"==BRBxP\9pơޘڷD"ǎʓ /Dvpa ;6y%$272.tg@)eh=S5>& I-$S1\D\<ߍ%񍿊K!$3;52I66l`R|D3̝2Z{PH)`c$uVؿ?Dt=}A{v17l ^KH.̷EeAc~`l4)mps+hOZUD66+q96zTͲjR|ziWgtY2Ӊ"I`$(`|$r뭷Zeµ^K">U' v[kd#uR{`4z/B p^r{>k탻Ib_3Ҝv?r駟F$2L7]:>0t pWd0 Xf%Ը\w6˘|wYHD;S-KAK}|  PwQj{ͺq9# n5{ UڼCCC$R-2cS$Yg#chD~[ $g"P@b`1zXtrr[KFx*u_C}_dyj4{Nzp9HV6 [CRB8{ve9?giD nsG\o'سgt]jB$tpw&c$o

c%˸4},;NFx瞳J뿖ū-^G1eg7>^~{uDvֻ4ϭd%FGWԟ8BQ@n R&:A{#slzC9t9K0|[ߒeDNn-тu$R62!]w#Íj*ٸ-P?6mD3dikpP5+게dMf3;!?>dx<$1MO6 UI+e(&6{/!D)!<L"]oGN[B"k9nJ"_|UJ'?$2Qo :6"iu#m6 k. 2y])f/5tr5^󡉱M"U xDGY&9Noܰ1r^e 7^)!L F~ǽ7vf9 ՚ tMT}Ku[潑1pBd@)@H$4CȞm/de2ebcG[ifţ# IA\ɝ$2)mUl\~"c/sz"}r2B2/nAB29KXQm޻u%|@՛{M``bf`%}2ZpI]-漑FxD d?KY2{|Ʉ#˗厃.Dt70 w`(iG^{rYb^B2W5Iy(̓I<:.(`~|w;V-)wp|o$qd## _J?Ak E)e(e ,z#*L|⩺AۗȂz _VygIEV=DD,FB6n 2~١0x/ P&6uuׄ1CU(z衴Į+5J6NsGm]8zEb_ThaU2/jXsQ&6HmF@2ӹ=xE‹B~z Er߲-tRF&F['n9xJիi"/ =#%2[X/?utEe3˘˲V,%%Fx6(Y8#n_ʎ^&SVI)!ʆEQebHǛwK[68ֹgԸHdxxW^UW]E""/wЀ3 ,ep.f 2䃶6!k\ʽLeM, ߗ|>_/cUUIdgэ/oFGZ)2Ȍ3^/e _0?3Izͺq99;5Hdڵ^#84qj7T8&G wM;$eR|hN+Wq^f<Lhl( =DZ[[wI"gָr۸2#;e/st:CUHDbtX0BxXʘ)e h srnj#"9H϶N"ueߠco5F&F:H+/M(em,r(nGLYLxUu*q) 9]eh*I"#|LPDVe.Ш.g&{K˼g$aE2GW4  r]2ڗ S,fB"]wyT*E"aCr0 Ͻq]hdrrD~X2dX?s˸r˴m骀!ͭu ^{VNy饗jkkId8ݝж4bdxо]uA*!RB]~=S#RNvxܖ2<-tDY9W_&uHe佖`D +Gs7$pYf.<#aaqN>М̍1wQ@XILO'cWg压.D E90yolfԸ] !,̥n1?Vn G/U5dedb4iEQZ85[Im9\yXr^2VB)!t]W\v|s/&w1 e'xGuQIayY p@);hC÷{H2†4wGow]`p1ٳD޼42\-/yq<.g/sUoQ>OBx J FFdIaâyLM)ZϽ;YEzZrEC}兽c߸i/x#m oٲ e||f mW(_L',çZEDzzzyk\s5$V ݁ŵ^J68?>9xkw|(e D#SVɁ"K7x>q4e;#;@?5_l8&HRޙ` k#7}!(e-5554Cp^2 خJܾXD6n8˘>$ve?YmGlD=&ϋ&ˆe'D)!ڔK%,9m[WI ^|x~@]]ݳ>k͝_~yttyܷlHĐiG- pd[ I3{(e;Q̦Ee`$ì:V>|ؚ#>hP2K|j،Gƅ :˔ ڄM/Aѭu.W=Zsd޽$Rs*:eΪ pOHD(exP?| f4U/eB"'Yo~S04&k~p׺298-_B2. 90U害-dDj qžpb>ݐ0Id寽5k:DoǝI9BEhdb42Bb'@)@%B2,^;LR s{`XOO,w?ȦZWa"֔ƎNhg/ϻ&hyj\9G܇ %Yݻe'#?*i"rs;*\q֛H,VUI6r did<Ŭ~[ Wh—2]ɘ?!2諪Q͝%_|s=Id0ǜ8R'|}DʧJohm]h#ýf8\-:vqVv|nMv%@xA 4\`R\a#[^FY0Q:>0 ~WHۧrQsdcZE %cI"x`MCm$r[C΁PH֣|ӟ2/<3K˜ s"$/n@)@i.2>]Xz<4>u9r3,ZDL(׎尗n%kSe`.E)RP_oM252id}HZZϽ?V;e_;`9ID=b]𑍚 2P(D3"2X䄽Ǝ FU)TڮSO=D|/Xp6h}gNz!D)!<20dFb^w(!kR;3fJL\A4.G~C0wa6J6Nm 2 `)B2«2#'m`>nm!["^HҎ7_'GBōbkJO3TFfM8@Ձ m61)EY 2O"^}aBx#J ᕽ]w֕$>a[:NG Ά4LD)-ĹO?Z8`jjf+JqU^$r c<~;,4 Pey^r!D)!+(5<52܃Ijq\)8(r]FtwI1oyFwwp@)@}}=P-e]&9dܡA7<0RUTX/(Uxo^8=@I^kHRbqC&/2e^Ky|B6(e(eL/,jJ>y&[5rɌD|>>{hPOI䷿@)s=G6.sʌN[$A6!#G0@R\z-HXVIoN17*RuzV׷6u`QdD|Ik!R%gD#jd&^rtaS e'n lO6ܚʣdf8tº Oh R&Vuf̑%>>vK֛Ec$^6q2GMY"gJyQdLfǢo8SvtsOC je*D)!Lli*̌4F"K<\gF=8a)Jhk*ٸ[Jb5tr`(#Cǝ2{GxBI봩2ObϯɏG KϨQ&xQRח@\(S!gt!etD$E8*^qg[ 2lܸf0%(H2 3Z); c"U1RSҧ hd6([ 6ڎ[o |[j]$ۍM" M+|n.^ZfJ{RqCA>NYbr_{Aq P"a-PiF(|)qe*<,Svvֻ({Ȇ,1( ]g-%!KǦHPdW5Iy(x!ý₹w0*IL$$Kn<9"d9I^yk~R^{Me0w 𑃹=whiS.fKM,D>sM{{6p>et퉃8(e D#*!EhS)HڲɭKU.ALHU-% M6V.eBBܪz0.q(;K#F9B6{o[H2,_X0seld.Y6Xqvd#e|ԖaM"a\:>@$ˆN U+(eWJ66v2qrk &z3.Ե!aư()U,*MI((e E\_z(ޭ+IdC,RKf$R":H$*E|z[2w}dcoW1rPar"aNGY4u "XY,?s$H샋kqB2V_0jX$.?em!Ub$#yȐ($2,7:ٸ+J;oK5W@)O>I6NcTS3~}`G[}N]ﮜu\{yYKٿ!6;j Ǧ=90UM2RaBp"*I CY3Gu_;{el|򓟴 (eg?K6 }YOXRW[E9Uy1ëy,^&1̵1P(e`5Fޭ޳iyti ʳգ(Bh?#]wO_|] (e?8ޙa9zhOY7+2cmz#fIY8{Q@X~tImTȁݺtԑKHdw׊4$0tPǼzTdY 5wM{nkRM6 $廔i1MĦj=eg4]떾 PdGR!#u E)UQDFoߺ&ԁywoRKCHy#MSZID]X#DQkRA!(ymdN(]p5OЉeۧ%n0ݽ{戟B/S Uׄu.4՜1>X+;CH=z#SFD]d#)4M ~@2V29HٵnC" Cz JEw 2̞dcYПG6/cqvCێnU( ^E;P ?p %kMn LY Q@މ7S#SXK[ശ6ࡉ$鼅|CƟٟY`|_&:ܘ5rtU<0RUʂ.+;%WqRv>N6L(e gJIXT_M5*LT"!f黪Nz#,(e i(J&%pY7N"Z}|V^2:I#{s|d ?7i漑Kje TtH/Mpz@(1eᆞ&r]ҫ$DrGdCu 24if37E!czp.NRvԱ.&.^r8R’oH<\&ӧ/!,Usk0U${(e\W]"0oGI䗿Jfo~1䶔{)#D ͔^zW6i'rqp;9 +kRƇRoʌea2w[U0f9e47H6+z@)l{&әw2cW8!ip)? JKK2[X^d K]F,R~F$Nr l@)l8|0yM8@m ;44ʂOn BWfdGL|T9hj!Q@Xt/Ȝ1Zvf9%^6tNmk^f,$>;Wɿ"@)lؾ};͠0d̔$r 3P+rQt]Gxzð&湳xz4xT(e ,紅(qD&H}}rK~)~dĈΊGr`` axxf*rrTU >eAaPTD&zMrCƥ~ *A2r+@x-H=USޭ+I䤄6RƔDcM"AUI_ P(dRDhDְof>մFjuE”FtN)K[}dëk_Xـ_Jy1zF/c#s@YlT D<Tm/DMcM?ub@)">& c@Uim1’xt7Zk:y,ko>e_>^&B2ƺL co[5Rc壻X I9ܠ&H揘BI"F !5q=X'2w}dc󂷯zzܖNE( }~ģM6^OG]^ K?Jr)^r 1vr)׬'KqoyGȔ4U[ʼex[^m.l\x։ ]v(ޅ=ׄa{{ E,zF'`1+@+\e܊Ȋz|(e ̡j\dld޾vʦ6x70J/^f,$Qg!}l2Rk $-pK(:睃O) C>҇FfV#ٹ2z;Z&KD }rY]~@RY%Ɨ|U4KEȆ)YWx}W@b|[Ęi떶%9,'։ ͶqBJ^2 W`ht8vQtc)S&&+Kn^MG/Q@ +nN^\sؾ.M!me(:{]}=$״y8%"N(e0 f3y?7Œ2C05~Ġٻ6n q31bo*U<<^B2dETY:rڒ"G6.!]9 Yxvl^) ~HHg?J_ly{M4ULhyPT]{swXdTC+o˨; ,RF+RV4 Mr)/ r`Dރ5.\vީIM&7iRl<dc:wG\H5 eAbt~wYJ?{řNӓsh$4ʣ !$@ƀm2H`1skl8\{\u7GSww8{i>.lTwxerΩykR0K \.Q]܂ B_2nt)s߸Ībt0Fs1r8DFF*el7}ZF?z^ 2LF(Iw,߱rU&!bęUd9_6w|.s/rPBSN8N{u 7x?0- dFy4Blnn$#IrB h@(|t Ց@&0ƶdݮrǿ'*N͔0Hxj\&4enkbe 'enGDHG_ on LZ.S񐑱 \5+}W5kNB՞&!w4i󌄃t$nnB`.v9\f2-D(d.G|fˈ{ ddW3m^QaHuA 7 T&ZD:oj { N/]nTmMSb74xq2w6n&T zq8nl قe |/V%Hܹù, ֶg|.= ڒeH1M!ak $+) "02cyM[Ԃd&&+*(2h27q\JK6 "G:-Вf5IWV'ZK*q_gDk). N8s4seMdFg/I$.vX6t\2 :% A,fC%2uQ [J)m̡H%Pm͒,MqYIǂ 43::J!2vrd-ܙ4ԉIؒrLTqsº3XR ݲ@MeA.w'#fr>Ȃtrp5S]]MũTGɌD=i&4 g$373ܜjqqSǒK$޸S=9KdyMd8qe$AvlJI>S-*}F̦L%iu; 絷5$4uTۚDڇUmքQ(=eD"%2/'efc 2W e6֓jd䗿ByόFX94 ]~e/\;%,l¡ ul"#x 6N:b8 -pJ$=eN9-877ec})dFo&+$ԹqQHH9V@k23q0,!24wug{~O6lÌ1_q"zJ z{9C_]X|2Cm-VmB MB!ڇpKI:.;75 Xpayg8l,({F oi=eHyݡa'W_}Br-HUuc,l2ҡ"B:눣gjWt"pwC gkB_۞!#)}̊L 1O( HGe~|.7"e6s~GǦ$pm#=/lØs8Ŕ@2#ڱu>gK"D:TySN_UPt׾2|["}>i%-$9k9wuDј}{?fi78 ּi^;c fhCyxۘCHG)yS!HF6C ; VUB{G; e8p 2hCSźK.czi6$\vl"Isw,l%*L2L6= k!0vp'jQ'#˝ЗH|.]N1>>2ڵtjXX2# [4C4 @kA4~M:|v&uۉ#D(\w!unDF 5ýY"02R[>QDt0B1!]]]Q@(5t,?8d*IvZlD&!ׂP̥cyS;&qɤl: ߵ_3\UD(P"YY!I?GZ ^X&35e.%6"\+Q g?@(~+ґP"3c[MVj$eA 3T]]-~$#ub^73.·@2pNͽ,"IZ8nhR/wcl)2Ra,ܢH:/B K:r)uHH9K~!d,Oa6{tȢy}2<}"S D(W&uцʼ MeQ;yme\BFBϗM!?B~i1M,LȝUWU2C :UJƠF}2QrPE"82CYHd&*% ~j(5KF0Q9\ODڳgBn&ұ0{KJ\4 k0V E}rP&PXvC^7Z=jxO)N NlwOUsL @F."K.@(͛Ih8W17ª )2=cOOW?i~at40g.[ؗ˱(N]x B~k7 "?8 n'#;`[ b?xŪL&I VHes4Ʈu -Yh&/h+N['=éȘڏkˢtx2 %*qxTKP 9X$o %FF"NUs2d/Ice8H:vB }Ssr,IM--$4 Yrh<~}bƟbo~\ZkH=?S D(WUmoB"s|[O_ýYM`dj.|gsYNQn̦LģY"3܊}byM&%kQnI wX}*>~&pX]2~mqɧGez2H)W4DFy4GB ehc܌;!ߵR W^!uNGE6c&NQwgYL:$0O6\+2zbA $D(ѯ j=6∺LսEײu^F'-tbd\t|_JP뤣0^0l+[mHfd&_uwE:BN;fE&#G!9f2pɢTK!>/vmd.OQ ~V2w}cDFm-h 絷pzo1vO:Dav?!s?Sl&X&!Bx(O9eH=MOݾtv"el2j.@"sGZlqu[,C`&! j @(@uu55 u M¦TџWIGUЃ $|nhѼ] «LQx?V%hMoC"sl'#k+XȈ,ZT9M }V2|駊1- :bQq5:$6Ir>u,M, %68:#HdNцʼl DU(\YGLɗGZi"絷Y8_YCȓmce^'q\ c՜T22> SDKƞUA/pKe7pK  eƳM4 /V2_%T72Ռ)-a@H]2"I\qic 1D(cY JI{h|Dh*Vpo֯<25?裏j @(O9Њ:9\v %@LwtQ""S# a=e?#XtOWu12c8Jri Xu`M-)eɆtrz}tY*o xdxmbId+RlgB|L&C>o!-Q2˯, s騏gbJccYE(K8!F&UKP݃dW{~Udd$v-qD"'1 +#hZ'9d̨uV{HCfhPgG{ Bhew7MGzPgCΈJ}T(0UJ ݌Y\*TI_5p.c^">EOOWOf%olIG:>C3HN2rc(A(ciϮ$_Xُ8s0KFήtrRNXY[S@-1FFdY7P[QQA@E9m:$2%yi78ԙMđrXe@ G(?sߌ=CFw4f篢X-yWS2|-"yAGۜ!2Q.n s3ky0:ËZ.;-BXn$ےE`h0炠tN[\1zBAHo6]P={Cdfդ zgF:ZxgsQ>aU06[ zYc%=ġ(3mcEʻh-Q.hnq }GB:+ ª̬5 u"cdƙ FaC"k33Cˆ:eKdV$ݦ0>~nܻ^92U$#6Ixm6ؽ{6CPMe/OB6DXި \wghx.>8*w+ZKX\E.%rsuV-V,"隵\'|RQ@(oL&96p^{KQȌnr꼭G:II^qK=\ ,t;\8"< ٗ '뷋DYHdNT \/ 8wđwtuDÆv2#hR a޲|?9%[eNdN8ԧ,CeMC.;*:;佭'1ʣDfQ8km G9scmkNQ3 "=;fy6eT}uq "Q2o]ސ8.;YCddAf +0ʣ)蘛c}@(C=$wSn@;Ϳ/d#mODnT +e2[WN1 i8l'#{2׸Wb.'qlܸQC@(ߨJFƖtĢdWqtmʜ p~>'Xo,UH\M;d_jC)$tz[[َ}iseD-sj ` UNfEvu ʌw66'5w$dD X\2+|*qJd`:5uS~!ډcMC'2Q$#V P8*:N\wF&ȌKp :DF:꟫\3(;ԟJlPƎP(}KXV:{a0w2} y*]¾JFxK$2k q>_ {gGz `]c(!0^J[-es8d.V$eK`1+ I%2uQ&2^291)q⨭}2w}oE657NL4$XpL'A}u9>nHD>i_ m*]2qdA|9mϐShƒm]$# 8WwuwUıh" /(ѿc5S:l6M!3b$fCs7ee. qLs]%`K"d\O@|@FY~R /\ EO~@ @c4``Av= B1%7.q(2]eB2Ł]"y90CHG.h!rꞞ\e2">P"w]SSqdK;M{ h'q$eH:k*>ifۉ\ YOT8B(0w&& m2pwR=ax5PGY8"Nm|h ȌJt_ '2_-\_E7'Uu^Qd̲t|boPncߵnnnҷxQ'}Ytep&oas[#ӾB2龤_cEspKIct׸哛D-%?P 7 IȈ(j3GJ2' 6P[G gNv⸦94(D(_pqj.C.;8e˖) nw p/nx@a.pg=B"jȜ [u6V}wn{5kB F)圮an5F~`q2Aq]?]"dM,04N/il U&'ɼe({9QɌe@ڌk59!|'B2ޮI"؞(5Q`-Oo7ր_Ȍ?X ƍc N{!3[k v(^ ǚJo!/e ){;c3Id..D0KF'af9Sh>(pwGO9f gQwh\,H+Yzuۣ#>vYK!=CFnoÙF(e(}YɸhpFG:dQ(v1w wd(om3IdnEU)p[!!1B3## ַd⪸7; @(2eX ߞq Ɯ(D(͆#2vi PO D-"FpF2r7k2?d$w thP$cAؑB2jDF`wC%)|'#K3~{[y͒PW_}nh)™vaH%x;@8IGo BGl6Id$ݻz5(>\YΆzg}_,e/Ɉ"K3C8;3pdEHCqdjne%AEaP=žtt0u݌ Hwwe 2"2(F#cneymcXӚg+L.ٿa.F("C.;7M7;Ɉf{W4 B~F9D i-w^d'_•4qz )]Ϯ*IB("]?DF 6ebv2zi 裏FFFcu܁gFL:ʱ :j#TJpG(Vá_0ZQ\l'#Vе;VH2|75P9rd֭ı04 @E:.;6btCgq }/ cV+*)oZ*Qt6V~tm8l z2{i ˉӯDۃs=YMUJ /D(3wۦPߗ%"|{ª 9B$2 7q3[FPG{Y9>wۛ|^Y #\rB|;(Mه& f@QkQ|!2ew+cNyMkȍ ^k8"##wuVPd%B0 k8V>P^1) X\ff̬&2 N{HdDt =/##O>VP@ve@ yš^>2vI%r20E^wN [Pd<\, iw$w] կ$vNk0髖va+-`.reefܷxA;qC^ $2p~22Q iB8rYA+(@(;DFQT_;`#=1l^>Q'ʋtG(5TW18 >Vf'#N?V P? , 3 wTInV`o_@Lro B3w.RFaիWh! `YiwnH;?͹ <L8+Vz`ae'nI( @O!#oB#Gl߾8Jj\]=JuzˈON C(3c~e0URJ*|ti):!5PH6}40W\}nG%4vY\ڢ$/A"ScQMb)=NF&*\=!'돫"501䲓,U! dddd$ҽBNGy8bhs F:X2>7q"8@(3C;V,#Y:>~4{ektUoj$FFo߮P_ d+ 66 L%ұⶱ8VL{(ª><%v2i  oQ;!@(__*BF"k2;% c,1 Bxk[[RH}g 1,5G+  Y3hS2+ۓ@dK:lXrڈ8cND [#DYxKд=A˵W'*\d1/}I G?Q$!#s\k HvEzl0u]{-mo!< Z9^C-.]8C6 /qh1 9!/ CߴOY)ۚFt_M[n$7xC駟]8^@[1Z;$֣|YñB<6@UyzKIZos(+,vսQ@(]%+_o t|~lpI]8.{H,=eOV{Ϫdd^$Gf&>@ 7|3qY$2$̸dұ̹8RN2e*/qg',o&#+KjKoj$uY5ʀR_"qe&,7; ljpEs8bv;Hyf8"nsx`8,%(##BP|_8%v}mw6H$ZCQe,G|aSae'Ђa-2;^pqeپ)-HL@M3{[H. C12S8 Уe^r+M%2>w(qЩWύo(0;zKTyyW/sJ푰 !/ȷPӧOSrhUJV8ZO_W ^U-^^Ϗ8;^sy=^Uoh 4̗k$.nwZL.E(n"3X!uSnw`1wwip0دo ,/~@go͓IJ_]hկ7juQpw@(_GpͱNGkM:kB&';o*yXpDßm]˂9 p"4G;ɅOW>>XPy1pպb\rZ*±*b$yơ&u+j'ñy??2 O,0:oy͛bS-0&MPFfL;e*B ;Dza:%:6\Z㻪6<ҁ8$a{? ZɅGS>֟̑|e8Y[d"w@XS Ecؙ䓏L^%#xz&n}h=4ygM[&^HRx[83L<e8y$"<&;^-`.0z&vEsdΑɆk'b ;+ND2p/k*γ\ Oz(Ūʰl oXjv}(1*'/೿³;Shv&.L^$l2Ii Ik׮x^PTTD`(ݞ& RcVո5tm+6˝ ;|/{J< ;X,Si$nܸvڍ5W@('~v(,hȴ5-cH [iaT=|nsQ)+=Q)EֺQ)|DwfTJVCRlU,rW 0 ;NtvT͓|SrrTʑ >|Q) pyJGjdb*Bm*PUGQ`'>l\4f;ʋ#,3qOyFMPFT!]o*^>m,) x[V7l׌i7w5'=$Bxa$K_yȎܾڑ|MeV6lm㈇{yxg,RXS~c,5kGS#b r_*xˀ$f<Bn*2/t#I?9ytrq^k,u!?I4%oGp/B m)>k"F>vza%"O~IB$@uI #'`Me~}GS‹N!C! ~SSݹY y7r, pw`I0["Nv}ZsURu1Iִi&\-"J7|32 fM$a*'2 y=EmBhQ|ɜ;[$9w.\PkX"Ѧ{nd5IJC{5 &3{t=dYgj!dgڢIsPd:Iy×*;I PD Q6|vOk^tO>SGʏ;F.i-F(=Tݸa-UxPH!ύ+G CU}Qd[|]p:C̕zPK_R<exIg=N1JDek ja]nNƹu(l8W_?18#l5 Z4t]TK4-`Ŏ]~o'H9 P4M#ms;t  /$(~\ܝ4rرyǦ-J_j^+<$QsP0p޾|pCp\`u0B,eDFE<e mGy1I0w.[V215 f&oE@Eݭ$: 'G z y@bXzzM](XS ۷P7=ŠTDc/I(n,rZr_d.6X$QS!|X4%2K-$2paom14<5%eW7{IDruvexH;n$ /}(l[)̨*Nxu7k(O; Ydoh+chETU:pu4uWBؚ4ͰdW4:j9U$z8YPUUE'WzkHT̉cflHF߻iMđ y5uALt.,eq(#QUɖ"r5uʌMٽCH-ɇ ?\Ǔi[M%!2Xvm eLr|fp١ek /~M!kWfR%M h/Κ\coU2' E68D«W#Φ/1Í(3B~% "|$!t$;,5^Cו$4t$#(\4D.Ze\" ugb$.q@ۻb|zZWb$mFL I7pC, ez$qxbaҙ̮&iJRoP0`s{yh!IwPX]`*콤,v^a͔pz(++Kd](o;Gb$̬e\Cik*¹5SˬjT2eEIG"t653䇫Wa#ԞAT,2 "C eꚥ/=m$akZ%23r遭}\fM\D3C dQ(3 B42I@.P[z($q8_RUU:"ċG»[ ӡr:قI\Dlөe@hHչeB+B$O/' q~ȢP;$:ʋ݋D'||riK,f'n ŖJ2 qS\4 w,ኊ\W{=y%Fe 뺪ī B***H< =>IJDBU䑋BS_.P푪h/+BżvFJҾU($dK(??c2[Hwet4$Y|.ՏWfx(S72- {Ŋhena = &_ Y1Zh(ʿȊPݻwe,|/z Y̔k(nm%>Mp%Bb\.CEo cbW*Ȏ~M2 OAgS$aԞ #u«)Z_9L e>?\2dDC+ 45YL *"Ɇnb]]; C>OD4łg] Ԟ M ᢹweUf_B\ 7Tȯz !HPz!ܩBa!H CVZEkłgIl,_:05R}L?v2.P1`˪PNp|І8UI%Fe3O_~yLe(0D{ȶxr .t-#"^"q'2>\lmE!ٽCHxka!F易lf^A@E eHbMe$4 lg8޿z%+ e4PkwvZ~b$>u_ExXB[>8DH▱9zGn5x}}ˆiRxhP-0CwWSreE9ʼi;ރy5`#$%o e(..oݤ A^3MmOeN4.~(ӐP敞9]"ÿf#$!f>DI$8 BO$yz %>$ŽK2P*0 U%6r,yLwR~({jʦm>eZJ Q0s}:0M~%eشiIY0®ILO0dY$a*Jf05\\Jo(S_Pٮ2mDFy| fIbKM( A/R%eX̞nZD 6IhRl]#jʘiɵe +]U7- ΟF0s3wfh IXP_|$X qZ&O`رFT`f!Ue/J(*(CU߇0CIPU|Lhznڈg,|a{ $qyn3^p[q@dW5ur1/B(ӘPU-]{hGf eٽiGwqG e'/oa#;*L%|F X? r1ZͺP%3>0`$᜽ 3xw,I(W^IynHжx 0f/ &8ٖP涎D( =/)qrQ-ut*$$ P$XKIx}$BĽ=(|p4Xb]y;Bۊ%3o[?~-%B$?`C?$QAxYo;f5ëP*0}"M.:췇ST%fY1(pL>o$F"h>$P0;Kg?YEe&km[P]$.^׊RQXf2WB- STUb$YY0aڇ>8Vu/}Pj$S`i*I^QRY˔5  F"L?5O/oYpjظqc ex'IbYpK!EŎ}i$1T:V|o|f(9E<ͮD+xP ]U> x`DTU׾`BITB"L>uX]Y:婣cU~rQ3>=KU((s6H. x.fo"B鹟^,Z$CPo}[lKRr=+H4l%}A  \F~~W\fiCUbr\]Kr=]A{iOI444P.$64RAO *AHO:(h* rʥ eKdK}l+HI0R1 B>NxOO@(@  OɺᓻLȶH"P'0{sttmsel.!gB WˋHɓt${RA M%Pvl:f0(W.f(3V%kcYBxdm3IPfBP-0%}~IWP>kY 5yIl+Ϸ$LMElex^5(09w(B~_k&^>8E ¡ zo%˼Es 鬊 K69^#UEn̅!oh6RR)tcs$vO ?I,//NEݭ$ 6$!@p}]9ILCNdVbCsp2!74h$T` }q}/&BH.jޱ;!R H`u8jO4 '2^3'}$aeD(Zx K_Ix M] |`"^O2B XK\X2jb\1!45?^O‹(Fe$rx:e8|0IlYVE Be$\Tg腶UqlV(Kn^VE. E<]>P&hΒ rqpMS6ky% ptMW)syP*0ut8e@|$Į.gkc-U !n!4E4LRx+($(c訛6yl弨/PC N.ua,' zb:fQ-0ն7 H-o6ID.d޴q2hHI\+ I\7s+ȅ&ck#Ʉ2-AsD񞶜 ! {F/h2蚮BZ0E^m}(N2 tuue)BbJ٤(kSUI5$xG|h\Չ̖YCOBJ.>0*= U%BOH_҅u8XAP;3$JlA~B"Z !(SM +ItUGr* {tY*ݽ4PCuIvs܄hSLGcX0L5 0aԔ r˦{_~UUU4uAnE~m?O )!ĝ[fA 4@_UQFw.굫^ ^c-W M95~ Pn}7%'5/fRS(I?f˅&xP^͕ )8i_Ž_[[C>y'x'0Gz0<$z{{  g$v~(/qL7[o%%5 TT|9&ةMj m$v>T 'W ! umPfY:Uyּ 'v zRڿ_=vS|U|r&7)g>|~!<}l9O X {BA U@fyu_[tUW}`BR#X95LnK{e!^ryh]kfCPZpN޾$bX2`!\$Ko89Зf@L=TxuDRchZ]ĭ3IpWUM^Sgկ!/FL% 0uS5yn{M.8'C#??P̏'թʛGa #}A3'f|(7j3[RY\l: AMj*B[-AxNx&׿4/p0,uc%>]$uQ8~x2`~x$:2s{[ ifuȫr^6qGwWwcceqc~UQAxdrSS_90o?Ol 6҂5 ɏ匏]r_)mM(6xN;D痿e2`}Xda2}uU`K/Oʷ~mߺ6Rt ye4|hB3ckG6FSceКÃ7) ʭ2CyZxO[y+98=s~ wY14׵&LutL>{SU;Nd)<\G>_mai<3%+75$m&C6+̧oeVӯn*xgYn&vŋkv c:ěGQ[ ?wV玩e(+aWsX$ K̨Li \U5KD"ǥܮd=2]Ż7Z"E'B,Pww l0͍D,/Vc>1TԐ¦BaO4U6lO$Ԍ BU/v[JmnkQ\6 L SSSbGY}2푈KTRuJ⑾7PіǮ2(&lvb0G(Mw (Y9σPF GFz+1Lև62Q&v9Ɗe2`f2_v\4B BQp rjF]p(]z SQ9ysDŽהN(Ν; P⥗^"5~ךO1B_eh?]$*6Umn} K-=AeUf1Yn &2ij\X(s'SM.[Z ESSS2@*BqDhyy+7%߇^W2Pr4N]KZ6AMj(le*&yk&1e頻" f=,Oo6E@(y΍\D,w}2CɊaT}Xش°]C [RZ[.MVv'wY-J /7~Bqwde XU||{*6O0T_>YP7X-*d#?UWڈPL("=.Y$ꐍJg8oC*CНJv ɧ {ҥ,e8B! 21W7ܵcn[P+Ȳ-!\MCeڙ+m=נ~q)k(YAY2 '%|>@[W>!~뷲 4˿L(V#|u`*l 5"Qk{pO!w\,cP ٬Z5 eh[8`e?? pLx&g UzD|jz|,]Pv,x<*p Sp=m\qe |dSgϳv4ЎS CPNȱ݉H{ 2rBVFMf0ܲ M[kP sGEWWW3@($g]. w}l"Ľ޻ww^ͅ2ݦraƫboz6py(3F(KrVHԵhD>KpPVYt9 B,u7+Sa$Y \أP'[,*;,q[&ӆe n۫ܲIZo4fCj-xS2ڪ F}P߿PMlM5$ Pn ˿1KtokN3|Z_>ulJ׻v]U n2 6l/-r{y(3P |}~h_GwbP*M_0 JƐPѨ "?<ӇP&" [.kExC jΔE(3PF/>XV:7VEpə;@(RT e,2;0K=d(#9|хoVV X&M x C͘*:ЍPFw>;7'h3 D6Ql Ƒ(2uet7 :Jykubrr2 ʀr G7c jvvD,Ǘ}%26VQPzan 5 |e:U&B]?ݵ <6=)/nv~e u3t˗2 e 7$\6!ZrW Q@]Ǝ6 ŋ/7 غu+m`j#bEB߈},lD($2i. xi^9S*ʔ·v)k(YP^{w\ 1~ҠwNueG}$"aBrB+7oA(ssgY ¯cCP%Llu-/˨b&'` eVosMHZS2r??e @(Ο?O(R;֒r$e÷rƙeVD<`i[Els4tBOxQ:`vǢW Dxȡát}]M5p&B(n^e@cc#eCP#>B_,зla:2+kg4e fz|E(LTEMڵ^#tB(cJC^;m0xO_LәZO6UHmc-2]c]5tO~̂DǑ9$; bkjBqƬe%Oa䦆|zISk3¦јVPf}uđl ƺ4´)>P‰&K5d3?5ܞ @܆eoo لٷ~yfQ{ }! |ӵMa.&nT$0I*i=ё<_׋q&~iP455eu@(.\ ]ڞI U<;Aoj|ı+nX?PfE=fY峨SWc>Bx,+YFeq -25Q%]0l]z  91Vهg"y }c^owMMk(A%pS(h7TXNV%Zs*;}.P;fM %FicpK ߞ !aMy~3 ]y }!<:=PL}o x!Lʅ2dԮHcDCGŝc@  xwDsvռu*>qp;%G4`aScy1TOl+gh͸dQecŖPhkl&#B9|kxuuJ"ַe@siB1`ح-/QcݼiFݝ"(P *\NA&#˶|/ϏeL {6^k]a&.#padQ(6c`ۛ3bvv6 ʀRtvk[~ZB_MN C8휺M&|b 8-,LpUQ;xg]ct$I.]2t_%BʘtE}a( ߍ[>\'K1bQ"Z*BBԭFM]d)^eF e }hf-Em#??e@266F(ubЗ(! B }=01P+[!$={̦C=u) Zw'*b&bT$E(% ĜG"y`32D9 &\o*-mO9|7„u i,6 v X%KWe{{+<˨Xq}\teq]CVҸZ[ sCa29{,1VgFnu&ԗ#76J(̂R_TJ6Uzlü]j("ȫ ݲe bIx+`gO(,@(JT*E(]׍wz[軾'p/_pqlD I%nqD4=>'gZ%AZk(Cz4L( ''~@2n7h4e@WW1V7/ X؃[ C,l:켺u"B/:mوPm ^,BLZWB e mv /oagBPZ9rPLU0VWwOW9ܹWN0rPbaӖgte(+<[sZ D&ưZϡP(s_WdM IB{,@(J?zy) +* }c{YB_PfBd xTơ ` *Z']Ze |t^٬TzEf+o6 (!~'Pf̵e̶ڀ+nM=C`aSS0hE~Ea|2\2ʆ&:nV]1LJVRB(a1 ܺ`qXOOdBP*ýj)]γLq:_>B_X,w "¦O2Un5;_:my(?{u%FQ X-.5 Dt2 ᑾzBۛe@I,KPV;>D;[2, C,lxܢMuAʔZ뉪TvRw> Fz0Ζ Ͳ_{a܀s@1'Z,H<7UxIBolZ5KZ ZkG2>\2C=uKNBQQQ8 ?BaeKmC5~'Q&v17.\MkͲʅMv*^4B5]63Qs5~߸VP­=J7#,ǒjŮ6Y-ʀBq]}fْ }9Fsn<EDiLۿ[,\@,lj-#6d32zL}sa*97܌ʵ,u8SI.D2-eZzx`ݻ[؄ng5 @(~~PĜVLepm]V(1mʆqtBw>]kA"d%+УPsOud,~L(~1MBI(N 6a]W2ry~:{QPںP e 6.lY& UvӦ<¦eʼ0yHKa0kNh:GPfUӡ e1>9$\H&B(= p(tnznv}^;Xseb^;wনeBaG(baS8N;lhu;V9ysaƐGZkaBV u)i>p]/p:YPh~P$6̬Ͼ=omJlJ"iG(' 8u ۗ #98}Ϻ2*_rro< g\JeբtM77 MMMV ܋)ʍ $Ba7Xo\$ #'c(Z(CDDU!:>X@x|ݴRY؇gb||<2wߥ8|yܷފ QcݼiOnhoC(5#q˲A]:c;Z3:m-2T&nQ*͡L̺VCa7W$aۋ}yҥKٵ vyg' me4*ϤBA(ux-Y}E]n(svls)D&4##у];$APNgN5s.:ŋ/][Bz E׶<ӓ[ EKʴ BM>ȫ\sZs N@Ȋ2/[[e2Dk!BXtA97@ɮ! _UB2Ij\oIrJueJI,lJ"ǩĭ)h6Y^(ýI@Tad٘1J"YߎPg :Jdy>Y^{e PlܸP̵dm*9ՙз=~𡒾A8ڂP¦\ra2-Uҡ [eW10Ae(,4n2zgFMԗ ӧOgP|<ӿ7k0zZ^͛tppLIMڅM"ώd"Oofּ;ӷMt4<,cP lHh /_!' |9_V{f ɮ x EBEo㥓'qp >&٬raY4i_mK*Z OZGC2mzBx9`eni-!I {]}Bku"_h/LF XأD(D(SbaSs(d7:&au\%^z*xӵ֭>jPfB7rx&ܘ"֭ˮ2 +B!G(Eqqtn<75"ѥXTYAen'Aݷ^=93;<ӐFhyKs O:wؕΟlS[ .]ʮ& `*6$S]uHyB2M*^R-rBvg:KAP…aF*2磸J(Ν;]5B;Bqvn }Qۿu76") ao<uةM+eNX:O B2h->SG(@(~gP,>+ig9nSW˧t_ 4 B ;JW]ZCP۶o3W9|Vzu0tW|]B011A(r 2%\VLU8|9f iG(Sbad" >ܟ\ׁP|ś4 I=1G}[nɮ %_:s}:+X_R$/ eOt7!U8 ;D~oʴs[Jd$>/ 2̙3dB_!٣P}B '2\ztlh +@,P&+tBM2(R*g )ۼl 7le@*"wNuc<77e"nsex_pPH8 ϲdUY6`G¹?D(K!|t0HH%lQeɟ ЗUW !BY.g2H8tZE0I\A?,,S s""N2e-V+I=?4G(DQ-P=zPUFK~lwD|;p* #;lpFj4]&hX `@ !B= /ESᲽ^AL(~leG}dX;FK7f,œe`iW6HApk5^O<.NdY6dvǢC23B}.E)_)PfO{5hjj Pȏ. ADže Dpcc \9P|[ʮ, F =2#^=1U]O_41!Ue;NB˩9fPZ4QX&a\">wi6 J,b$!۶mˮ }Ba%Tk3 y~coj|浫 ,^L& 49dZz@1$kC2-݌T#%uϺnB!ŋ+@( TM\smC !7Q&޽sNŠL \MV~:i/^eo{=T&(4|PfsW.x2Zl"=ߓ]BpI.5TB;/ee`Q+{˲qktR79v_C=uWh(hYy'KgYŢM psWIUlz~ǨʎP(]& { EKīB(>i!lC([k+{MXpe/ fYbB7ܮT!B׶8*隄홸vv92 py C3}Z+ C/˳5D(^8^s<۬V׳R0IQZ2djqi"҃P y0=WSLOEPر# ʀ_5Ba3 ڄDtÇ?!- BX*{=f1 NGȕ0\A0C2@ߥN t,@( cnnPlJoM@llބ !B:!ȱ ntZ{. sW-t">0&IJv~qngBP~ ~ڞI 'VpA+ei&Ri6n}Q,et ;١to_9lW2,@( ^#qum\DL(žz¦C?B22*{]{$@z`YV[L3ln˶:%2gz< 7\2S

s\/IYE1v&˰LW?Iw]AC<H$] WB(:bտ_5>Z%[l,6"PFpbO'96WX8q$X@f1K~ _۵b2) ZVQŝcW&dBK.y.?;o }9)Bcet\ٛp:eqٕez: ۵mV _6Kk842•p}[iӰ|n*2-/سgO *ٟYB-[[?0 YC(+SZ%I,SH v:"ANg<yܒdb׬d1rT$tv!E(aKxSCr5nn!$]x1 5չn\֔`1MU.E(SNdpj8F!FMTma"T*6 xkl eBpM>߯ޘZ.gƐp^Xx(޴mECY?=BPx}&Yfes @+N4&D֨2UQBݝe5yEm+Žd(vU9 eP ec4T 0Ƅ]j l=5%A Wg[w߆B0̿gBjBqHK }J-&lE(A(!Tr(p"O+o~٫ʀ>f eyT"2{Q /aU8PfEN~e/Pve1 +EBB B~2Ob&MGW\6"٫ʀ!nieUqseTZ\J2kp2x餲096h$2$x 2VѨT屶} <@($Iʽ " $;Z*WWSq}\R3 BX Bb:^is:yztS a4]eRcR(&9m hVZɄP0Vc)mVR& 'xPdDU+K'֯2p(Yr, Wm<ag }`0X!q&H+L AF=Dh|f o  ۿ[ 4ⶉe^x.Y$ 0xS%X !B0Be8IӧP mv9&Q4 <4JbRT(PK&! t˭Xv^lg? O(LUR B2~BtRc39^L_1b '>@(SbBxI'?lP\pl6gF Km }(B_aҏPfɧBwİ,+K$<)aܲT|B#VR |GuaOe| E*wc}2O.M3%: 22Ws4[,d{(3E5IC2y}LC(!SzsHsK!_W-e  K >oks^ıF„߇P&DŽFY,8R>cLdC(!=݂W0^Z˻ƯxeB/Eʋ*vDS"-ᐁaYNg$3*haQKoF(!iFA!8.L{O(eBpS}ט+=B_aE(dVa>?Bg^j5y*te p}wU%Qx[]ͣWXr \k^Hm[۞N?sn0E(s3i$XF!,u0C{$|)P&‡;fa{-P<#ٲ _5B!y5yV͛V !y=eZ丫N9.%˥gQ(8Z B!2:`,>?7r:F(xlA( he4Czy@d Դ:sW el\6:F>׿<7aazgfRw&ypm&WRWj^b^ӈHC/p,@tY\\l*2}]Fb}",|Cc71F,eq1,V\aL&ܾ &#f<\Y<)c;"Vw[OAGԴI eW_}5]?>E{.&u/Q]Yq}L )  y󳙒h;6xt}.بfLNRF.eq!lmo5X`{Oyϝ}bey2]&ҫRI&t:C6U{xvS'])zQ !}alE!oV@_Iq'ꇻi\3dRc|p=J%̮9|U)ܺuXZЕF ןk 2ZIDp4Y biGn4X.N;LjJ{R; ݲIJberqiNo}GoƋ $eꩤpUU-SBft2[q{+2=1ѱkylxČ2< xqkK.f}8ܩDڙ'O62??2_?Z~nj.'FGB,ZiOQF2t0MTf)  }owi3i=f+y4cY.;y9q:TwjI7 x2\mz#˿˛HoKr:T<4՚cG?^Ҧ Rfr o@^H0졯+u6VWTU!F7 0{( 8A [NMNd2>EqvJgM^O-]e oMs8$ȏH)={REYf427}7]$5^4 gR%x,~z^b腦݃i~HXXC .ˡeuRH;|]DB{+Z,Ls,ky:EБtpٙi:"ZƹM)f  ehhA~Wl"T\~MxE2L@U+̙)zzϕg|KL!F160fmD[}}%aJJ=xu#fe0R)*aڦ` r00J*ܶnH!\tAM6"iM's^&aO07KӼ4Z&*GWSi/L J|_D[#R(ggB{ P0m.eD^x@!zBW2Z!i'GS(r\2*``!e|,s_<0Ow>=vLNL0 qJ)Li)ph!:3P+/'/RCz) ]+4Mڣ-4CL(<2|4o2)#w'bs|mnxCepЬģLJSX10tyR훇`}$KW^y@!z׻.!ECW@?WG-4՟HUo:I_(lKb0@flctmD<6X2Jd,+-wD+eg9}ia~\ls)MWrsCA8k MR&]8`t8u*j ӧLsN)W ȲrV*tcgr;+=؈v aFlL9Q%LLL4HOO|͚d_q<*ne-4F|:;Zr!/&˲^@h3%`H%{+6&r0~Tp)̺R(D!#=,ݍ7*.W^m"2I("qLWErò,X~#kt0G1(d)3IWЩ:t2QUv<}Ac\/F¡, i!#᭍ct0p=\)3y|beyqx(hq+ rꫯ6H#+;Vxz|t-  TFn6ZEj NDA``U]MRze5+PHq8V\}_8}@>T)3NKҺ ccz62(yT J|;J`mq%I#f$ 0 &*͑B'=dFt6 KK;ntՇʥP0x2M1J'ǝYKC(X ۰aua޴䈄Pv&ؽN&,c5{L( F2yaL)m!8qi@ bHe3-جo#tXwSPUSk+zR& 7x؀xi3=)AO؍t"J>waf+Gx]ҡHgAc^~ 'IEo Vo7S8^*]77Hdllr Ms+hit+p\*]޽k:p)V*gcyl͔II&*l*4 Z%e|.H^ c,oS02t0#?=)r]& exK_岆'ʻ\h@6 M3hA C1#quLG/omۨSI[vQ0l*y\zr]D2 򶷽Uܽ#Tڣf8 J`bRf@_S0%RZ}2D .4H] ;5Y;7 eVwY䥅w_y8]qm=]uL:{Χ666JNfy~NCmH+e=ۭ`$ f4^e _`[B}),,,] /ONOxqX7!߯(cܓ+t  ejɄ_[nLtl&wm&!qlmC!J(=sNqzj,*y/h (tgAtٟ٦)ܸq2 /!%#c1OgِM׏{ikJ6)S Q.=ESU~B;Je2)#D2`t:-W0Y?K#7`Z.IM;Ry饗.~pQӸ~Lg~{R& (N]MuGgBf<ǡnjGHR,X.e6 e:E0V+M~x]i5mH~.ӹ5O48Nf"NNv B"7Q RfX6,b3 /eYN'f 2ɄWtR^`)F"Q*o',W]ʦ1 (,xdy0:77K^-eKEE ;h3g3!Շ,0jABIg.`4+CTD gFL2~b>g{1;jX$ i>W W݈C2G0ĺ,q˃)P0#?F& )܎c]1 ՇGS!AM ONto 2ȿ8񉱑n\l74qVv"p\iKTӼ٫{|Hbb 5?`R"9rڪ,!߉[Ѝ9p) D⸸"wd O}G]>3>@ w322Bt9WeɾX4q9ɺ#>գؼIeӮaa ˲pJe}%niv,LN\.{Rɞ2>4;П L8]#. ހB=#|w|GADZpU04p B,Xx>, ^8fOIJ  8~=Ʈm&IJc+WW_ѕeUU _HƢVK(S @rAGD e ]-dpQ2I~K,4(f¡4/o]އQ~L#2dAy5ZJN'J&`S:$FIzK(xlTO u<֝| 2w~w]d%x0MɜƺTUE1 >u['eʡ1J*&셑AH(bN<X&HJs+]6T{ԼG̘J]`D p(h]4ѿf׃@ ;T|9`0DIc.Iڵ4RD,jH:^8vt|B)Z(:]0ܹsMR7drR8u ~g0͑B E70jb>_X-f/_^$ 075% N'zDY>nN+x\s,|]Z>Ko7'rCUItyD e{sqR6݁ YeBA$1Ś穦i]Zxˣ٣ɤ؟C2h3EC!aW$c:C?5݀sK?MR79Ct :y4`L*h``á|$BZJ;pX//QAZx4}%,70 j'*pU#2 +>5^鮛5 r@ ?DtIRw-uFбbUE?aYvin{pLOLH[)ޕ}%BJJpPWnX4rH_?@#辪zMR1OO 0HɣbLL[L$dv[HA@ y0wLBUC8t6ell@ gn{gj>qI~d2$X ;Ё--~:"N1XT[:q:Ojj5}Ci@Co]|f 2 }rCL*Y4Qu0M:t2ZMCSx4U!afqd2@fgEA3)smsDUHŋDTn F}04bE!|=12``׾R/@ o}[w8mu\i@vK8t>g>:z_Im)nD?(dʥ"[9^*kJ~D+lRoV"X)qLu>󻴙+`}%,LR @>&)7~7vIxQuZ}/I~ zRX!5o3A4X. i$p ٬W.MR1ɫv.R@oϙ 5I0ms~H(R4" ®G7|%J<1G䓚LZ?>QŽ^千es=D e4 ~]nL-;d{JwmVx##b2֖6S:W='W]ZA|:4-S`?n$хh"2y\BtƢГ(OT"{@AtG A˙OP45LXhd~uvwp̙}%ɢ@t)Օ\d9)ip*#ш.{D e%UzG#-\$uI}C}XlOƽs>|pR$hA) NՕ.Wpte%Jhbf2+:k#// %k}6k@׈gyqcEEM ٌ:-kJl;'UʝzR`\LGܮe,nq}r&M '/88 a!DAЧsip^n6jo7m%]U_kYڳӍ#+iJD$hYfhD@^iZEp)ڟA>4%8w˩(WJxCGA~iXʀO~Đ2((n6C.$M_q. {}\1Gs..g.O% U KUo Л&1G23 1DteBozQ].Uy<{[YSˤθY' biXt +I\, ]U۳gy@J9b%=fN=~B1 p8|q`)o$(+zͧz٠s}-fMEK7L♟0'b>ONd)<70~<8l\Ey4zg7İtq`)~_2 rAƐ"^(dK{h 1];w2C >@ ?'R  ^~9v SG| {R=]rS-ejdȉD,Ùq/Եr,U>Cs9E c½J(R<İ^ȣ]m.5I\+%hkeF&fg1I2Nh'ɵފㅔLJ}~%h(M\co|(R|c#r$2^!.4bQr! Vn7I .t.%Q<_9x'T2INDaoNZ6uGC ^Ź%yӖpa\B5tzQ_{Xʀg\Tq ڪ$$k 4 /eke1L8pbLD_k>gr [NuaT.bBȾ;L^dXʀo.C۩`Km1:Px3 9p2bDвp8p2tcAe 8g+Q p=Z1lmm]p"Xʀ$Ce>ikDNȢnٳK-1bԁ`4!', ѡdc&s!bиls j 9qbdBv}YLKp||L w6PbF&,h`K2Fx CǴ'p(DN 'b3pbu &ΙxP g*c#2!\/oo0Y_,e?O ˙4JnRUb:2:]/]LE3S}\0 #nlHެ (ɁeJɀ",=76a[;|;/2_$e@8l@D ugr=7$sipDܠ I ؠLGL5j 3ϐȲꫯb Xʀ/Ġ2J ^7(dk:]+fTGmPUka6&5RD e &΍BB%rP77gx@8$%ޅBcR˧n>*0ܫA 'B}b٥˷ݜXxꕊu婢(8 E!KF1؇4Q5S B:Z` 1\rcRubxbfBj7û l<ĵOTvS$9|{CSEQF\!g]UI$'v ,1.fK {JB3@!R]1Uy6b 8` !&MS~͏ !IǧL:E6R02$Z ȅB(pTQf.eģ*]~ 1AH.0L\<:A'$8;re&e-wЮ\XE構F`)^ySxZs1!|KkNErBĵ|'$ęOĈT(hzkWc&"og8$FrEs*1pR& +K𖷼Rt]b輻ףr!4Q6۶R^vSeW;8 ɲL6<  1TG`OE@bֲ]Bs.!ިG?QLKCNrs.!|kH"9b/z1e*EcaIQa q|Db&qQLF,Xʧs Q_ i3VAm/} R,8EQn 1i!s9՝2A٥Xa Ω+ lW<@EpD[E!$HEBP3k|U3I.4z~L 9z``)ybXPtN  %Bi4gATKZʬ#I'18yVIpD^X4J61h=m?W%Tv1Q '>A PEBRAr")ۥ$y($q٥1X$'Jbvp{c9yVؚ%1ާֹ=sA\XHY_Ek _b`(R|aLUAс5߷ڍ:9!by-q٥h*0>yS'wv76pD%QGf}t4L 0`lLEl6!OTtd<8 ?O[@mQw N'ƈa}}})RebxhyNOWRI.T"'{ʠ.4Fk$ QVȝ ȃLlD-ӥ&#j'Dƃ4Hpjͅybxꩧ<,e&b1SGSzMt Z!p.%A3M%nׄn-/ሼ&<ٶ$Qh5ef%&o}[G2S1-No\:0MyK2]3 cQTv;Ng~G)lN&ɫ21x)n9rBJ *CƾW^8 _O(=pz|n_Ʌex -F<+KU.-,;;-mY6gV1B)a #p载e4QpJd.8 կTU%z>p!LEW1?Qvi.y($!&!+;yr9yDx׵`l,V,L+,U&'I:UJi1 e"XʀEbxbNs82J/-ceIb/9[O$|BE8"gj5 %3X z*89K ;3q`)|Ab8QyUܲK(׍-p}״ xTsNRyAi.5?ʟG!F!a(7͠J6C 8q`)>l<'N&M.^96q֖]Tn!&A`9;CTJEDp)hYdc^r_k &' &ޣjȅF<$I4 K~b'w.-a>cbe.M 9 !\A ؅jlQW~5|\DrThi24mX(R|%YQ&Oܽș\Rڳ BLm|ox™z8 w6[pIW~>%\·B**.81yZ6ĉ`)B1<pB|TU 2Dȿ}lI ',[F V| SēFs%JN2K\24u_Hƈ!R&*95; hɠ]H'__8,e?qbD(CпWndLU(&jBK1Dtl,R@c1Dp) Az\^ $x+D@#'A(o i$CG2_"14e'+I3@.Q&%260_a1#wbh,JH6k'Vs>O0Sai>ISez˲;,ec'kQPRC,'۵eqdxö%Zsffcl܉8 7}aO ~)'OIx:GtaTn Ǜk1o>,e@>'GWP_|zɅB(pXMe\}v3l:MAшw\96 @V$Q6!6a u!zOom/w8 wy'1in;f($r1[Bv{Jy1b0J;eF,, dc&uUg]JZLN(x\yZ&7}Xʀ|ͦQ<.|v_ɅeT 2]ţky#L6T"Ah* Wr*A i[i® 9RuyY8W򕡏KĐ Q,w~Nv #R&:M\"D<͆֌=Ĵ5}_A#IpI׉G&\'&΍B"H&Kw{Z:q`#l(R|k_#ySxs_EɅD@۫hKKq`)õz%#:mDAh&B\35%.MdIUջ]ŨmвEQpD#Y.'` v5W%TZ>/7b˿KJK4՝Rx)TL'A-)5MܑeGmL6J&I `t*鴥 ˙c~hohXʀ㟑 vJNc bl! lp 01H#Cѡf{)9?3uA $xQYȉ?i 0ķ_#G2뿈A$1 9Ty.t*٥ 1;^iDaKs X&r6:rJ*\~y Ƙ2X68 Ň+ |rXs CTXOjӚ]*գC ympx%I2(\YꐍL,2EMC"M3w!H;# |zG #H2`}}޶04b^$t'EZ+ĹnfKܝ1m1!)򩥌(8Q*[.LU7mD k;aۚ#8 =1UJ(L#oB)lV\3%]Qw:BL,q#$(2fimT&t%x[0 ȇ'GӌAeG:,eC=EaVq b/}/٥r.Mpx4F88 OE$QRngli\ә`.%BNȢx) w󝑎Ku&8lZC!r!4 osKvYg2ء»_#_ʜSၢ(ģv/H0M =Õ8\-UepёKT (*+Y 2.|vi]GG U=d K8\S$fSM>z nE"'4I{1 B6FWT0,e@*"vQ!|f} ٠qPq R%1'J p.+ģ)"Ipr5@qLI4KCPãjr>_xob뮻0,ek׈7(O5+[joe. 1I[^PR6 jP«R@&<΄[&z!9򛛳m cA 0,e~bXP(|n߈EɅd@+nѝ db ^A< 47S-$jSX-C'2.B;|z7& t/aXʀO~P P%J9! B3:Ո=[vIMMΙ'd:TUeb8A Diϓz.=C%WsqM KUoҧALbx饗0,eWS›kqpMN)y٥7zYV: <%jL [8 dY&apoL@.\t.h KKvkL2 c<(R?ED,. M_v 1GkG <&St~)0۟k tEʢ@NuN2 V5`)1 uaR&M.5ex 2.ղSx3e 1E#KHomkjuIBE6f )/3|)&+j.E@HC o{6,eO w4P. |ҢM5F,xt˞6!Ц7]tWl!ŅˌInnOж-/.+K1' ]Ν` $D"xhs޶ £ZOdl(R|C߄B:p-K76.\K1 p`“'$ *İpw_EGōV㤬` ԙLp[<|T<+ Kĉ6,eg>bX&م,tVm9bU/y x0 O(fO~٥6]BI33= Ckvu1$Y(|I`yK0R]!qrBXF"qK_0 xoAX B!T쒮ݶE%VV'0J 3 ^,FGē5*9ko!ک0:: [zoo]maeee)Rz=bxJ-GYMWrK.3hb ZF&WׯRV. lΒJTAs|))h.F1a*29FͭN{1K裏=B诤-\ .\ >u%Zss \YO F bu^Ύ(ē n (Zșz,zҹ9Kb?1K}q*jvAFťLb %<%v55A]\/e,~)SpPH8L<,-a4 년H&K4gNut֢b׾6q`)>C<`TAgWˑ04Jbmܯg={SBg \Zh;,ee3iԹs3 T+4$rL0j sSk7:#_b(R? EP=אDEa1lW-VEIN.ηApZʤIم[R6Aq $EoZk̔T*剉`)81<ٿ6LPwʸ 3쒥bB!t} vy'jN xuv\ Bģ*m70l@ TӹA.,'Z\tİq`)7槼ZME Q9!4WݶXxt]l!&f2N huFlN6mKlr`2AF+3!M{b{[\Mb-OKV0u >o&BTWKɁt ECL-TV2>A؜9e86A b2B}LpX:9!rg7\o(RPFHlϫDNЈ.eK1qoovpQR& n+P0HP;ը 2.!T-n=DAXAŨ b {pI,wCK0tDr"iv_BTD Ju0n6-3E^G+KA%bXaX$YrX躆.YE<]@Q$x.%CvefR>_!7nxhXʀ{wҚ^XvA1GKR,CLz\ξKUUqP+ks 1*r 6!L>|ooy[_xl~C(R e&$bApy*ņ qBL yQ l:M $E@ "8(\Z?=4 LSe?bRerBZe_IsQ ?}jY>>ur! =R٥6d]ʜH $w^^ =)MbhĆ`GL=f L-y2=0}{G2&}TJwEk$dqAf ٥2K1)r+ D+3%D N&xVq b'W7R(` Eirj0 (-eAE9 cAXFj>9a(}͉b=7,eC=D  >^>2.Ȍ3."$w\90 Y|xc)#7 >!@/L È=N%^_ES=ynXʀ~|M35I I;?r 1S) Pח218<7dc~VH0n6H"9a[-_wŰÿznXʀw(Lj5!bSJv Zlv)J4|͓ wO"lqVouL &_Ig,z}0,s(R|'M}Zq=YQf 3Fx*fQ$Kk̩_vqV5KȒ5K`, DD,=$ANKGqA !M*₌gKmG"Ĕ1XTU,Ms\lNNji ngE~ 7-FNr7}ue\c=:,e.1 O:bxG=:,e3<ÿe*'䂩z`.-VCkabE!L(21^_HRfk\xYهg\ ]D na]u o7*Ýr^|EKO|fb1y]^VU\`vI@vi!H8!Jtjo zy &Z9#6!&ɑ Z-{[oG2K_1D %N&M.y-G*]21u)3ᩑ/lK~wyO ^?>Rm\<:|*& 6,ǵ{fugG2?)YPE=[YCˋ pd]{"bOSo)#7hV:|J0ȸވO O{,+p5MVr1 .E) vX,-L*_钍 hL=H^H\$=7㵶>'-OKpwj |ww)iB.8'$w]]I,4S;mB≼QB5B`)$835jL Da>F})db2l!a}̜pwǾ PL!΢}oެxz$\DeQ$'B@=v>#W`Xʀ|×P= 5mv'.Ő]Xi^n2bXʤFY8 DAhĂG}T#T/DžyΔLpJg#LLϱd"N<9S &];#a'"8 g!FS23X\kfOdDzKp=T1&{Kt6ؔ \Zhh5P0ǝ`;`[ wʩʸ]*Iw A(R|%CQFP/oTI"'> S٥!Z2'\[Y4Ά &d{s^(V0 ~`3rh4]v '\TU}W1,eeYlß 3 *{Ãjx]DlmoM 7V{Aˤc$`<%7j&V1rBZem6dbF7(Rlnna%?uYv{Z%bVf^W|3,e?qbh&-NTr!  Kp! 6et;tYE׳즒 C'wtM;۝'⢶+Tpke . &Y} U3ՈE.T'+a+ /| G2_"JfMV1<]# 1iģ*߃*T,!oLj=>e+eg3~OVbJeI"eo2nV{a K/q`)~ ǁH4r!e3fUQ䕖S ~ùM#RǧKZɛe fbbHd .Mra6{jwj̛+MD LY_dQ녜 #$.eEvQf N!՞Lx(E\v1+$ \9Y!' j!$LR٘*D@Qo7/{CvX\\8 7n w,_ww"~ 2{e\.!Ĥ(WeȄ -72|_i&xdIUp J0J:4ȅd? w--C=q`)>xU] j1X#4f 3{s:0'L`a3z4ef&ޫ[nj-o&W~J Lu\*x/E;.EpydK elhPf(&~XoP6">>w~j).&Fk0wG1><mdrtPf00?y] GU (˅ٙտKF߬` Z]!/0x$1:t!QP|;!׃ nOwTRECwiYK(1 aSc^ַ}No>a?s7_{M4&>PhvPtN["LB 5T:l2,;GVzKC ?b0U $8EiXyCya]ى b*^t٢(i3uwu[O 5rjo0$ G} |rer&68Lw$DtYm34]j;KL>c$YB߾[d~jc21̔J{!>MO\wLŦz:5){)hK.K$Y `B4H4D ʀ1b8>P}m,q@\Z]lZl }ֽ˟} SbE* e$\]~!P谭l3#6tt!{Ilr>8 JU.';w68 ʀg^n(%$q 7[w %&SV;{iq+Hx]\"Wz;7,Ua4Z)'BUow?-͒K| }cɠ92cȲu@,G;+hq ԧ?i ԡ~SUCw5Ekʧa<7ݚ[Qu5_ |wA U2gVZh'>[-y¹Չto'&0+B>ġ'N27GBCCmvD)hmdQ۱Dw f*10JQd }nWzW**B>M#!62mw؋RS:)1ZرɹwOwKuJOqf}0, a"d5LݛL.fͤ0CE#.B I.*}(+)@(0]bL&ͻ@k_hJL˵ɹ1].*K/|ݻLP]^b>zoK_2ٙ`(3]ʚɴ _Lqb~Ih-|v`K/wBQPXuiC-3:4t4l!KYb*vW11};wfi*as/}&9Q{pLߵU\|yʿ;TuѪ|X9 ̔RZF裏nq o|a1kl㝫#Y¢$K.JɺgdC#Udʲ4;𷿵*@UC#f?s> o3 n0LǍ)`R9>2l)fw{>68 ʀCg(HmLg¿!et4M*@ @ee2}5ؖm &AgZ%B^/}p3E (@(m+ԔKLUH%K(1׺d̵pf}&_s41 FBU2y$5\Izy0RL}QIja*s!l7P2{M[L pxm^GtuʢȻ,7 u}RFvdIÖ#r<5].;&i]looo 8ca_Ol[N~`X2X]BwFC|6Îpd`@$kW#oQzm>3q%J{ׯU%Xi+nPh0dR!Ch-I<4Ãp)48 ʀ+&ض)!u-@neQbChn}%+zha \Q(|?_N6C8Ed'vuU!Lpr>8KM0g᳟l 7I 97'ǻ#aHd K֍-~jWG{ }:gt"1Wʜu}|<\Kd}l(JL6 KuY$(@(kOkۏB>]֊(11֝GGUU!>,I4X=d{("9Ϭ7~ tʒMhiΝ[rc-K+…|,qp袉sA $(@(^y (4>uT?!$v*f''Zm0޽D,cwxC= N;Pʜ}}Ӆ*\>h6S$";Hŷ&4^< Ip)kMI}>_SM $׍7^>SUّXn1@E9bwNtDa=^p֙Wi(#[ǩ,~uLCdGSP\jE3[|+w!u0]2Yja b(JM52&u@f4(lKXb *x+֙ a+$ M B.8m['}CeNMfڻsYE3QlǦԂ7@/GSOo=p48 ʀ05tP[UC沑 v]XS_䱜f,÷R ;P梳rr~x=_L@baH-Plp!Fk!R˦d:I lq ?1$=؛f2izIR%#MzTad`ZeܓO8$°|C@%L=s"˯~3.}KMf';b/je3Q4j#D43?쿼~58 ʀXqwGN:ҖS,iIxy|'>(Z}87_t1T\󜶮=gU>#!4SDgyiEqI F LjgZ0I_ǫi쯀n ~,(!r5Z4>2c5wзc#K.!`x\Z0ESՉzN{2]hh0uy^5 AEֈm&CvI<<`jT5AD f{DWsxn&GB".C=m2~Hy]5: *Đh'DQ!@i">(D WXmo%B=yϛ5}6XԧeLj3Ȏe`)#L"ብ 9I~Q' DIy&GB`$iw Jcy AtYM_Ct ks];%l裱H~,zUxo{W,C*!E˴۝fڵ$ u6 +,|-5'C,Zttw׶E۽K92 åSmz,vy܌ 4Vtdt*%^Ͻ_RPW~o(@ '{]V7+@e]ʼ;qk3ڤLnmV ]a-.cI8tCMmdfM;2裏&Seӹpb- $N!K%&Aس̉w>Hz&!Xtr(+߳PCti{o"+3xTDm3;:NmbS :ŐzzMb8#ve_92td8DM}GV!K.t*%&q{;;bjz·0|G mB=ȕq?x j31$Z%l2<hbn8G#LֻL@ {vej>POD'o6_ݥ͈So9ĄZ.o7]'nc3OP*.#_ZmC'0 3Xbet1&^9˜ݹUzNI}1~M;2#mc(2q:~]JAQba_s9˵5Oģe=uplf?L yoeM z#v"]fr fh+׍7go02LpkE.B Ivf4Ϛve/~*bvk;E$ an..K%∈#"kH47F Y|c2JB=܇>Z#9>&bWǶV@43DvӇ0. VM^}fHBK [}͸uާ3VoxRmDs[܅C"W*c3? bV|#qy1UQa b l'f:r]H{J%B``p6*ƮL-eb 8ӈ; 3LAݥ|%:C߽+JW*Jم%qdž2?*B=w٩ȶLE&>HdWr9N)jh0Q̄\ƙ#[udb8|eW_M Vl.x58x5e: R%XJL.Qv|dX}aT(U_<3c}ĐedǙUO $ؐ`@t@iqvN& 8Fv;R T:VeW VM ,1|hq w}71d|ޚ, -@9\ ag*x\bi**qؖVl !D)sBkNzx)w>wlSg(H >` |rP0['R:o*.M8)1-ndQ3$Wʸ"]Ό-n!1enKձS",fښeib=Ib LMΑIwwp ڧh =W e>Y=_t,buO!\KpBVVsSFB݋A?1Ό-nbH+ ePkuׯG6igW6Qjt,4%7C;4ZJez%QPLMMy#JD/0i,]e* l g};wC!J"MZJĈaיŭ]K B7|GYqa:1 c\l3ǻE^u{W;X+^~dH%Z E*vn>!GBpaGIHR^bdඩ.ZvLd)W"qTMә7U2q9WW%L+%iLDd"!U4යXe|. \81V$u@/18p%QP|񋶊@,Z&bIzAl%Jv&mMEIUUV37kL_;nw`r$Ɯ;o8}!KjlW&6f ;`Ǩh0Me`p"J"gꑏnʤkiq O>$1f;Ⱦv@&j (of|.tZ2Tu{$q⣈l_u'$^Y(2rvgCHי%lO\_4^EqIe3Ӂxۮ5q.Hᩧjq ~cf( Jc@[c~'+Kat6-JL@n(;64(bJs1\|_"óK['.a՚b"+#Ġ@fT64XyeU&RPęg.U9 .-EĴ{qv }-s(&KKL.n?tbjBm.-JCo(.aK{=q$681Td: y'҈Ps]WSO_wdr0 eW\A K\-]C沸Ѣ{XOӥqYObz 䌲(G^sM*y;V)YK,յV#ud,8l6Ubaee̼7`J-ʀoAfa]XIB8 8|Lvkfa,GUb:lNG|LE e&w+ 1X8-[e†N ?|1ž7f*Sװ3Z*R:G^sd&`e #81:tS*hP a^L <.m(1ŢL̫eQy=8{º~P&de~N[׹B 16lKGUi3{(+Cq ^Cܭ;©Lأ) e3kZ)~ӟbJ-ʀ^z=HJҊ=ҩH"Fvͥ#Y ](1)jp]T|_i+m1}IN /7VUy%} t2gy)b&=X, }p#&s LUx%/TZ $S=<2 -KQtK$Iڻs9+}s<%A[, :1-ʀO<үL@K6\' %XAAے}"N /w!WhER@4_vu.Y }n{2jt5+e}&q+ZcQbػw/Sr ?H S>]#* e{${q}%$S{Q^L١M|Ty,lf&t`?' ±dP%Z E,vh#wݘOA˅2;2QEi|%XWsK{CAg~M#'V٢ʼmo#r.1\Oi4l1k"7M̧BT,_.z9Ql.*(9)됉RGOug2n8Dr["a"@%$z!SЊ 0xd8 HB%I]w-TZDAb8rnG?,ʧNefJ%b8 q~D-$oȆ Vܖ6xḘEQ^y C0>>N4Ib]졍%]QBqY*8c(=i3C.vT~"2P&{Ӑ.I*8uO8HvF8x)hP\p.E]l ' b]5r+ Vr$Y+9_QgQ/*"Vʨ*8(8ԌK.1ܞk` `jHaGCkq '`2-ʀ{8clp] n)©ΰ+.$e"T>*`۩P؞؇ xCC3_W_~eL Xel_#^MJ"j(~ɞJKNvƃWŔc_k{Ή0yq{(T!Ry wqqij_.]`` & sшuUWKd /G>4Cvt`O~*@Ӭ#*I]w:$x'PF$$2O=bD4MnVd#Î3]+`rp! FU7M? ??Jk!Y:Jt*mYCS<7Q|3qNA nPFvf(sӟ"WeiwPYRB^lPk ۓ1{ b!xm%I:3ʑ =\I8+-c;T&\yw ®tB%Z(Mlm_ Vї|N e~qU4h;B^T7n^)\`Q+MQxQGww(BP/] B!ƾ I™t$($q5  vs q㡌!3,;g-_s41|U4hkq񺌑<2G nmΐG1{+L 1<2K; 믈.Nvw}Au$)qǣ-7*'bЖ9(E#o4Q 0?k0&`h0-`'S$8LOO?2lU+ ?~dn4a߸l8&)vtjUxpZYrpkh01%hԈW\q / /RjF(֗ 7+K]jY;S1@6\S( kR mp*}qUU+6MIYM{{:[mKDFi|,q6UU/h O>d@)juJf3Ik$|+"_gg躾Pto؅T.nn&`!L&1U  =\HtYgkCd /Wfjj :!=l#!<6An1U_׿ q06eG~ܦU|x{?&hU Y 04uٔvdg>ӆ˅x='1 ?2<An:ݠp4$;S8ޤtM] 0v҃Š;G2EEC.8Rnw-~d X&`ttWo1CvYR%[fsdxÉC;xa煝 d`yS]?G?Qefi]9 4iC]j2<^8cZE0$hLLM^DXÈ&ମqtMU[,r&&LЪRkA??zj 7YySk[k]j^U{l]%HW4 $"~3~O~9){ﺉ2L|c ^e[ tE^Uv'&Yޮr1bO<I' 5)Oe˖Ma͔lmHh#Ԍ.iDYSIYU, .$lJS)~8.WJHhxB, 7ximT;&ipǽ}{'j2ԯ"1,J{n3#Ar1qB">>=Mt$$&DC2B@L1tAF3ݕ2X $xGjٿ;"AءnRDۘDz.Dz1 KQ%e'|F:_tML&w. @0P6/29vP58!1Y{SYa[n< t Skk(D"xX<`X6kc!z|KcO"T552a8]S&f>)e[:O#3X2Ӊ_Fw&'JYSk߷;<<|)+l޼Yu*pYU]jh- E&e8q]HxǮlMoD;:5e/^L" &`MF:ܠ &CXtRkjs 0exg:,UUI /ՖKRNB%=)kפEk{FUG{w!MW/5a! ;B `TÙ\.?'(įmY\YBvUJ41 x'> 6~M(ҪbQkʐB!ˤu5cp*,PϦ5IA`ZLI 1y饗ݻL׿):B]^S 15֔2K iqo犦8`Qtrᨕ+5LLb1?3!Th< LU.ag`f]̠W+ H$UUmzq 7xʕ+ؿ[QeX3M>٥5\.(H(0Dj_H5Jř2rO$z.Te&`EK9 &TsnŅi]!1Hǯj1 ;w&1 Sa4Td.PL VрXH(x3P@I`,-CaȡyNzj 0eطoߎ;JR|!d|V]ranzM0iM <,ZFn+$ktuM˔N4ez4elˊ9 =Ude; F9Uo>C&2 &h]94ap =X w޺uk$eG)39-vM!G`٬ B"'kU)^U*I=W_A8|W-E5&`\e.4B4$ZיLq9cZL^y͛7Abxn8yT4]dP |ݲ1Pm'IY==2eO4e{{z?L j&` XW]E8s!4Hed46x!1]gٴi$I$Eu8 8VA.ޥ@!+ <ĞTBG" %emgb '9]wԕJГKc];Dr $zA䭷Zk1)?|g 0ݞU\d\ϱ,{OG|6DWnH蟖)3>єHʔIt`_D(9/ F%:J0A#i[9CooW_]ka)mI²  40e2 CSӡkPf9DWw$bm,$k,JOo/_{6B0mbaL冘@cȲkMկ~w(3Y(.ͅ{: U!9hLhX69< :DS6֔B_H ~/Bm2޽{֭aNjLu:3.AK]S'H7prq El S)x7olYg^ל&H#.A $ 4A-\ Iyamwc4MDXY&bt˔ AMuLUY]HwX`㢭ŝeY=yϣ>ZgyfӦM,.`B{6yG]zr" lIsRX AS˝x>?KI4MUK&b(-A4eJG(P$Ũ7J0H0͕:Sz0L?qF㦸0xґ]Dz,xOGT|;1I^оp)f&I÷4- Xkx V nZ>3)kW^Y066_);7l0E[E,JBvNK=`X6kcD% οfٲ2):c|>oI_Q-ȴMn ȷI H0͞KXCbӛoES_n͐xLIf] 3גG&R]J*犦]BDPdw̓-_9^Mg,=r('>?+|H.·4B0q }qoWd@)K&ͳLJ8CR+k92eW]uҥKI<"-{gT($'Гg eI+27y`ooLh4bPGy:aCE^D pB &`e"t9| H0HcGc:::o߾`NM@}7900@qUiy}dYdRUq#C3H"f׿u嗽\!cgNх>,h՛I?Xțbv?B.GyC&YN4D!TzFί}ujS"z &` "wu+ۢ8!ӡ3]N,1'x?\4);7l0E3s.2B5TrL+: (2zAoԃY>a<:: -[. "eY:C2 ZN&`S }w@Ldan&:_~w]4)馛=X"pápu8e֞&..t5-sq=T0t}zgz?_)-BC-icb5KY=c\?;o|;ׁX,> >ļMx&Jw||nڔ\yjcHiAP'ԗP`BOҝv$B& GX%tp cIbOj<^S ^vхW}$%X2]j% w Z^{m  `_I<,,κ]XKXjRQ5FLҚ0G|W Un/kh 4!PЗ϶,!m`ZI)Gb])ػw?? Kr^F3Nk߻TO0T Қ:HKY<02! -[9CyP3)I\T^0W{Zc,Dz24h"Obd2۷oo%-l^/)j|U^jСlviQlj*ɂ;hi0uȃ^Qw`4+lρױ%PLƱ#r%LLKro8e˖a & _<묳 ÈOS]gkו[34\t_he[FCPUz;_<6q} {vXie-6V,4M;[5@2'|ӟ$ISdTe:3q[ph B7[zRvG&TR%K;t@q+k]&K#B$*U%,_җۄ5@2zhƍ'5w8ԓu&`e;J'TkӂtJ9C>ofLK/yf˲H<)E\dfQŬ, Ml)l/yC-uNVC1EEgNM53 J096\$;\MI ]qs5LOoڴIeCHMq`٥Rs413_=˶*JP\hj3@89)O%2 x3n3ш* UYUlSL sdJ!JmٲeϞ=5Lh֙H4tM(&bwRI:ٲHb4j2y淿 2 5UŠgkJ!`٥N[S öKb{wqy.i8>AJW"Y8vN ˖)R/iS5ۈUU%(_x A~}$eV7Aj1[eN2$(92WCK|<$:vbC1iLlGu<³clbZ0>cߴ-\|\^{WnXHLt;w$&~,(nYq~V,t 1"QDQOr3;mgZx~/4{a:999?m3~+MB#PّtDJmVJ5M P!Ur40Wji&KlZ+̢+'pOȣ0C7Qe[Fij>.';0A+Ι}&4RT O|~g<x&w~~VSI&.'p4WJ6-Հm {Ced=3}N(Ci78bAǥ]Fl< #!of rsi8@jyW80l3^`4<Ŵ=P@` M1b̡RZ= +j'Ft0AqPXtBEBHprrt4LJH*H*.aQFovJ] cJ~wJ0qB<<"8hbt1xM¤顊LJw#*L?P'P!t"L)A<غJDngPoGl`@+k5NZEdpV2;Dž}fqqf kC5YfwBCSLF/1UqSJM:RӬmx >rS[u0MD팈a̲ @! !e!Rb+*".Rn14юsIS 4jZ@0r;~|>/Y n )J Z*WjÁ5 LߞXh41Q0s4/! !Fm)Srwiz*l"S]1< EPuHrM_Ϭ"Œᨪ\|M!MR3;; &@O{j!&L٘%~-!P!|2Ix>ѢXz.M B_Yt/"nEQ@@\0kfe٬d $H}qgx)&|SJM(Rc;NsBFϤf4 '.3iqx:Gϸd@ XYY?k! !?~i|3͗f*@znr^uUv#jo%[D.$&b&.f * CSb8eS *X*55 ɹxtzQc^2t0&">kI(!e!p=niU"4\&%޻ߓs PS F 駟pp,Ϗ>` }BFp0^DP~<(5oA1{L^/%˛ !e!  O])MI]ucd"4<9U_]x !ֻ/H!eD䲖\Z}bRE7SjmVӝLR>QL5X|{B(B/b|K9xj$@)Gӻ49T11PMB_"Zv? cXӑ4}8hv)J <AfJQd3 *H|D;s48\oF'2ZNNNIN!VJ3#z닜{UG(2sw- hS rn;m_HJqXt'-.3& HSId>ܦY;yxhR3>>iJMz&>OGSu0#2Bd OuB(CaooW^x<;FRa`/pPXH5N=KDZ$Ux;m|_\N/HX@K>D|VWŭ8X2CS RlD"ASSjܹj*5z hbLi&TGGG}Be!d{{IM]!"*ͨ)i2a%4<dov/Erq 9~ bLo;/M1DDggfY-֔xL|0zxSWKi86i¢C\^B(B/@f>,#{&Qt>.޻4V.j>aDrƒZ:*N/XF~. ]G{`ppPW'>fy<44Ő(5O?p+5 q_ 奿'Wcl<"]Ƽ4܆U\.vQB>裹9ɔO`ޥjob$*1N/hxV֖NepWX\\4i4 @mq~hnz4橧RI-6^+Nk#+L)ȐNcb54tQB 1`ddJR>?]$Ŷ*N GEQLF^]0!uqtt;gyy9K@ DRpCћ !oq}m>`pb%eky0W@<-bݻcLcB[ }뭷^~奥rdf< ,I ~d:et0&">$HxWBe!?7bTb:zjpK tn:&12 B2xfKEqF!t^@@oڹ풯(P(l E To͉~I־b);L ޫ6km{K:h0$"B@"P @@@H :w{ۻ{}OsHjdDŽ1NF)c_ eU0|>_xt:l6_ Z\].fRSrcV=Myh[y\1L&nmێ=!y/˹\;!h@+P8 t($q˲jדEz}8VEavFBI^?Qb)&򻥘b8e"bI|BfNS*Lä1=B9O$VeJv'j^,z=J5%wɏ‘ȕbZmwDaYeޛjBXII[q =CyP !@D$1مZfwa?;_ǣ🡡}/ńb9bԬph4r\2diC뇗˥w |>0y:rzdn/jt:TJ%>Рg+S. PJ$$5PRi3q؏&v{: _  )h4T*4l6.*N;iVH$op#=Ռ%R ?z:CA+{w q}@8l.Mi]# ƕMG]ߑH=.m4MZ'{>|e%@27o8F_I3u?~IVi9=;tFQ[e|EQKwTV gO(F]*s Fq(?8QXVq B&*vq2|>sN3_U+Qҹ<{lQ`\N`nTNg2_I8⣘SF1Dl6n6 /ӅqjQ ׯ߿? ZVʙl6I8~8߽{98(G˗/a_R@~>sأz~@X,|j5ؿ0\G?eo(^̭[:NL7O3jGc2/^L&I(3\Cńaxҥ:Dh$gtTLzV<}ts< 'O(JGTJ^);_3@7n R)d2RK?w],Q8iyE^P(, ý?G1 jx8^p!꿿 <~W\fA"K17o|z2n\N`nPT>(@f}>s_y@8^w޵kd2yS 22 (( 22 ( 22 (( 22 ((vPC; 7ehFbZ0 `uix[=WJ9z9<}߭۶Ѳ,Ƙia`Ѩyh]W`<ދHZOkطJ( M@m` $x AU0Q(_Pj   C%=DE@ !g.v ý߽L>+&`VVV0-//G455e/n$'dzǎ|YǍdQV5;xN9˻ÌO~ξ 3ǟ̉D蓺IIm_UR{Ե$$3#z33}yC~o a< \%"h .)8)Gh'5558oI%Xvya ؎Z˲ajb ȁ!V\rcn=An~דm@7-,,@GQN8o@O> _|+p+Ʒ_ xa@z ?BB IdE Isp Jp Y(%zV m"`Yǎʎ`"7xe~VUg[ OWsFia ?㖖bw!aNn]GGLxqMÄlX]me^ @|.|<- 9 '2 @#CCBȔ)ȔDJdJ%ƔDJdJ)\ $jHIdPZQALI 6i-> C>^@ŧ.:xLX?VЦM> !ߋ ͪ|# |_2G.YO=}7)3  2 =ҫi HE $3  H@zhϞ=*! !a9 J:$ʔD%n('ߕtzyq>4S{cƅCAl,I]v} B  O5Uw_s[ f$-4İqb)qI $3 ᅖ a(CIIh'.9?sFk{= 6(P x ;KΜtWaUlm-[R aGݻUBrrkzoN{ko=_lGصTBz֭ÓOU%Pu0i,"," eI,Ƹ:,'AIj9wȠ$Kps5[.:Q..Ww<|’}kךR!a(&R wKzC74a(Cp߿'OF>q7 2O)}^PJm҈̙|ainj T&W^ٵk~7G2{JO=aD)=Pm{UPt'˖-| fPIJ)uһ=سT!mzdeeO?0'~ { `|o1B/57U-u-SJ)Ŕb\5+̌9r*a(C\ҁ^"4:YÔR%ޒ98NC #PV5B6l؀'p:%y9;ܳ IFRJ[K! eȞ={nFaD){F  ״BR(w2'#ٱc^x~_X 1wƒw΅?!#RJ)b0STTjAʐ-[z .+W/wpg(c tMXLX0}ߗb.6Q9QJNfEywN0!!59ܓK۵t)K?G$ K<Vк^p|?\\\d|\9tꦔR R ϟv" /0xvXJ)eҘ!8.4aI(B Yvޭ/2a„z'{̘v?/<ΒRʫ1T8 inn<~Wb,K2vNb!ǔc蔿|uCǔ=MNsNœߙʯVkښ{0ݝ}{B!n%EG_D",  3I9A/Df=>Oq8pF~6D ZQ$Id"-v^{|pСqss'~@&"\Z+:uauccccnPotG") =$-[F?Ґ(CX>}>_jJ)6DbHH^5eםт[~zUţ l,,*(2(DAL`@Sկ>z ?94l`D~3:RߌU L4 tv' I'QJ" G"@lDؑ@*b$ zzP.#/PHa) D"1/cnG~7T%Qp8|pTK6+H$bω#A0SWF# zqDyq20{s a@$p$;C[["_}|0f+_ʢEzzцjSgtrhd 1Jsk@(A9\*j0uaN)|JPS D"A? K ! 6mb1@ 5Dl80;G|8.%9rAU4z4VWN{u+?u~X ߶@ n$qN:[oiۯÆ YYYXe>G`vO= %+:vs`#ߴqAn+n$ܹsy晏>Ntܹ,(?y.;~3p}77yܬL2C!N1F׃$bA,vIBF~P =HE"iz~jĈDbq@J]eC_kKbwMHAX3bY,<ŲB 5x+h뢻ydARtJ* A$x$+ d1MCVLMҤ.PTS5s6:u0(xeYK$0BaҤSٳG8p ܜ3g# (m^y0N JEtU,n9ϥknqz}>Y '$A3v *[hP`A 9T914 7Q1LJI bCKߚ}p"(Co;GHaoB%KTBH$jov@H2WGNG_ tHѝ#Rw#GT[8qd]%^qho*/?wЮ,:q@}MvF:Xk\9C[ 8y5Oi r33X/ƍRW{#]4wD".YTKjq{6Ow450n}{yVH!<㊢VUH4_`G`4S1GxqT>|#\HTqsiIR`#yD*گ#6EGlG7c`S;i̘1O<;CGv+m7(.  s'6* (bAo6,!׃%々L d.W'N^^ʅ9`yduAנR_LF9`Λ|!0B}ѥb!8[M{{ D¡CX Aq[7EBzHNKfG@SHl2+@#^I\na5j1\~+HŜ8҆[s# RDay UA C c+񫾵*v@] C͊y3mcw\Mh](9s<1=OEz`<¯t}s҅ȒJ3Fs nⴛYHfN+e^CʅQP3+JX5A,) J/2>9RZzP׼yP5ź򃢑^n95A^T[jAR<?&Q&uF> ]Pj?IZ,`CƲ].'p摨8#4#8˪#`vքH1őXVRk#\mw8B$8~%- 5j|^,(hkJ} \M;֯hn$/?toDSLߋ Ki9pu Mnoj@Kw]94Ac{MO?`,y fPnF5;ZkHt%(_ gPO- u+ A" g3LZ42J9Q'qb.YvJPu=H?DTLMMMzmԧ4C ģFC> o ugjw{'}Խ,G_RK]ufz#tCstC \6mzQ]EY\~Q7^zv%%a[c v9j L(@+#DIz;ﻕqPEe t]  1eAh6N M AQ=$i 8S O?r82ÇG@[[93sD>W$9R`#|*Cai_jDyuK!r֛ CMQ$9a;DO>| +2 ݸgϞM7\BȈ<~ kf_i62M捗 々Lh; $I7IzN_U49Ę4{)fNP@g$iSL,F~%Q&~[>sp>C;3G qsXbd*#s#XcPH&pޯ@f΂V,qA@( -+2 5VerD%6ĩ(_#o {W5a 3DQx— kͅl984?Q!DJn)5ωA`,F 1($佮<:=D/KڕI$D\Gڊ]ƱN@Y0ub{S}^V&NQ`+MC&lD3>@|/RZ|^=T>NXǿrlY,Oe}5Fl_' A>)ӞF${)$N>8f#DbQȎC!p=lX`{ J Fv];*HBj>{ "Tʋ  X_W3~?  ފkAOSMUfr%!BA-cOCH$Z8<T(C}4\"H$f$6TzW axꪡȒd&=-m@mc>P׀P j_{1;'''G|/?-0φ%r (ݠAkq'`.>qDӈD"ZvO[~=)$fΜɧe"HbnYuCA0`eP3jq$b]bV\'$!-wxWVV% <} UQAY[8mt=v_}u]8E"H$ʜvذaT(C(//gQ%E$D3nۂJx0)Ym5(~d `29!#a.qR^{ C1;nYatf0Y>J2cB(hC%bPZKmqg9cw0hO#D˙۽-U)!Sv]I$FQHT&:q`l&WriQTTgs_@n|BSm'%cϊʉ H$˼23P iơ<&D"8BU 5}6Ĭ(M? /gϞM}bɇ(l)+6f$.'4qLH$Z͆8۷e6l`ZC4zH$-f[XeBɛ b5eFF7ԩSoV/+cǎb4@lk_4}ě 6KD"HkݨZO Q0|p;.H$Tơ(K%?zbV=͙4),̘1;BٻwoUUA U[^ nz]/ K Ơ H$]`T(CB,xLD"ʯ0-u5T^ ᓻaѐP , Au",s-=LAC2D"z"oJ}`w(C8$H$ =0Wɻݳ}Ƶ_0uC4bvq~&m|^dHȒooNE=.:2ˋ k0'ƙe3x7o|JI8仭2D"#3/e=〼kH$4q*q¯&zgΜr15>p|FHǃC?e"΍kٽN-\H Hf7H$tB.͛8t_WD"HH{W{ĮmXvyǏ6EyRJ_ױ'/~R8F~730R䭌:hı޾nűnܱC)s7Ӈ0L^A$D4rH.jjj} D" ó%: ;[m,_1dPsc]EYqOi--v` L3HfB.K,yw?}w.\.>(?'#(VJ~ *xxUŭz ո̸e5wmSwݎaf:Ark"D_Ypjv'2?PeEXMH$&#Ie,,}gB5WΛ5k˜C:5R]V DSҶ黑%Kà -n6e_5Cݰwg<~h&ӤEv?}9mx 碤H`F^cJ>SwT-vΌG‡Qz<]%c1.l:meݸ|7={Id,{H$1|qHcE‹/8H$Ԝ,(Sۻcu.@JÆ @JKEI~)-6iI0܊ /O?Swݎ1MՕ,iYz~+!bMY)<&ޞ0/Cv~u_xUS1m@SMUYQAnV&D|-!`'GQ` Rڮ-zM+8~qH^A$R8|+_ݱ cK$s`H;G%[ykf A'KDd U.iiH 薖22&mX`h 媧8uFeրkYJo uh3;$x"x.x:xFxRx^xjxv0;^ l*8X>XDXJXP'EkmV;ИPH$ZN a8@cE78}\'D"HlTkJO)HpۢQ)^YpAXE+~߱n%Ξ8٨mfPgȞ##­c,e/ T:vXV8ڊ2e.q_D=yz͜6}̕V#'3˃Ynv?)eiH"<[ {uL?ڽn($&;LƍKu-4tUqARɀC$S.UWWGcEBVV'gH$ HmNSQTB2,-Ic A}3Q imF SP!ڨwZl1m[苶dW/(%_2͟??O n4پP"4wqΛ$9G5 8~Yb&ERW2h!İ*(dYxw(CxWlR~D{H.tzzT1s}uw`,L18u.Rƈ%˕hMMYɀUQٲ@+۝Ge3(Q 0Z}ɻe0=7_gM6f$6al7E3qSW vKtvD[0-10R@Qo~qOV H:Cg)2--TC^O>x(<0v๓{g|ޘ7c$*I w^!Y"O~G?QBLxW 8k+}' В!V nf*!P~{wㆍW/Z4}aB4MM(Gpܒ 6S\JZ?E$2N݁ ;dD$ߛCMwKZgJWOi""I.U\Ah(An8TYɭ+o*-j(.o*e$IyY/cw|{K;4UeV[Cc/?}aJe̙oFzk޼y,Ȳ ?=hiQd%!/>>v"V">$FcmbzՊd%9*zh];<>e$[6l E`PEю^bcf- Z!DD8`@ e8vDB&%.奎Zt')Gh PP0?3,7YW #;jsmPS `#t 2Bi_`钷t"ȲDi޲t ⏲D;&.uSAA;y|q~^f"LHēHaګ˰"F`V 6lA؈ٷmP1WH>>"D~˜1c~w(ChjjbQhbg/H$e@g k4.әnn۶{Z.- ٙU]:P^gk+$\Xp+ƀpMU Lq) g:uY,A9sҟhe*++c >|W SRJK`AO 1j*,.HD 8MA% 8D"ч="Lߝ&ȅTH$&csWu\UsƳݤSgQe٭{YZ #]R JΡuCG]G^hd/w0w G !u%*NR[ΝbI!ڊD{g`/B-\map NCI!DgH!CC>RM%-T*)8;D"9pY%^~e=*H:Kus ?[ʄ%SwsOA a4IIFl#uWІ`GQ㓅㒨|aHIJeS(Q|q_DZ*!9OHࡨEyILx&9l-&xR$jk4̅x.|`FTqq'-?.ꜾHe4=YLKL{WcʧuyDP]} NKy1mñcWpb-TD"h*Q a˖-$]"wZlii T?Z`iI F2B%95YT ƺ0r8@hh9~tY.iuLiӦF+Ӝ9sz m2]:3*_=sZyqaB]-x#xd)g8W) IFg-!:G\p)TD;*Q aĉ\$|"5J K /x]T5W]p.=.U+dNܼi[A^IhH3HhZ:c*J:xa訊me] oke"?'^}$a5BAmnw{S˿tQ )8mUe8epG`r )8D"HUDB$aQxeD$YrI4MFL]+ d_hд1 OW eN߫8sB!ā؛c 8Wp =.9uAq qx饗w(C2َKH*R#n)KCYxKYZܰd"lSgRW[22(a,D$B:2yPOEߗDݛزk׮qK2!v7N|1FOkEK#vor}F4QZrKrp6Z\ `+lbj* UE "/OT;D!V8hXZj7>\֡2JMJveٴnPs#bqZpL5/\\ @=AH![> X|bⅹe0'@y[Q\`ّĊ2DRpbT"F41bP˧-i*yf"/_8h1: nUyﱽuȗ֡ ssKEq{e$IW {gOP aV@AC#{LZd߈}#  #Qp9-!H|0~x"Xx!)DYpRs41]h R{ZZ+JV\Hśϵ)JMU\;kg$?|`7Õۻ^KhYʤ(Jl+SԂ-x&־wY/sÒ5e cGL#e~9(CL4ั˝8  C~cĽr__"0\'$p%rUR",X|f RGSVoR5V2Dopi()-ΌHTCPoe?<7^rtDKΒ-bNw߁-vYp ]7KK B$*K⽇/__Y*J E+DL:!v=pѶ]0YU>5/S&Mqx׬%|Dq3CSKD ^:%$ee7?%I؂d)fdt/q{4MvP1;^ ▋nׁ qmÛD“O>8h--RծpK]7ʌCMEZ(hkBIW C\9 C衃Nܚ:lÕ{$8 XwHeɹLtU]Q_35 p]e(][V_k./y8QHܻRA*8Ⱥa%,03;fxw?̦$vcNBe~,GYfLwf"Lw], UzqF13}&Qp72~J#!MÕit;J-Mi8{@s4ґ5pi?{|"3cu|;etf>6cgqkEp\/e>زO}j/| d2)5"~qw\ AЉLEAIDC4VDqMoHƱ;=<\1ld؄{|v3R?5DºuHg) tC݈f.ɡNBnÅ88T2D"7 clOPpX|#O?m M gzuoRYҍa Cj!'W~{`46d՚S_#{-њa+sȲTIe[8LMeqFl?mr3]ɃN~#SSOַH#ojAXnۑA% ZQĬŠj_sNge7of&պY^G,--ǡ{H$ƁkU0#7X:wfC((R!B!TW4Ԍ,ߌ(RZJ(z?Ѫ|)V&S-7wm<55M :&=<f\$yR( #ŰUVٸ0(C={6,0kta,:%^gqK ްxs0 Lj;a3H$zecCw]tکmM)S'O1݉'wKp!Ȓd<\ e: Ly7Ve˖10\P g܉޽{'hL< &n虻d\'p|CDpxSk% NSq'QƝ>קpu:+pJ-CNG j{H$'4 ks QD e>t莺8*sm<}F )hYںug*%nۇ\Xdv<NrGa}P$fj6tsׯyƟng߈D 8M|2c$1eZ2~QBSNB%uGT$mhOĺ2-Pgv۟H$Z@8`hs\Lznp|z+ 8?hlYD"GA>x<^dv<$3>~{Q6%bӇnzk |;2D"8D$R"'udpr9MͩάjӈPm>67q9זD+NvH 1:R[)DQB6X(`h1L,1,fY%Yde M$-8~7~K\do];a~oY: aڴic^z뭥K2:L35\O#=۶@jijaөKs8xIxZwؐm%d&K) K d2*qw{w3H'F ۚdT ]b)?mYI!y晬@ +0Kt%v0fmY"Y6qK}7{3,;ui#ߗӡ!Jj'9Ֆ%2yorxޞoOAl͆4I⁇zwnO&As4Ҟ֒OꟊAbgåi%&MQJ.|"#'Oe &Q00ZJoG|]w(l#x"86yGxjDg?ѬL}k_2?I5#}gOB\F -/2իW3@ % co]l-gՁ52\[u1:00u~U/de:SHYLg8^J"=|ֵ77qb@ s-K/2M6z@ 0Ta |{qP wod6_[|M;01bYӟDExwޭVVߑL@`Ǧ \Coj OO @ ȢhLMݚuSq ! ngj~x8\q$v8ۺ精%Ee~gsQnOj+iԖJѢM4ŽP)l 9׿5 G)J:~'kΥ*t ޞ$%c,AL :}rD@h/޻gׄv6:0dڲy*ߵg>X,0Ob.{<2v3:( TM*Kd )ػ36`౜c*3ޟg"x@ȹcRS2-,z*zZғ1>-k9΀: uٝ'3{e%QDҗzuoZ&i ikV3 ,y*JiO*JA-??ڈ͜f JRsLd:L2;nkK,)hezzx?*{<̉lGul!w Td $2t9VԸ¶5tW,l\ EwS@fw2s:wD ZnnvtJ$.0ö,$z!/oDfqyHMjZo:Ad nA6\`)r^XB}r +V읱aÆ dY+-~ ge`<>% ]//^QA_qk/D6FX>wK$LjhZ1|U͉ͮD"q^g3Oز֒(Cxg tMDNh7`pL\Nխ*c>E` g$5MH6,=!R8Noe q%{=n6 ÕN|p%]{d>OGQsV&L~% oЀ03nUIp9BA/ Aʁ^E-PƩ|D;rfkqxWlYeI!|`rM$I|$B<=gEǧ/kQ6:?? ϝ:0iҤoT?я̙t@ 5oep<jA] ;h3~qᭅWVkCPw$k&]UߑD"898|e%QόĦX,3"H ³R錆(~ NԦ_-tº y.ńI}=lteLŸ"8>ec=K_ѬLw\mV8)JmMPdd_#{-f @wZ'5D"/~527 aB$k:wgfpCۑ_B9=DAB!FTC@κ[TT|jx߲ ٯ#z˒}gL>OLPjN2L}F&My[GFSdAP}F 6`p81;@o  BwD"TI%z-kG":_ ;m#0[A }I5evԇ鵇}Q׋D>jbt,KMf:L8[躵2͞=0.6_kt9ycH$}n6L&CED؞S&+q{T| =G8xe 8tė!mV?qlH;Q—d 2V>! |J,K'-K"LVH(?$U4u`n`!ݶEȸ5eTok\N1Gc+bV=8 b (hJ /֠lρPD"1vitTI1;.S H$e/Xu$Q+" 63qhy:$2yދO[o +exI҉ߑЇ>DxV&I2ee21 Њs^p + )2)2Hrq8di@#)ts|Xz:& GݎrkPP{Dql-ZLj*68{iyd$wt`pi!_f!892wF-Z5|IئCo̮-" B=Jf-K gLe:|+_B|Xf͚U[8 "ג[SW[T {*SDjE0?M$5.pTI1>l.QM/I|T 4z(Vk{ޝk/%C;xK%UNt1 ߸90Ȳ(TO2+A[_'τNlAO"Ѕ&iNGD.'s96mFiq˂bANx\M!RD"` (cc\ve?$#/vU񅟫WEbS>ԋ$:B/溌.sE?32Y(2OH%S=Yꫯb )+S㙒F8*_7mTX1[A.'coѬDvk\z!H\38\uUTI1nơD"/ɠΙX|qIB7] REۉ$ 8tM7pߡՋr2:~7jB95q~zwH\ܝa9BDGapΡE^ I|fްCql95ɑt/j+ ^ÕxbhNTa JV3|Rc5)2s9I:SW,mS$ ūnS#b 틈p^GOR%Qx ZH [`u3He?Y *Fvs#+}p'v/6g$$S-}vh.l<*?Ȧ/}KTpǭiƌLCLh^ O-45s9ᝌ7s\N0wB(XWEѩ*BBXCh3[S̨(cc|ӟf(D_Wk$YBE`(xdQ=G]Hbd(P/ 虇UYtG1}FM ÕL=t|tL*t8묳~PuVL|:o3{vU)#r9lǹJ\N #/pBj`Y(k1DiIخ%ψH$NiN0o&U[elc1Zģ$eFk {#̄FVmΘ#9qv}BQ _\;4.v?xU> 8dY2_.heBoex?ue (2nRdw9^ 5%s9Yibwpߣ*p[n\yoy)_ |ߟ>}:aā;=W5$ ¢Y_~``Nh%S3)nYPD2F5.BX3~ DboqU,26~3AOJSY2e$SN5Y?Q^-IM.Ó.%R+;9KZBeW@AMo{=ltfJ[Ié_*l +Sc84|uUL,f%~&C"SYNpv"+i5Eg D>tgxrDuBY(xX , d$cï~+$o6/Dȉ&/w4cv^Gtd)\d .܃e"j{J)*%a7~^Z_ugc$3'{!pVtiZQK/YLP9ǍG.'Hh*X,'Y`AUl°m2V 'T&Oݲ`U&$5%f #hv{z9=Ds͸!JP)imA옱)d_Œ12Gړ GNyI늅gVэ\Uёa:.?{^OHeÕxL"LZ 7'Oc+ϴs+ q|EՄd#| }4rҒhm"-ED.fjL* % f@ۙ x2?5\V.l6P(DUDۣDĥ}mHۅq45kQ9s[Se饇@qFd{*"֯UI@ N@P@W_.'F4+h7}5 η}V-FI_D +uMezeI#9FS(̮#Hk `U Ɓ)DcN(pPN(Afe~]Ê.t%&~qꪫz(c{޽qX>gC7l_T, |H (nʥNŅd% phHw]HBM's!qt4Yh$|5[G-tXf~V%Meںu+!m]W1X\U\S9k\Tw.'F.5`UWNnYPsBeMأb6 \'鰌޽{t(c{u] ?-5;"Bjp ϹjŖ;Jǣ{r̲m[ ;&12A-cl.Y7`Ec]cuc7WV)r"N iZX9 X`ut"檓T$.0GI3:im/t4ū(W/9""#jQ(_x%pm΅ Ru!IƊ!0%4~ₖ6_ gLݽuSkSX;†tt,Hjl}OrBub4\N&w8j.nl`L(ADZ`7FlMp'|*&2/8lKR d3ZS=6d(9(<(2_ܲRKGmG9r=2j%Prԅ(hu! ^Z,75LZ?)KA~w33PeilO 2|OTc!S=pRP-d1+ݎd]4v X)Dd 12}sÔDݮ@;5@H/pnBTK 8l4%r5*5n \ L+]a"18֕e@-.刅F0{$ɭ)$ {:)\NpgӭU 唰.!Y[v2Z/D$`2o2/~J5-fh(xyϑuOaƖקTq4ub*kR}I4w0rT|Ui{>lÝ偸k ? !GĐ@@"D"B$R ɂ$l3c[oUpTutWskkkmDssр jE;q^s=`kUNl3<ضU9;Y\LJ3`\}h1!cQWO\GǽZ%C%dU~/;(f6tI;^K+ .)&1|ߌL`aR^MEt;yInt;7>,Ĩ`ccF3~z!qF1G[hP 5n6 5=7TN TN vA k}ۚ|Lf7Cexs3UG,HU#)Cz=d'9 _|׎pĴ?=e*ɗ8Ti$4mkW!LBg nA-ѱ%ԩGYU O>?SVJhPz-?\{B_DiTN-G֡ aQ~5@F>۩>ojl31>0!VWWE ..5{5<"ňv6=|ҋ64hl)KKB98THZIϱ0NYH00iY)%˃Ml˗/#;lR& GSf"J՝]5~'r҆i81q!רNÀVm^'rM31>0n!9"W]AD9GZ9F/C[T\ݞك%!fh⨡BZeUHɳF-&)% ܸqe`шۂ \#jd'FADf1m';}O%j%/(T p[BERx'^k]*oif[ɊBK#;L|ꥊ ,d m4X.Q׼RT! 􌘤жm̙61sBquֵۀ [ ⃡T|1@]G.LDY2DCe{2UN*#k©`U[95V V*о\k[,j!egyl2 1%b꒰eB/X:R/ ic@B)*&W_ ￳re  ,}O$]H8LS`NTN B+7E"a[Jʳ-\hXN<ɲURx嗅 i%t39QR8JQl|o y򏣎UHʆ댷 yz#^UO,U 4=Ki g{*brT9ܞUMGVd `)U\,Vc'{ /KUAH7,vn[#JUSQGmn//.ZgGWzUH72mie19Xbذar4 ΁TÏ tOT9!Y=ȶ& I1 6ya7`m*)C;/I9BL!V.YZɗq!_K[PMDgvFw>NBbXuVݰj cĉyRJ )蝙HX+Ѭ*6'S儡s*'i /A֠s?wLeF$/ {,F!e?Px׽؟#ǖ~Z u4ƒabxa!cK2UHhIV}M€ ~W3x[J OF'0)vf'VӾT9p5 BAzV%0P/m2uէ BXk/=1 /Rֲ/J_"QuIq˃Oȩi>L@eB/!؇ΔӄU:u ,̍7Xqq%9 3=GU!S夨rYӉlk<;Y#%Va)lq5۪ 1MAH>e<˟|?`Y|5kPP$SN8jdT5rQ! gXeYfXkXb ~ PutzۋʦYU6;w#]3ZŵU;Wlbykb5%]93gX_ B|M>9ӎt/ɗՆP+. x{SZ;>jG?g!Ɜ3#8@> RpmdPy5cYAC beVՔK>L;PA7|VUy>% <A7BKY*/UH0?#KF r4n].Sr4`"HHHXv.xWLӰnTJrR崝UxV,˃ ,yG֋B2_@ K)$_]poM욀_;K6ͣÞkBbX55 Fs4RI$-wHص23ݓ0\J"Vg;s*',@֢ͩئ_=ߚtUqzw⋬"e/²,aM _ktb]a%Ň#aOIhp3R!Z*$6Y0JaA ?MqDU-KuOSiTNKөTNNr6ġ xL@р:BHg&]$")Ck@`ɢK]B3UK:Z̮FAp iêdX5ADco{tWF69 QɢRJ3G땩9D=l+ʩfST[B]{=^cAW^vĜݞF)tI3Q^K3֏},$fB ;xJǮS0c`X5AĐ`cch\MM&;#aKlt# :x;_,'T,'C 92ĩS.Ȳ uI"E^PQ%,P?5u*8 zX fD&aբaAzќ8qR*=GC] k;۬etrJh/bx`4hl+) |GNj‚0KBl֜oe交vP^"U!kiw'uU׼^{<$'I n[-2@AA&IX&@ * Z[M#-rEU= ~,@@@c#ihY] &l!Ǧ0._]m)XocIw0b_=W{괜涍nž/GG=>38qvAG eٳFjaVg%~%]Hvȿgъ_ J+م(m!Rt.~͛73anFC,d4=HـX&-xxS#n_ZNцbSM$ӟN Z(wLE? ,䬣IkӂWQoVǞjM 0w UKKK6}"hߔAœU{T^)$;FI$؄FvTp\9q8q"ׅۚ(d|M:$fo .;N^#OWN W`*\Oa._yl6/Ƌ 6J ƪx jŁR{_s{rŃ;'i)b `r83>lB(&e0&6-DzI:N )ЊRZ.`5B²,/@FSב tV4?Բ4t^$5ՃQ8'x ʀqJ{I˜^DS䴐vwQWBB& C7)w+! 9w\g1`FN©MA±tUYmi6D t(aTpPMI)lC(Ǐ'm>ǏEczgӈb_/G/~L趐+K݌DYV-IxjV= xxGrqoo \ 6AŒ@(s'-' 39q< O>1NZ@<\R/i&-0nzIN XR:jJ(,&9!eտkk+++nFwhz~A‘*zO2ZNJAs Uy~mfB8uq{9/ˠ4^fUt`S32/Z~NBz&)5&'Of Uܸq]˝Ğm\q>&U{R=uZNx;r2\4#3gdP: !ssp×eK>O$cz-9fHi!aYO? k׮ g4$\@W{2J6#3ݪ-' .~EQ]r%2piո,`C Ղ^]hs|ZkmHPZHoNzu&NaxvnFk.b GFA¢AF?=-rrFD I%#~:sB|wVJNl i6h}$܌dc[HX$IwMҥK ]=::MWxO^0H85W 5P4Gޓ2j_ Qs_왍%q,//gBxljӻ7>D^]KrQmkM6Һl1_t} 20Qղjd4FNF/Q$P+9FB4`  F/b ,..qL6Rczjw P/Ip֌LdGڟro*[HJ6, 0ߖUkIe=P'2=z4 &GfSx{OG-'ߌ cȨǧ-4*ئQnoݺWPz)H%1iDM1AI^ҾvFG (Iix;-fdI'npvjX]]uow9HΜ}2AžQU j'hW>[N5v1 @PxW?e뺸b9|H{<@za;jƫHnA0Y5Ma6ܹshֹS A#R3J=6JU1Jbm9 %=$iG/ P^x^?9iD]1YRbN))8B ˪-aM?XYYd4;l0Jhq|G>qBmF{,P|Lo8} c32ݟde/26m Kz%}EPRSl@v81' l+BӁ30UӡhyޣIy?iYtܹ>g q!Ğ)&;*` Srq Fؚ q*`9MT dk-@F൓-$,>vohg׈;HXi(GOA92qoqx7cz)ՂZH6")СC˪xf4fmlA¡:{Q 6H.UϷܮy… ٯ'݁t]/i!ZH[)U߾};@FSבlA͵ޓ'gwb/ZN}NǏ ܭ~{ոMϵ^N#j dcp.8)LY˪ xS$i0 \'d6(oFi܏> 2Çc҈J{I3x(hOv!Ş ˪mEƍ@F}f4-wWL6ԛ{rGUrq8yde {G`2{e5KҌn^ i, 0Sszqq1\+++nF"wh5S})ctGWҷvF> v1󂽤}D2F7j9$:.>&ZrYѣG;)˗3~b꺋c3SCxk'9tFO|o$;*qx" |GRp:czG59݄Rk0nrbYu `p\vh(EoWFA~5NGZYAPl 8=0;H㴪䝉B4)„ahS[V pnF355E\h0H8_JTSKÈ$5*xD3ⰯYqP>s!{͝խ E]1h-$-:E8e$)ewMŋWWW-]=::5Wy#BT%dۍKdZPN>MϧjYKa8uv! { ˪ UҥKo3g91E0hv2A{2W~I2 nZO<B(7|hoe=)w)[q7Ufl޽6y饗{[nel^u}Q6Ɛ\$gVo]uR}OZmii)2P{%xefdpfXε_&$mMԩS? 3Ν;~k#,,,(H.ʙ'7a=fpȤ6JkO} @\9pC;ڍ !Ǔf0eB6Χ02֭[Ōfڵ,mǽH6$v6% ҾLd4uh` ٳGH)Y&iÆ r- rM6+;eV秦4efggeD:6vc˒"%y\-Na>}En dgx)3LHX~U&3]m@(C4եL$eֵeX(+=qTY28ywi K\i~h|We'{kKpɿm6[@f A 3Fuu ޽֦Ld-0RV]La %+,LiFyhx:%{]P۶< ePdkPFYh҅/NHaeAPV m۶hҾGvWsc:Eɮ]4ePC^zu!eu͋_(Ia*]VO TҌƲ0d4|weg177A(Z$a7olLeܲAP"jy)vGn`U}u>ٲed466$ڃ/o߾ A/rXTb4uYKWʪm5Iccc;۹\&o;)m۶iʠn5?2|>/_D.-.?*ϟkʠIe"qņLytj 2x;Dx;S) B *q=DL$=8\*B0Sʪ -hh%M-=״g߶o߾i`U!C'o2WceՊj F$-o۶2"d݃2X>L!Um/+[V-`MLLeYQ-/12ܜ 㸚2ʪ{zzdU )hfcCr 'NპB@ҊByq,d5ZV=22Ԭ/_׭[2YN, MSLo~ ɓ'W&$5%bql6YLaP,.f4k֬ _]ٓ>vY @(HsRJeQO f+Ȱa?(xB/eQstructlog-24.4.0/docs/_static/structlog_logo_horizontal.afdesign0000644000000000000000000016147714645734712022211 0ustar00KA nsrP#InfT}`l|9He (Prot:#Fil(/Ht-:DGKxa۷z(2G%CкdAlM0\q-zOJII2Ȓ Hf}Y;Z ]HB%ubX"0m$a@/ U˱,=Js}`zU,lsgRr0پ\1i -BS?7'J)Ha@yrds U[DnPES(ѓ7&$:-R6CeUE(`(OHp.8r%\wD;U,A-lC S-F}5BپHuԷElhlhB Fil;Jѷ tHe; =hlg@&n}JHg,s03Ú F𕖄tnLҼڝ>$=5D'^j`ap AÀ?=l``@J96V `wȔ9 g}פ@emMa0i!tQ4:"HH?CU.2;\/ "dnG O#grBAӆ-TNZ \$Q`[][h5Z[]]\ZP"jQk$E3#@!!V//Bז- qS @~4-I=w$y XAC ,X< C/e/-0Dg1k?kחѯ.OBZH?zoQ/ zF*z=Á oJ*Ty!*&(`{>׵ +JcqOPoKyQt/7E4 KQ~uXuվt! *dKV5Z ƣ(iMߞJhZ?Rz* }w*㿕E:]^V|vYey}[|$"%ņ0'hLJ)So &qVäL!%,bwx2DDZ+lAjX0VZyeu&m?S[g9fίke&֪֗JKMK EeCESY؟&L;u+W_@40W&Y`H9VUUIV!$DRBR󚌙Y,lqդza隲?45jXV[`XB~QjZ!t򟫪t JȀ!Ⳓ+( DNt.N[`<&XYX`V."eK U_վ$rE#W5$PBBeX#4H _`x`@PdW Ŭռح*x;o(M/cezvѣYW4/E 7$Wi^/\ɰM} - *j=.H ʱ"M-16d}q mC ! [gKId6Ƌl$?78'$ -.khҡm|yYo3$9nK=y0/ ]1T͔da rP1[vyo/rL?U‚O%iH\yxuJ4d}ǠJ&(T ){ =9kHr@AjkIMmȉg<4B,/h~i灗$S-)^0ӷ[؀zN&(WپِѤ\P>e lcP,eꛍ)$zo1Lc ) 14L h~m[-Kx BS$6`cS+q36z*w)ڙzS$/Ut$Ǎ ?ŪT`?)VߥSS$ >X6A8f jbQH7FieGNj%)Pto7D.`PC0J 6ADۍRPD O'* a:DW&J RgvD,Kζ [FgXEپ݄1у'$Fhl 9|"mx Q qaN#~㊮x#ovQᗚX~gC$C ̀jQHپ$ОC m-Ɖ 3M CCa}Y ]IDCct}GyG=/*fʍlgn#  0gfcvSJX@$J'fMPg2hjo15֤0ILR€ oL7hԡѷʺ"Jg#L.oFJ$lmbppj?cGDR! 4`~ذ 3$b*)'LaS+e1r#魧W-~ ^/{~姊i}R՘ԥjKپHH=3Zl?7Y.%ն XQdo6"xQ ᧪Jw4%oXEMU&&|o6oXDD L. b lc@'zYCl@tF;F)?@0 T$=p ~]pvFE- Tt(8tDFȈM(*j&`PB J4 yyIog59d?7Y>Pˮ l_bi g?I ?w[ E~F1 qo3[f(`ぱ "|n0/9CB '8r&yZTSr& U<(hig6eM\WiCD8?w5Vd,hghX[=g%z}"l34alߵQ!xAp}gMA!, 8;h<` gؘ $}hb g#,l_@yl$'Ȩ J o9[ -$g Q < T$E1'٭+R~_r<ۥx?7,$T!Kq2O5geMe)>Nn\ w\8۷ ɋh>P@4g"Cα#de6:}1蠩m/Hj lqcR^EM1ѶAtPپXQG촞+7M9Cb(A}ˑ zjX{WK:Сm~rHH#پMX4 DF0?DCƄ݀rdM5  L: T:DE'0I0gF: i>qT4Dqo2!ɀ)_dd0ÏIR>IL+vR~!fw5ӑ)ww$gd훌D-Пvپ= &Y6=6. HsUsHm+D\3X"ͥ|r% +ȕXr w? [kz_z?wVe5f1(slߴ@AdLS$׏>G^%ۉwѱDSRپ2[v'I^S%ϔ۟a!I,0$ŏY=c9^֮D۳njpqgX☢ W뇂` AR˭"d6f玝:+Z  *^#kr$gA3G:IrvQ񼣗7EU㩮8p> 3CKSt:.יw\AvxRUP4G? 3]Σ]0)yv_ES$,S𦠗zqtyqZnb̦^j9-ohi=5- I5A #?} :][X%;r4fP|}񲜦HP`Dba,XZef %)27Y7GbXdwʅhO^:hu rfW2DIonzEX`پkhPCGPhp38p}!sh }d"}bT6FroZݔ٤@jV>#` /m&%VQDg.CGI}vD'j(leޘ(MgGl_A6}6Ef80Q@Q$8,GW^R4ų}+Io,Y^;}\yM ;97l٥I #! ɑLtUKLryAoG8QH;td.4'F]=fC ãhL#&=G%"F gv*գٛc0n:ٸA@7Gkgo8,'GuK#jq5Y|#(7!&X inٯ' *ڹ m(T.1!%^Է>3Mnpk)xu/,em %rA={9, jV3`mCtX H?V~D#T0JJ>l췂7(*kV)VTnk~z U:o*;i'jL4AHhM_޻]&/0q1[ \ A0 [dF)PNӇuDb4L*JjJ5PQ\'xn; Ē%_^iadQ B%9XsTͶ7UeZe$BIŪ7y.^C9j7& #" ݟ.ɚ iT`U:CWAl,p5^}V)(>1kXˮe[ic[(-] (x(6Q:ȎoX/qژfGy4F\ԟ\F N!'jNWH_(Ҹԃ0:kz09KOIr GY+ԊF Jk/j-m*LcD.k9%FnVΩ@s kV%i\VB.ha+a&9zH`ar.37wq9oT̿y>F尚LI}Ko/Ʒ|Jjϖ],Д'!6xn WiW 3 nJ 1bx"re1̥o][@aT̗]v/$欽/9mՊPtz@5>q99l.r6h\:22f^r}ϕC 8ʨ_]ÇMBBdj>"D\Qhuc';"^G?:Z~kA(&.ZTJ};B0ԉVb68T$ S;,!MlV0"1Mi !Z`|'˫3>+ "؁r`1L)5ܬ*ҍ!nP j #HV 271jg,?_Ơ ԍ4)9)ACc(#OcA}8H7x *օhiFYg[p̞˅bVqH(cm Ůtpdz([28J,T߀"ēHfP':  pGN(g:-5L b: l#:˧r{\/LI "@ iaB:RFĨX %o^YD0Bhb - Q||&+ R9bX VAg=Bp\W@c𧊾B> z@FClbt>tr<'j4)7eٹD(91oZ&C QjcP< h+w5MR - 8shJgJQ )c],%0 ;A@;M*>L3?Š5ӵ~6:(}pb0-~5}b^u! .ZhZ>%Pe(6ݟ(|n1 pLǹ%#bwVth"&E!NITT 𜒠wMGPj V6w%;VH-܀t5UlfͲ"UP%AxƤ Yrb&?@W#7]iwڧx  sz/h":8tχ<Jx*Sc㕲y#?cjQV2\hUCQ&,3:A:ъE= cF oannØ) -s3ŖP;lFla Rɤ1d& .-;Gڷb ~vkYSRӘD5&ri/;LvLc>h):8œJh!ڃ )QAUdD)e$\܄Zigia2,]nN pw1$ W! I \2*~^^(N/+BҺEʎ@7?A|CP."M㍘ߺ^ʐnV*]#Zi }| m\FVU lpouӫũ[*MtdA 9J"cz3ΗY[FJ2 N͢CjeHE:\%5f  `\οn Q3Yt?2+|9FSwcܿ@M ܋+=ko8&g@ƕ< Z.uN<dP'㒢i,j -1)DBCz,\[(6qk0oo.cRNS5jn/yʍH,ThM3UxO\gcPD$ w&4I,5݂U6rz,꭬W1 ˜Nwx j@et䩨zLcı 7cd] WrKĘ͂\"ٽd}.ViتbNVKVк"OqYUKySO*vztbU!Wݙ8|qC7X mEk0f*rqP\$+w2j J= 2dR~QOI`EuJd{ǩޡ17rQ ˂)5a4=9I Bj7zc8u YCφP> M-FtHh'کjZ1O,W1F#-.!^J~a''z3b)U&(ta9(V|+?Ҷ`rE&oI_Bl["S-$>Aq3)@``bJ )*6}1FWC6[š~h~#ٍi^U!U4j}3ol%ʡzB_vCg2;Wh(]lLʜ)V/"%F-4PyC<'8rjH ^Jy̦Y=0SzKD!R_zAuXBZ!FB_z@4!9z젡b$ao XBIe$0?K/$  />AD!,qQ,>Nx2l񆘂)`שc=;3 ~`Fgv*h edB/f1F6(`xa@SMJ^ I`0 J H9. %3ǬN0!Jfp_5a lNa5IA`ʜ ".h` A"KhD.@Ίʜ|A etQ+'7~Zˡ LJr]PB#PC'+=B# b<鸦虢ׁLG2 3=,*U)^Zp!d:B_z #Pe< elCVU)CUeI2Lo2 d%P 8(0PuUO3f<⸎e `&G'#5%g*#C3f3STCq_҄F9(!BcTȗp}RT!H$Ԭh4y%zaWzYJ& $+Rs?&O;X...^(v;Y0"0O?b{j}4Nx8=t<,ijЫPۭd y2tO}&̩x'*dw @n7ܗwt gJ䪹قSʝ-8Wf s!)\\U˂+Y7jw)4֭])m~r~ПM&IHx!Bu$H)ťT*y%fd<.-Zx͚"HrV1fEJ1kJW"H$˰^$={ }/VObH̊BJG)$D"H$kԣ=qP AXGĊ2JcXSF^D"}e{#>T@k=%VXSW"H$D"H&^q'RػJa/,>ݞ+XaEi֔Jry\ t)2%X2k5Q?93۔6{gߩ9mSμ5 0"PAGfX fKrjvfplmP+DM=U8vj$oSy4WL!)/[54UjL̪s)h -J !Zż~1swjVu)o᷒9$S0{.̤'VQT")~`_\Y=3œiڍKE0*뙂j Mj`h'vbɓ*9KaNUJ})"fiEa_Lќ"1QD8yJ2WUS?;J`_ UPU? 7~$1 ')K$5S>׍!ur'd6;SJfQ ILDוL(_y1PTE1 g1;tT)4̢z9Rv)DOM1stQ8v`E}' ,/3(KSIV0UA`VS; A4`bBh'p, O2fTF)IJ JMT̜~ޙSsJYR\ǰW h~*yQJUa!~j"ScǴ#}`O1Д8Kئ$H SJ!w *X*Pk)̘S0F~|6}8 $I9e\/whf2O]l&1hTIMѓLSIU@r̝Y:0ݘJe60Q4s`>ThJ6'N&kFN*cCi~YQ ?&b#xЌIoI(/D[%ws$ϼGs i ӿ"<O0p%S)i+s ]IPV&*4[JR)F\.Cf\.!Jͥhl2--6{zh]3 d3sL8p5m,)yCMYI6ݜ龲IO,־/,mR뼎F6SM:%vr>( K&YT1jp/n[՘7 [OfW{kVl5 z< fNI'/P'v;j&t:(IGA&>[!Iҫ՘0ɒ%KMMZQ'v[SWt2AQ1 VCO-%OYPΚhMZZ IP͝`o;͂91AQ#{hyhaۛ(2+-jcup'1I] MJd j4ﴇew^Ež=-BET6#oS tf^PTTTTTtb$Z<$Fvd(ܬKh̆q3zzh:AeRXDY e $`  h5o}[C]J}~e2-%4?H+htp3YZ^TU <\UUVh+]O-3Kh{!?^=l]-EucU#[BVXP(ЍlDǩؼ/x ^/~ z}isJ uPE߯?bA؛gQ Ž.N@!/?G PFuFu#VgP3ې"y;쨃9`<2&6jVJ v"q/;I$!6o ?&n U~e K`K0"|*KA;k+` ?hQG[@ V LE&'QcFp6vM`Xk7 mG^tZdEYjnc[,5XX)^N/VZ?Ȋ*Rk_PZ/6òp4(Đ`5OE{] ҲBovZ<0 L`&?[a./wum?1[ͧ=K@#^@wj7tk_IPD`<1Yjn{ⰬVn2X`R1OMZ{k}ZWj7+u7j)M3 f\?ƅ~hpGAf0ò~8=,o{$\ך [kˀ#NF(v{2CN}21p:8f6SA(V;aC#b ?ڡ9mK\m6?ڒC^cqLF  E(( w֪KW/֬嶮jhZV6a|6hČ9CĘh:J-ȴ 'ZSpk[RzVIL 1-NQzYz(AnnJ7kHFܤ'ݠݜ7fߔ_M!N6aL/ MJ7s"݈߀}on WZٴVں8"N[ZeA/![;`žBG5? D/e} ~?ʭBn6(1+CCzjemam.W ?hG=ݛW;{z:C]T"Vwnn1Tn ;, ;7@2T: g fu[Q[7E53T1iZgbW0ike*yZ -eѴC:JO FoXoZ/)2Zl]㩡6 +[ T7 >2E5]AuxXV,ܕb4+ffd0Up9Yh%%Ǹj8ɩf^odN~)/ &К_X)6|SzG)V+]mEC1=#Ù45=LPA't$IN$%p@#HE@ AkMmAA8藮r9cTdDkoJGC6anJ#B6rdDH̉4!82zZ3KoŠ Z.hjAA/ }ozq-s/i HBX$ dTɏ"0GpESqIcG_M3AæA2x􋊊b 1ѥU?K?69%G&BFi2nĈ(0BUJ7fA/\ U)7fw1\ {k~iMxJ[Go~h_ȋ}h(2j*i9Sbfm* 4Z=WnL%F1u=1# v?o)H4^A9yT@,߭ͅ.4b @F1!w330 ɑ0¤ "Pn8F" ~$ P0@瘚5O~ VɑLsNSXoNJ6S'Q;!6;G)l`f>$4x;L0AZcf'“D}˗xɕsK:QFޏ֜S>|"q32~s0s3e PJp赗aON|UB?<=}>$\:ZdT7ڙN+/8f{uJڼ`W, t'՗\ŮvUeNwetnB *z:1v:ߦ-Z%daاLt4؆zfodAAX&qbC/f'I!NmxrT/@ȋ,O|qzCxL$Y@~|v[ !3Kndi}E(Fx\>w|>a~>+-gAU0!Ir$g fV9qz,7#y@ :mPTn(juSl4ΖQSRxQTỶ  ]@P1c:*3`=BJ'n*<o46+8q@իmg vr]`% [@V!Ŋ)܊Yjq6Ktk<@a"e/])$߈Ek`McVͷ?w} ;ڑs.ѣ9|d9'2Bhȗ) GID\ `+\.tRs=6uB  ŵvqrCMQc8I`A8=o y;w \Ho/}I\-rV,gFlI+{c+p (P2iedU/aBGSgl U>hC.C֋4_62߾Qnwt+d3[}iBi4_CM (nրZzʮ5&kMô69k$e#_,Q&X4Zy[3qmt{edj2pa9ӣ4|7)K,/KROeL}vZ؉0! L *TLsl;mVN0)d;E0y(#*y-HN[֧&Ϳu0(׽ղG >Ѩ-\ RG[M̌Fx=\ )MYg(SA764N-%@2E!/ࡤo0~@[M}&#Z1qFQj[:5MsF3K_?<@B-& u@\1Z>zk&XD WsR29'$nBa2{(կg۲L!KN:3tյig.9]tbl|pmɳuMgVu.0yQCU =b[GS;1 O[%F&OM4P1:cדhoE ˧I_Ƿ'3_vEBS ʿ52??VM աP"ţ ij@- ;O_naS+Xkt:a &仞4x9MAG=S9q}VIaZts˒LD xA`6(9el.d0+LWs)4WPB N-E;P6l04`N' e1t˿džs-Nk}Ie1,JnbbU%ߘ RRABIG&(}a3E@S|?@1J=rӗ͂T8[M#S=Xalw=1ܥ /8R8yՄ7XQv@-IFANT"\6o3&>̢c/DYe%kLS6q\UX>UȰxV&,4KϳݜxE?ZcRH;* F1onB3@8s]6WVkyε x(f`ځI Ks͵ބ\x╵"88@oS o"%͗*:Srr*J N|2"qJU?|*Z~ ]oXtb~:эr6]hh |ō<+2 zX`f[ ͻ,S]om$F'(Y/b* n•36 ^bd %_ݙնisFJ~=(#f)dM(F:Ad<+++VsVlx>1mh;Gh4x)L6T+Pbp@CYqg&FQ' ZMn\t[Ŗiz@[Xy[ w !`IbDR<uxxdw:FzKaͮ0C|Aճ+f%}E`蒚,͊]!7O;ڤNVX4dSW> 6 Gy}<*A:̓XE!ylD#X)T,oGmz.<eY3.+( 5)omL]Q 2!:]TNT4M'& \^xWq 6t<ۼ$} l`1? d(sp$ƕ&ub+!na8ā ui2SW1GE2O46*VmD)^7]6.q#g#Md`KzՖoc`6HX Û::[pa>1(`UPDu I#'^=Goǫ̽#Iƌ3b6rN2~/Y Pm3` 8wE1fS24FhXDbTRD-)e$ə euYoʀ8895*/[ vHaȁ CT0.ݶ ARtęղ;.!ǭ.}˺cs/bX$EXB[AYcqΏ\G9ZS8w;Rs:k3En4l+]ڞnH,'qN 9S{SLfyD \y!c~?@WS!_OTA vPj|Ј^G㹁x}Y1INTsvfP1I3_|p9iO,ꤍ I .Q$TV<\ eִUd#Fil(/H0,0#Fil(/H} X},#Fil(/H0Xa ,gq#Fil(/H0l)fP #Fil(/H} k#Fil(/H0kЁMV A,#Fil(/H04"#Fil(/H} u #Fil(/H0uЁD@a)#Fil(/H0Rl)#Fil(/H} :>AX#Fil(/H0:9X: @a,#Fil(/H} x_,#Fil(/H0xP,"6X#Fil(/H0a X;  #Fil(/H0N , @ PQ#Fil(/H0fVS= #Fil(/H0r!}g@a`#Fil(/H0xaxqPL,#FT49He`l|HI}ziondoc.datnzBJbd/41z0ǾBg{d/51z"\Bkkd/61z eBR̠d/71{TBd/81&{#lBWd/91Q{  B$- d/a1y{] 8&]8r d؀mlc I$+ޝ{:wUW ;ݓzvv=OiwU_UU*B[="mCnH@N?[3L8 @u}.WM%[fe{Wgs-+iplڛ!+7!7;Bn"nIdy9U$pO=.ҺNZ7ɕMRAZ7}ܭn}8 8} B))`!PaZ;@ ܉aIA`dSm@/Ў!{Ī_*q)4Z.n*QUy[]2}n/M.tF :3%\$t eߜnƊ3-by`ga{8 B* I(B)prJlG{~0]b.4s PA&oI 4yU\li(K(TyM~jߓKf_þ.&%AlR4Lf4s93Y,ViY_с 0 !Vy49p9YBzQӈ8ϯ688888\gl~E*M>/p6ˢ>B25iYCL `7O ^*GzUTY£Hawd$U.uI(& 2/3{$_u#jZ춿b" u/; ඐM#97$I1i̖JB资"p?f0 `Z'RsXE]{<7:>0|qlpAE IڼhGkV'qBdvsG˶9X<1^²G dDYmD>O^IJ S4 e)-xjs%*ʻl X+l\^jd[iw[=$I"|~Cy~!_6-~8plyd>A(6gTފG%uSK {Qz؈,I dt,3҂dνpP?qu%s *3˓)w:s˒p!IyB$ v=D=  \2oh R[OM'([$p{@-3 p"$r_l$!>Ue4%LdeLKѴ,K*d,$rc(6 +r{%^\].%QH;'#Q.fu>src"uk&JãY e"wk: @dS&p(m] @sρ&TYư,<̞>eaJq/*U @,I$:U%zY %!S%V^@|T%NKWMHodXes)RAk8oٍaԻe}*v+肒{E*AK!(¡M-fVU29dL0-ҿETŹ,3ʏts %TYbPV~7w8W&ʮvεnZ<2oRL C|G)AEƯ{Uxg*cO /8KyȒD0+mk}{ëP$ J~Ex6Ǥ&}n+cF3Y_d>-HP]bʄU ӴI帥~BO<=Lɸ⸵jX'&h_9šI: 2ώ{;xlǎ/;Έ]X|y8}][V(1@rI`(LuuG.Ʋ9~X-^;Dݗ'+lknG40->0'&M6(&L"{[ bڼ ץ:7okS$%<~~%@s?el,IBl fNlrobto>Cٴhp˼GoJ_"4 vtw7L))ovd` H&:N ۢ|GÀwOQ$I=ɋ[x ҲnEឮ.|pI#g.TvX^lp@0.%Z[0g2G*t):A"aX'g.X+s:uf 5I?Ds7.`l>z^z/͌x QLrAG,oǧ969f,DxUMh.Fp @_|.]VxvdtӲ@W#$ "cZOq(?}A uGg *dXӅ4LMicw.pžO%SeՋ[}wS,yzds>M~?:\A.D=W![4X1隷gG}Y7M^4`V6WnI#KX rW|.I iמ繍ZjI([Luu>d,_Vu ;D.,INKpAPZ\õVTu^{QT{iX/ML.Yw6zYKþV8VQ/˷#GI/MxQI.^Gfٓ3|c0[Q߲2`p&exdrnW,?XnQY%ZZt,뛭L 2a`ZLL!TIb[̖tNV&>px7ۣa _,JjY-,dQezyƝ4Q,K1PkU,o:`:@Nי+k2$뀫 fp0zs9dYb(LӤ7m~ZkZKE6-Zqp s9ygu|Ķ gOOk y/9t{y ;+//e2[-o9ѣPR8) &ydI,GM>ooɵz`+<KqD./ f2A[#}"I<6UdXRuƀ:9Χrjxda%Y 8N`X )̣a!_?f|x6ʑ/4{:| ϧ42#,a/ [ Il!S( 0p?#D:VKP,^T^ow4GoFfJsvo\\{=E扉x0fLtՇȭ{9E3/ܦ *&z~g K熝u_ 2I2%Q:׆".w{x]lncCkEՎ&S2 詍.ͯ"ӹ"c\8KW#GϜi|>Eٴx+LxfH^珎Oqbϥrn޶Lf hG{&KpKixAʕ_f%mCodaPN`5V(aWy]߿VKN d2y6n[ϝ 9ư+Y<3É.ERHoj:eO%n+[<6U |Vë?(^ROdD5j+iHYb`JdpnY#>E§x2 *;.-1-tۮm~ώ4e}Ȳ~KllHwoãM0 zWhE[(-tB7-& ;R~"SNck Ј]e(lLpCl%) a(*~El! U8X' %N2-hXۼk yͻq YJwG(LdΗiΤK I]}Z2#—^fZKKh%.MKZ2,ݴ.iAV!66D<&%,V=};;8\m=mU1M ݲZ*HW vcnޅiY<:0Db8Kl+/e};CP tFs SHJdNN996P*k7-T๙E_*l]=|ntQ O_!od3O |.]=/p9yLNT+Gg.u?4_R@W]M&sz܄д* TbOKLIX6]xUqP2 4àT6(C es %?ݶA!\0g| c2>{oI| n,\*,Rg`^@lP֋hp/_-MAB|0.QJ%(xS2 RF n>|d/orshE_x.9T>v]"׋aYmwFU!@7 $*Rznvϝo \@"KU.ן<*u՚H{!hû$IZPt\~ M5;^`*<û@y0U4@s '_wno0MiVe~ȲBp)2Mu WЄtaW'~NNʋxi2́$JUŲmyMx^t3^ņ4bX&q{o֋i Id5i`K/4MFRY&2yCwm̅FDs &ëtoUv57,mYN oq*>M 4M(L8@xǵ!8WUx;D7ʼ!CGpsl+i H.d4$!K2"vU|Fo=ɇƮ&B]4|k ʦsT5&$1 --f_҉ipXY:K.ן\zXןf+2ph]r2GxDc3ȒDc۹w;I.)e $[,2N,N\'/ lk *V[ [2$ܸ;6a21+r|t1WXLmz.EEUdPmMloi;=ϋ瓌_V޴]-רtՇhG4 ['1aujB4r/Rn ҈F ( 0ނ*x̃HZFW+Jckg*x Qr, ן,\DzDtן<*McX1@1M ן\j߯F1:o7$b9}MAo^%\J\[E ~|"#~0WK75Ϳ{c߭)'gk1V\UEf_[k籀DՋ^`^@D<H_r|C \T~7<64m1oߝ y/v! mz"/%l9z@SgWl2r@[DiF܃6O3so)4B"pt!,Mbļ_-ήakD<ANw#]5Q9uBjhׂ2„=RRe# .BE1b x؟µBX5/}qf6A b^eh?7Y?OIbq_3{.:}|xrv~]uYL ˒rQ󱯵tȉ %=p2[d40?zn7nm_e'0$>dXdh,}ZcЂ$I4;n| FffyahxLjX5 IDAT7!LeQ}LEd?$¤;z`󻀟'{Vj|h auD< drtm{4" wVv{7n+X 6SY@m J`\s~z?{Ef%TE)mo=q7D z{a0J1Q*)Ss| ]ܻvwUU">B&XK]9M']891PGe<+)p\kWc#;= '1>rdu}xahô:U&ò٥G6`&e3(D< "=@4!\("֡vpݟ9?D<+ #D T,>(I !WZtlSy(ELjGnE77%U,2NSt2yM'q,{!u{s߅K U23^v|0$ZV!WR|ʈyEsG̟6߻wI#Ce ?a"IoFJc}_D5>ĵF_3nYY|6"pJcyjf(raD rJD>0j㸖Q;9ij2x&&"~*gNx/՟7!+#z+"rJ2|v{|WR"[/19=Ac8̛o[z{7V&x96"j]LKHdsuQtȕJ<78pLQ"nC$A¸dǽo p*oFlNc9:4ə'f_ nYt܋iqVu^D4"T6+ן,'?K "*|,dҀ0a^~/ӈ/+E+O/"j P~?YZnD<4$B|?߭;?iWn#"꽒O+|VB"kFJϢ ]28!=X$ s$ⱗܯV< |c[K߫lЍ0#޲gI?W(N领"ٹn;B[}W«e2t1-BE讯&<$坷ƍ۷>I~pnSeo7o޳ d bbk+Vy^ZM RQU?a"[ekm Yw1 R uٶfăXVbj-"j'TWm9T>oZe1ew9'W:P_Ǿ-w!ȇ,\]3ZWmx+ÇV- ~`*˼e_ ô8:<:d=>{kM8Ʊ,O7 KSZ"$ EQzCꡋrKQA8,:esD>pݺﭗZ+I n>Jx\.voX evӹ]&ZCTLvo/'?R%u)xOm:Tځ<}ۺxmrMMs3 ͡X4д,\J]P$xDeMW(uzQZCk"F@vo[08֊ "~|wSGh蓈^Ҏ~ }- yHz-Ykk^I#{vXxG [!QٽY3jD1>p) u&nٹmm^nRuLM3`Z>[n8(1MtBHcC)LR6 ,BUv܉_"[81>nt֭R1 s<7pl0H/QL%g"~P !ˡ VD' ^xwzQ5+J @}WeF-xT \`3on p*ȒD0f,TS<􂨧R" ܺ{'::P% $ wyFɖ4NL3/ Iwv[8~\bu0`Yޘ 6 Yry\eQE($ P'~qb|BYa!X41-Kd,:'yqyƕrO$d TYƴF_ցmD<WbwR { q:zp,nˬJuWc^Xq}d"X MDH1ɐ.jha2ri297O:UE!`mMU݄|>|+- <6FAtgKHcN39=E.g65G>_ RY)1 \\:Ml Iu0Ķu]r` Gʉ̤R$gii^W4d E.30:CΒ/1iVWUQe"ri zxat *nm9}g= T=B!XBռ:[K][JvR?_T{V{wT[lD*UTr c;eOctaa01ϓ4tD7 %Z^&2r "uuR׻4,\@>'We(1egv<`0H^ǞB(w"Ry4~ IczW*2sacm)uZ FuJ/RY OV9f(ixX3jy q3\U6+r=Bw$Lwdn%R"rò( &s9rFP S(pvl +TE] @[}= .Ev imYXػk +áejzCL0ΐDd߇B-bHP c&.Uv +Z0~?;{{ ?uP:_`2}B0(JRLΥɕJ 1H>vRdYƧyy%LN`5w);?{J.ϕl/ R+t|Tr] @r-AբeD:zPk #KńKQh i ,*eu z00-Bp:cJu¬Ƙ0Cۅz ֻm`޽/RUZZ ɓ. ¡ AA:#ˡ< B޹Hr.:;}v]0Gi%rüT9% E˶p`E%Qt RNW\a-?FX^;zn Nw"3;Zbw` @jA~[APfr/RY)PUB!Z+3h\n47`MT$Mh&Ju3).)(ˤ#صU]}>|^Z9q Ô4Yҙ,-[$Iwg':HDǏ395iY M-ƺL5`WC $EQ6V΢*8/$ⱗow P.;QJMT^ jX[*yj|:Y*[VRZȵ`cnUSk`8kZSWϽ$[Էucr?{s32iM$n77Og{8I6i\\쪩( 7<׻Wss W.oiov\rjhxnj<[$_B|[+VwدrǾs\;17Yյx~`rҕh&gN{'8y9{9Sn[";b&DJ Q9C'Xc^VhU+j#J= (ˌerG⢠o>8;vuUvHe`. x3?2/7p;{׬ZK" Pl~ <54Cgϒ*q)2~x|^^xcӵbՉKU쨙܅ȲFUOsi}Ϗ7?ϰL%i$V{9V7Ҩ!Gx z =`{_;ìjp9Tݎ7X 7! Uv9i}0xRZ<@@%.T9gn.݉xlEs ]f,gLqcNOMȹ$/MP4=쎆 =m͸M9srm$ UmF&XEa{[3ۭVvFy]K<=<\[uuaXEqGjJc 9|ʺ LD<H5H" >X-+'؛W8?lL"{/ x+oֽKY{@VgNR>Pn; "jPFX.K $0nZVlH}K$"p쌆hy$8=Ʋ S ;HQtGDj鎖e-ւs癴4D"92R6L2FfWcWtt cL{Qw[p Z G ,xLGhM6 uƫiMohB`T!@,|I,V]Al"ULڀ!Vf-~ aքOy|Ya7NF%BYm\}0Ȧb=~s w;b[,Ez'+~QnDPAij4[Y,^3_2qA+e|!+e /7'LbfBl!_OaPD%w4 0ۍ#\$qgdC|؎eaclbam_\ %S"Q$C~j$U.񓜞A[zֹùtDc2@Qb=o|=kWVQX,3TȦ7s[o) .oB?dKshϡ,؅om%zǩ:z\>*}XIY?'`WuV.6 eoTO!smzѱPIn ?w'.a] a_DU x/BwsY04x׃_3i\"5ƭTe2%\̭".8s'&^oF#z?__ڲOq%⺦?:*GlP{<#olR; |:e!ME$b45k̦YoaЧ Uj/9gq)2m!#4m_HVU×$h8DC8i9w\~բ.ӲgxdIh_KiiO4_$J@w$Ȟ0GGyh'օdk:Z_2xPWEe<զU*S^GiC0>^ CP9G(뺥?+Mj/( |a pOwA2r;{g˻lp(Oxqtl!ӂLInnו nJ{C.ɍOZ6jQ(K^0 B4GX.>GeʆLJ+$Afg4LoC"3S(X"߿0dn}b1jO"^ ®eDw@ 6k|)ԂWE֚.TDX}~?ՀV_iSlυe痁)ZC _Bظ5` 3*lE$[Yk!Y}c466 7,n6_a ha$Gj{]KKoC*W,;/; IDAT&$y4wAeW}ݷZY[wUS=GaX`iLȖ@"Á7`D ۄ!@Xm ml%BF{++{ʪ;k{t/2[nU9sE^pخjg{xi4€ eJ)%WkNd߼r\`,rvaZsva:;vG$Q|Z33təAA2ߋΌ#j;E}"6U:9  ;\c/b1ۖؾ~}389|-}Wdc,"G\z d|R8~eW,\S'g}'R$ɽSp^fk:nA_ͮQ:9iGܖ&u=%v8ZA+6n 7{CzB} "?/#C*OngwEAζ@6G!k}9cZk$WsON1^wfD0QZQc&T-.^iJ; 3ksXZ^QJJq{OZ,"l݇ǹ::M"/a)] ?Ϗ؏h\rgssnx2VojrOyrpTyަ{BDE"P {CSO^3V{z._s6 (TMnڼcb2]8WW[\]mIRt* k/ ,i6? jEHa)I`=)_ } n6O1VPk6A)J{h0(űrbvz7f5hG]_I!2Y\1[RR>G+)QVJa5YnGܨ6d|;mX,Nsb`_~; ߋ | }& !ӥ&OUYhvȇO/0Rٖ(HvB;I8DA@@kcp]JjњܒO[YDv8"|Jh;x2pXcN6Z3ߌXlEe|ZHvBJcbX, oE`ʿ)ژv51 9Wh@jLpQ<%NRfWVYmh$$ns.(>QqL+N$ QQ *Ah.9yJ)(B'1ax.$p]1ӄvDuum hIr'8J.yϥ<<1BV. 6nK]caHJR>$IJ\ZiIR5di֚,rwVG6!~t"{̌*QkrDk(vU4A".ӥJS^Zd^!8Xm))քLo ֹ#to`fZۿ&]ʎ{twrG{n̍f/:qtrNՅeFBG&GV7$KHrWk-囌R~䂬tsE~PwDR FַcF#@Qir@|_svKI|M-HzRvR>yRT/stqZKR%)DDb%Sv^b|r7;=%4bMLф"̰(357wOFg!MfBסυxw\MJ1Y"M'MYjiq^gs,|"0('2'D%Hi( vcFXH6&b&^p@fH8ylRCdmV"03G͂oE ; ǡR`S餚NPƴTf)|1o _Gf{!@ʮ|${<2R {E/_F gR}[D'DgbX,}5&DfY ɧ$Y$2qqP[qblunĽU̶zbż@Oxg& a6Yӎ9fmwNZ,m~:R1.2[vvlE3Htns t]^I#N@wqp6gדl$5bX,w >^=i@dGU_Pqsgӯ_{ҽ$u&'X\ҍ)25E!kBwFf8(8Z(E0 {60x+{Onj=3f VObXIxG\[32f& X1ggað8 >N!N5pdz$MtԚM$H2Gr!~1T,PꬬPJ 輨_Uo [mc3xZwNu'} 8X|!`b$++L[`4{u/ {Qu<C$rHT3qm3H@q .Zk4x13d ;T]YD7Z-3lLw'(!'{=GnCO_sqE>|F'e92uK+Uю~בcH6.RB!2Qc#>.y &/&{͠t&1loLde @(Z,b$=x#͌v8Jy"[`v4Db+'|X,{a.3Y*)Jap[1H<־֤ٺ@jQj;ҎcDໝ:J9^6swDIBVF[X%D.O?"_rɇ!R*&yG@j$̟}.vQ |ʹ|H1P@B..2mbX,o:HGoo1tSM7IӔDuЌ4.n8 qpr$PowhD1Qh p InI<+O#KvR4B1^S }:qBщc*Mm Q%)v8dO|BS  ߯p3we?ݾ#њb0&b , &,=辡OK6wL^3 m Be콜a3$a>j2;`wg_GH( `!Jd,VZ+-,5`5,ߑ0T`4F]oe?(h٧;DN KݔFj_SSْARŲܐ/Bf*ҴyoS2|K=]s\Tt1=A!G GAUT|Qr*L  K]#~_GbX,Cg@oL.1ƚF"rpH먾=)۬\-N5, }epH/B1%Sw6+~"}YbX5&+w <\:޼Xk$&$Z {1A L,T;3"PIDAT R"e1!ph֏GwM Hz%@^&041y#є/ [,=~D@Уp(ߧ Ht$kx6̨!k_d#!𐥙YB8l{ ̬ຊz(0v"#ׯYZI[X,ݎ| 'd}Ĉ~O!l]"K($|Lb 12ۼ_D"΁ $>8d#!X,eOx͛tXX,bX,bX,bX,e=IENDB`structlog-24.4.0/docs/_static/structlog_logo_horizontal.svg0000644000000000000000000006660014645734712021220 0ustar00structlog-24.4.0/docs/_static/structlog_logo_small.png0000644000000000000000000005423214645734712020122 0ustar00PNG  IHDR;= pHYsodXLIDATx1 07ڣE 3>%H %H %RI哔3gv<4w*qN`;ضm۶m۶Զo;s}|}_SJqHվr##+}rzz}cx Jkaqo`<$4PKI+&xEiA:!= Ԝr QF#h otvIDʯژ8k+3}G0RH U8d "LNI #-R*M*B DK3bAA8VnS* fFaa%eet;)㯵T''KgO9 $QC/@oN(~$aTQ"B| F.@[qAGq@]Ej k"azWΞvrtHJNol\][M"ʻuph(++ؑ#xZ C=]My2ғ=؅ K/D*rV{R@|/P'%2$1PJh_陙>53vSiisIi֗CC999ViiNz:8ᆬw9,vzz7<84\QYeafvZMGDGQh" 4,V2T$ Q5)#< "OZrx ?viSoo~a:0nAFƪZV^>99ᑑ\k++Y,"c;,@ CNm:0-͕˗^x]CCBb݅S'd;ď4Zh+wɍ<1\`H<{7?)OKK/s',ز?R$3w!!@pBn]?W\?Y-&a9k{xVuuQ[. ȘJ2 Z$n*R|ٲ{k_:򩧞y<MD Vj`B!t86Fk V i"mzm-K\kcEm~Jbk,4r3_^o߶lZ..IeQ@~JSk_Z@yͦM&kmb& R6\hCq˨%ھe8gV{T3`]Q0Ք(OI8V[YJBph'~1GU94)is)IeZi"DT̻;CD\:4@$ ɤ*B B8|є&Яw[G+0 g̅2@w6Y|泟-xD%I".R%K$IdTBnݼ9CfP!UcąJ(69"9.?1w{->z ..гJhΈD-YWY5WIvy<ݓO>ß .翫f1`BG<&&k͌dEؘgp4*"O%7 ,XM[hjŸ|n]lxO=՘M IwJ욇7-)6ѧ+.hڈZ*t{mKCK;]$ܰDȪLe*H1#aof&@}b^Q.ԪƸTǸ,TdAӱrځ`^m5ߴs'4ms>BG,+S X(#aMZ&ŭ[Z"*ѡfxvկu _u&Xm.-!(qDvJYU(%?^0~*jDav~k-ȣcDҨ!4G`pH(s\f<4a5H+(։8E\?}Wl6Aqt k54Y#2O(eUKimVJ+D‘'#b6/!CFbB YYh2ЏDeCP "/P©E-*[u¼t.t$*pVhJŧ?Lr-40x(4J,,fvY_*F9oJ+iS\qx D'kBaLm "5cDYrNM ^tIRCdǂ&Ic5n ԩӧ3+V̖R%3DthJK,d! w{Pb]u~~ \f2hݮah`_NcMDJ,b<^jhQٳ'|;{̢TAG묈H#2F y[{9gf 5uR,P]Ta-d|۱YYlհ K(֪\…Ӑc#I8L8k֤=?ZKJ2תDB:1qɅveg.=2-+9.7{Y.3 JhgP "wȘKd(mQԠG-L}z䦸9Rj  %T; U5RZKlK$p"1 Q pI749ʧ~~R8]sEA~LyX":}Vr&,2ĝo)J 1Dr ʫ "Vo C ,/H.jFDHE[zCyc X 2paD9]TZjvWdM5TeX.ʫ<2@wefX\mlOJ{8$Ax獻 7ۭ~莧yi %6oCF-I, `&(-:M< 4N.lap\". nyԬ:S3#-\Ƒ.)DpI̯7ʘZZJ$t63k&#E6>^M߷y =s`<Ď(rPPg^UXGz9"'^py{TH7q[z=1YK,tcMo29LZ4"Y!174w$Kx߆/|4ƴM5"<36O^evG.XPżq:Ȑs8t5ה[,hdhRذK-@~.;RNý56>p_z4d<'G$uIFHF]"a$GsV7(+Gp9C Ls#5Φ9̘Gjjmk xSզA+y%X%a$˫LܖkO<õY]j.l'p@ǦhH"(WX.?wq\=kY`gӆD/7(: }C źS q/*t, #v`Եɦ< l/ E=:!RgʘV/~1]jVisVح#M¢0+ˍ2LDutTKk$bhĵ.-Ѧ%uӖ[+F%~YR7ggcCp"(A$~;l\^{yXA@'7JSU *?,HAFcFf.%#rG!*) jezEIUfTuucw9w>w?/8'ΞgiԂYҜ,";+'k;R9Y_~7VKi6w >ퟝ?LSiz_氙Vm4\D<{K(UbvL{:ʕ=H"NdAE_};ˇ;^{`ė٪cr)J #J>p-)bdW\9T{Kjvʎ/l} Jxsް~scQrUΒR/#'4xB^;N˅w:Yn=+"Q eDrQK(]1yC;nM(Q'uɵ6H8[aecپ)d\"NsHOVP ZR"Y&k(b{)"P}.PBwW6ҡ|衇kzd$!#Z ?i"rJ r)PXQ sr^ _ 4ԝtɖcՠb&˲dIfff'xq94233\f^O&-YvιASuWY|O&6>8H<8D|ҫUK!»OaBKˣq߇ j=kod{gN_݁Yݝs;o|quᙩ݁l/lZ:O!pȠ_M$YzAM7': 3m?Ic O={♴bїW/R+H_lg.FӪ jED!DsHC*<ڞ%bv=7Lp:OgU)!` Jǫu:S -b5Fj->w1/)1D~AgM >]6!gDYƮQ4ʬiA*tVpPwa.aFa^avYU U>Qxy}J )tk7u:fu 3ʔiP ݱQK-*Wo7!&`ePJ.txggYМ~AoS&1<4%A ONHR(wδ:塼VKZUHgo@&Pӟ ڂ,bVFy e4R9a=m|2c/żX3' kX̕ Nj- v"cőZ|,+ I JٲγbY'":I# b \;[')GeŜ΍f@cYЄ!h>Ӌ &N rV&ؙ`=ȕ_-\HbYǡ9SdQjqRBp(K1mOf9𣏎e+/YMF,`=FՅQeEtv|Ct* $.PBI:~gkqIY'zyYDstCix3˛7pecqw>=X7A`D 9AS9)P vQA=>#>B8~';qޜbɤ`αRrnOSixO>1E_F_8b(I# $&.JpeTʊl; 1Z8v_{sZyNi:2,V aB) ra1Wm|#G"8F$#h!h8z%Sڃ*PK"2ŮVqFUwhdP!,˗kJeb֋y ;+ Hzfʷzk<) OgML(njKZ\K[E !IXPJh> <'˹D~GȠR@qqYlep\Jd%&,Z rDNf y jԊ榖zjV8VDk_PvAi݂[+r{Ƞ-GcQefƒW6։a2n;Ey?E U}]#76UJZhPxUhAS+-jjD(BI c :JdlFlX xg2;ܑ¥.G#>pG%A(h3?\[z A)E[|QX_lik,mp<ۛτ+T|m.k JٸDYQ黬@=q~ _<(/b,y{H9 D}@I@5# h|DsSVBDqr MSzJ؄+=!I|]PJY&VV:s)BXwt\Rc(կE]_"Acg%AsPZ%A`V*H):7ԫ NljN NnچzA%좈&v|ݭS\R߮)Do @ЄC򂦖PFk$7ppkni5pO"pu 7 es .d; B6صV $ƜAկ}-s#v3fȿu ۅNS LRz4)9@IvJeٙi,fTm%x@3i``j̨g%PDvio(C;uZN 3|SN4+" f?+ l,|S80v܂#w,#a AMD?B&xX z-\&^kyH1%A9F8H 874r>2Pmc5O:V#=T[w,Jl1 W^t :(ͱa21Q?M۽=(B&'el,|izEsIkU!BKo9 AMwXɴ:üOЫFjr9p @Pʼj kA#e"kA yLbJHL)?Cӂ3N66\;UcftI2#qag`u2wdLgR*_d"] A,j5$B#(Ge BE.j@jZ`ɟZC})/HUCߝe2giO ^PwQ@VHj6564$ \roQ$D3ˆ*,|}fq;j`rJC%tl\C%2'ٻ 6.ǂc33Rl+c'.33333sefSfI׺&QttrDo߷{#F$1 ڎBj6ӔjW[;$_kZ<.^Sd~tl`͜tbɓ~򫯾<}&IQݽ-?;ZL\si4uB5ԟ9 l5`Z&|])с4Jf2jx`:y\y6ԠZnV[m G$8}ݠPQvz2HDE]7bjhD |]٭$d!.31A4(gciшƎKLs3X .I2rO>3 < cTe"CE z=&A@lXPpCc@y%x1᠛C'xi3kh\6^닒tʠ#9DVc׉>~ cp^,iXcPi,tCO?Lc@CF9H,m;op=:bA,"J8t:P^5Kr[2P > q;d&[?ƀrٲe])%?n y9&S.pH2sű%qTNě(4[  (M@åXl(άT|~ Ӊ!t";lpY g0a-Ӯ gw)=u xsϸb]#6>Wq@ 08 2m ۵a0{֖\E~HP^w^w]~n$6 {_ꄘX84j:eP'j&wnR)c,_q`7k 6X_[Aqw4K/5O?L1@S w{\I W u%j9rd"*hJ,eN d8d Cp:r)m}y#Aȿe\j=* .@8D[Vnyzoq Á@zTgۗ'nt$FoL6#fM~Ff뭇i G< ȳFnXs5ΘY+V@d?C?& yA" Ew N URy-5Z>MأK/7MA،t"!KNZ?.(p\mt3j&H<kJF͇ڂ,kp N@u2IIZSs9LzqD2qwDɇ}2Ƕ)%t#Gg3ڂ,ȭ : ag(9e)BԂ>;|L ,{YWAquͭ 7c;n Au)7 kGcȉr NZ/_Rrh]|;Ǒ>˯GhP-3T *`)UfpƐ2kAOCv:\*(';V/Wg@4-(9vȦϿbrG;4897Y6_W(M>8vS@JlЪH(xURQ5le̲$]<:s!m<(m7 Sب,< Ɓ UmnM8{)P-񏵓S^Q`"FZ fUPrc6*9T`wA0\^{oTJa6é5a #H ʠ׍iDdpj% 1N) n&\uɖN-*%q~ $8\k +8H DUeCu?7nY:vH,}VM}S֪apz'NEm|hr%~__AneХC22Pi͛3{Ay엥3=fޜ9pppjłg4N`p%[[3ıTڞtꝥK (~dͪ#gnBW1M, SqdHC=[ԓ}eePt<5睋O=.*Ƥq `pln᎒%ǶXe\$7w P~G* Pw=h# ػD9,~'ʠ<J7%P>$~~D&6I5v Rܭ1H^*OkaLsϜӷWl1V) ϜU4IyNaS9qζ,&÷x(A^(f-]'kh#;[8 cs!Ha282&.W@8Aw 80b-#D I]EE&InV|SN68s?^el~_{هF 䰯JW Nz8538c ܥ mĶZW_=@yW ixD줮bp7ICe_HЀyaG7ߤ`n (;D/϶rǕW_+S )CC4~,m0ڤ֧p&@%Ն?% qeY g6@*w$.lOyG" &|L`6"+u&PHSN@YQ{c-(X z(IQpsVzWXOQrҒe?B>#˒ґ m%[+ǂ!h"v ǴBx}](ePF{%&3<Ш!_.a z,w+N2Aܚ0xX=QrA,c'Zm:u?4@m$vtuZV_'xڈ$qRF?l#PF=8Gq@U9YrSF%Q.jۗX^['.9X`;(qQ#"Y?D*{Ĵnc$]s 5"@y@)7\?pug&HQǬױf$dnAC W 1g}!(˳:k\> GQG[ w+`{?$9^¸+ɯPl8jAyٙgxE#+vH$Aд,6BҋViTƇevrOW_Ï?GP*ׇ~xܱ!qNMixfC  |eDB(uIb~P p,Y5xMShP ZX3uBDbBJ ;P*@:L.,*jݦZVb(^׌83`X:A*@Xx (_0GSU֏]@N"x-7i\NOU`TW^ye^gR]@5X. D~I0D]sMRe1^PF$(AmIUݥ$!B:Πs%؏`q&J/s5d J\8cqܨÒE,*[q6ـq.&&.:8p {GsE78/=@o[[BTTL(02(& 1ejbppoCkI^$B9N7$|]Czcw3*_X\R$v),l|. >SO=Iy}u(2g p1`bsMAKQJ"Qf<,sVkBEMFB6GlW2ǖ[huoqP@?#30$bU;dTLK pGDU' % ,=usMVOLf)oJčDXm]N+fJǘJ#NDB5Q:VFrXs͵~O&l[o>1'" pwBfݫM?υc4J`8ђNwvtNрI~,Bjbo/p Q3 &o|I'A"7T6 矿Ќquϙ={ʤIK>;ʢ ReGS)EMapsC:a!?>C54 m&^wк1 X{ZkMMVB%twO62{?SZK^~z饗?imF 0#l)`kpN1@me^(̟?/[lE R-^|ɥz衳fΜ4a nZkX{m6F].<䐃.]FT*N [U& ŭz;?l}w>ݝnT냞H[tZt7ڨ޳7 ꫯ"|iӺ̞{ _[Sk@\}'Ⱥ׾7Y=sguV[n :)SĶ|K.)֨@`P k>{1%pW,2ꭔ1no 1CR*3+s1wI阙sĹIp&V?N=Fkwjm۶c/ k׮u#^>/~ˆoͯĠ (}b2&1(HbCyΰ(˥nfbbbrr222: u@k܉RUU S^2?PZ$ȤHp39( y AnE$?Mfd/\v+V,\0>>^+AZsN[6mڲi#&?vaIbJDŎ* 8FGeE:BsAMB@`2g|c*0+`D2't(9I(FQ TXI9hRJިtq'Dj\|ӵxEKgUe%U7mhhܲe];}j+ }'^>%JWhDD7pI%1&N`]^8 /077W93Q7<AZ=O`ztyT˘ p4N_V>fG$S ޓVď͌"1-7ÿӉNGaHԣM=tL0dݏA;g#|qHDniYF'YyW^uW_EL<$po?:Z$< 5SS\N(c Èdh#6]Sf"zBdp%[Gۘ7x<3s%.ozMvp̣5PWz!.Y,T ^XpW__O@[WOCk^twߍ;uuu&'T\ 8/+ uKMNsAC"^3O>ht6o,^vO{snkEsÝAXoJw8Ѕ`+ Pxh5,H mOѝ;wvvvv$`VGd\- b%^p Ʒehw z|+,MioÊܜGza^G9U&CRP⠊]|#UcQvH E`f rrA pEZ;pxիW _@~+a8uԋsqs`+-` Q f51a7ub MKgkȱ(5klSQ'RR!561GP675ݚZҰ)g8猻t1*8d_Xp7n" m UeC[{ck;7KR0+?}Y!?O?~;+σ>|qgs)IeEŅEh,2)BC&Ƈ4#jQI ~bSiՈ]T uDbhàt ǏkfP1B"8SX NrfICH0'LoI< ]qpۻ{Nus2ϭhjضw`0-##5k0H@/{>dS[dzJ Gq{@(?7%1{ o?J"h1[KQ6J!J X_A&/`@rx\L3UjH }R@+8gi*MgBQɜFiJ hα$Cakoj_7 *w{C>t2,NYSS{5.{ @ֱړ '"!09@ nӨK%gIHaSP!u0#&t0)]+_bF|a%f7vݷx}mH7|d| MA&($h԰U tp4BTXM-=PȢ }CjLA|Ö}CK*U=fwf|?ԓ'%%?d5W\Ew4wbzU-8q"9!^OTYKލGP"0q"v!|W'4*U /4PGڛ~hiIyv^F\2q=MHZh`h_vy-Y "D1ZǚZpU i]w!Vf&/<ߡ%ū.Fx Hڷtv:pp:5>v+L\$<" 9}ykYTd ԦʬمnUpmS3wi-WC >3g ~PGgnF"rI.z}fZڑGw=CpW,xe˔ӗm۶}?"Xg}Hp^M0‘8>7!Tg0TMw R+aG]l~JXq'۠3Aՙ&`S ~?`Bs;=,_/ 8z"!&巔"KM{Xr&8P*ψ> @_ްjŁ#ǀ/)+E{W\w[ v\q?UKχg `=o*H9i(BOc>߸ˡ)vbA BaJuݦR Yu^rStB wq\֋OlϜ/@dS,CgPSldQ`VHz'8]۠*ߓLzAo04Xzw;Xbrd˖-09ov/2l< lPx'@+ zU %)(4mjإ).]xEA Ra1xÊpÊ0`+Ά칚=倧ѣoez y\˚!|'*g[۟ZM]q6fyy4fR`C8S=3e &_BNMU'1iDi6*1iHs^M$i"5G a1 aŃ'λO?= 2>lŏpD|˩>@L"gy6")f #}ˎD:"on w#jNc }OߢT63̕hYS\ʫ3)!m%j =yX[n v9(VOt-.A!˪n]Np!kD+ 7c~Lf80(#h}C0(;]i85V`-OzZ-}I .~FCGnmny'`|R}TEx#Vt(Ƞnʰ2=smm\@ ȇ>맾0fd.1^bX$nѱc4C & #L -8Iȓ LIO/(*Yh1,XA9\ί:x֖~ {e{j"v@'0TodlU+aw7<}sw:oQnJӃh K еȺna*񧃿Px"펤ʪjB(։S^`'A=:28sF5bT`1$'"W<fr&2:]V=8LdrQD͑5k"T`[nB'dMrj^aaEUt!򨥥YHeĕ!?| 9y4H ie{/_~ւN 嫯\eڵKo޼?Cz>,<ԗ5'&&8 ~x!$eLbP$&1(c2&1A1I ʘf)IzsIENDB`structlog-24.4.0/docs/_static/sponsors/FilePreviews.svg0000644000000000000000000001210514645734712020162 0ustar00 structlog-24.4.0/docs/_static/sponsors/Klaviyo.svg0000644000000000000000000001256214645734712017203 0ustar00 structlog-24.4.0/docs/_static/sponsors/Sentry.svg0000644000000000000000000000355514645734712017053 0ustar00 structlog-24.4.0/docs/_static/sponsors/Tidelift.svg0000644000000000000000000000404214645734712017323 0ustar00 structlog-24.4.0/docs/_static/sponsors/Variomedia.svg0000644000000000000000000000361414645734712017643 0ustar00 structlog-24.4.0/src/structlog/__init__.py0000644000000000000000000000553714645734712015532 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. from __future__ import annotations from structlog import ( contextvars, dev, processors, stdlib, testing, threadlocal, tracebacks, types, typing, ) from structlog._base import BoundLoggerBase, get_context from structlog._config import ( configure, configure_once, get_config, get_logger, getLogger, is_configured, reset_defaults, wrap_logger, ) from structlog._generic import BoundLogger from structlog._native import make_filtering_bound_logger from structlog._output import ( BytesLogger, BytesLoggerFactory, PrintLogger, PrintLoggerFactory, WriteLogger, WriteLoggerFactory, ) from structlog.exceptions import DropEvent from structlog.testing import ReturnLogger, ReturnLoggerFactory try: from structlog import twisted except ImportError: twisted = None # type: ignore[assignment] __title__ = "structlog" __author__ = "Hynek Schlawack" __license__ = "MIT or Apache License, Version 2.0" __copyright__ = "Copyright (c) 2013 " + __author__ __all__ = [ "BoundLogger", "BoundLoggerBase", "BytesLogger", "BytesLoggerFactory", "configure_once", "configure", "contextvars", "dev", "DropEvent", "get_config", "get_context", "get_logger", "getLogger", "is_configured", "make_filtering_bound_logger", "PrintLogger", "PrintLoggerFactory", "processors", "reset_defaults", "ReturnLogger", "ReturnLoggerFactory", "stdlib", "testing", "threadlocal", "tracebacks", "twisted", "types", "typing", "wrap_logger", "WriteLogger", "WriteLoggerFactory", ] def __getattr__(name: str) -> str: import warnings from importlib.metadata import metadata, version dunder_to_metadata = { "__description__": "summary", "__uri__": "", "__email__": "", "__version__": "", } if name not in dunder_to_metadata: msg = f"module {__name__} has no attribute {name}" raise AttributeError(msg) if name != "__version__": warnings.warn( f"Accessing structlog.{name} is deprecated and will be " "removed in a future release. Use importlib.metadata directly " "to query for structlog's packaging metadata.", DeprecationWarning, stacklevel=2, ) else: return version("structlog") meta = metadata("structlog") if name == "__uri__": return meta["Project-URL"].split(" ", 1)[-1] if name == "__email__": return meta["Author-email"].split("<", 1)[1].rstrip(">") return meta[dunder_to_metadata[name]] structlog-24.4.0/src/structlog/_base.py0000644000000000000000000001613714645734712015042 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Logger wrapper and helper class. """ from __future__ import annotations from typing import Any, Iterable, Mapping, Sequence from structlog.exceptions import DropEvent from .typing import BindableLogger, Context, Processor, WrappedLogger class BoundLoggerBase: """ Immutable context carrier. Doesn't do any actual logging; examples for useful subclasses are: - the generic `BoundLogger` that can wrap anything, - `structlog.stdlib.BoundLogger`. - `structlog.twisted.BoundLogger`, See also `custom-wrappers`. """ _logger: WrappedLogger """ Wrapped logger. .. note:: Despite underscore available **read-only** to custom wrapper classes. See also `custom-wrappers`. """ def __init__( self, logger: WrappedLogger, processors: Iterable[Processor], context: Context, ): self._logger = logger self._processors = processors self._context = context def __repr__(self) -> str: return f"<{self.__class__.__name__}(context={self._context!r}, processors={self._processors!r})>" def __eq__(self, other: object) -> bool: try: return self._context == other._context # type: ignore[attr-defined] except AttributeError: return False def __ne__(self, other: object) -> bool: return not self.__eq__(other) def bind(self, **new_values: Any) -> BoundLoggerBase: """ Return a new logger with *new_values* added to the existing ones. """ return self.__class__( self._logger, self._processors, self._context.__class__(self._context, **new_values), ) def unbind(self, *keys: str) -> BoundLoggerBase: """ Return a new logger with *keys* removed from the context. Raises: KeyError: If the key is not part of the context. """ bl = self.bind() for key in keys: del bl._context[key] return bl def try_unbind(self, *keys: str) -> BoundLoggerBase: """ Like :meth:`unbind`, but best effort: missing keys are ignored. .. versionadded:: 18.2.0 """ bl = self.bind() for key in keys: bl._context.pop(key, None) return bl def new(self, **new_values: Any) -> BoundLoggerBase: """ Clear context and binds *new_values* using `bind`. Only necessary with dict implementations that keep global state like those wrapped by `structlog.threadlocal.wrap_dict` when threads are re-used. """ self._context.clear() return self.bind(**new_values) # Helper methods for sub-classing concrete BoundLoggers. def _process_event( self, method_name: str, event: str | None, event_kw: dict[str, Any] ) -> tuple[Sequence[Any], Mapping[str, Any]]: """ Combines creates an ``event_dict`` and runs the chain. Call it to combine your *event* and *context* into an event_dict and process using the processor chain. Args: method_name: The name of the logger method. Is passed into the processors. event: The event -- usually the first positional argument to a logger. event_kw: Additional event keywords. For example if someone calls ``log.info("foo", bar=42)``, *event* would to be ``"foo"`` and *event_kw* ``{"bar": 42}``. Raises: structlog.DropEvent: if log entry should be dropped. ValueError: if the final processor doesn't return a str, bytes, bytearray, tuple, or a dict. Returns: `tuple` of ``(*args, **kw)`` .. note:: Despite underscore available to custom wrapper classes. See also `custom-wrappers`. .. versionchanged:: 14.0.0 Allow final processor to return a `dict`. .. versionchanged:: 20.2.0 Allow final processor to return `bytes`. .. versionchanged:: 21.2.0 Allow final processor to return a `bytearray`. """ # We're typing it as Any, because processors can return more than an # EventDict. event_dict: Any = self._context.copy() event_dict.update(**event_kw) if event is not None: event_dict["event"] = event for proc in self._processors: event_dict = proc(self._logger, method_name, event_dict) if isinstance(event_dict, (str, bytes, bytearray)): return (event_dict,), {} if isinstance(event_dict, tuple): # In this case we assume that the last processor returned a tuple # of ``(args, kwargs)`` and pass it right through. return event_dict if isinstance(event_dict, dict): return (), event_dict msg = ( "Last processor didn't return an appropriate value. " "Valid return values are a dict, a tuple of (args, kwargs), bytes, or a str." ) raise ValueError(msg) def _proxy_to_logger( self, method_name: str, event: str | None = None, **event_kw: Any ) -> Any: """ Run processor chain on event & call *method_name* on wrapped logger. DRY convenience method that runs :func:`_process_event`, takes care of handling :exc:`structlog.DropEvent`, and finally calls *method_name* on :attr:`_logger` with the result. Args: method_name: The name of the method that's going to get called. Technically it should be identical to the method the user called because it also get passed into processors. event: The event -- usually the first positional argument to a logger. event_kw: Additional event keywords. For example if someone calls ``log.info("foo", bar=42)``, *event* would to be ``"foo"`` and *event_kw* ``{"bar": 42}``. .. note:: Despite underscore available to custom wrapper classes. See also `custom-wrappers`. """ try: args, kw = self._process_event(method_name, event, event_kw) return getattr(self._logger, method_name)(*args, **kw) except DropEvent: return None def get_context(bound_logger: BindableLogger) -> Context: """ Return *bound_logger*'s context. The type of *bound_logger* and the type returned depend on your configuration. Args: bound_logger: The bound logger whose context you want. Returns: The *actual* context from *bound_logger*. It is *not* copied first. .. versionadded:: 20.2.0 """ # This probably will get more complicated in the future. return bound_logger._context structlog-24.4.0/src/structlog/_config.py0000644000000000000000000003305414645734712015372 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Global state department. Don't reload this module or everything breaks. """ from __future__ import annotations import os import sys import warnings from typing import Any, Callable, Iterable, Sequence, Type, cast from ._native import make_filtering_bound_logger from ._output import PrintLoggerFactory from .contextvars import merge_contextvars from .dev import ConsoleRenderer, _has_colors, set_exc_info from .processors import StackInfoRenderer, TimeStamper, add_log_level from .typing import BindableLogger, Context, Processor, WrappedLogger """ Any changes to these defaults must be reflected in: - `getting-started`. - structlog.stdlib.recreate_defaults()'s docstring. """ _BUILTIN_DEFAULT_PROCESSORS: Sequence[Processor] = [ merge_contextvars, add_log_level, StackInfoRenderer(), set_exc_info, TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False), ConsoleRenderer( colors=os.environ.get("NO_COLOR", "") == "" and ( os.environ.get("FORCE_COLOR", "") != "" or ( _has_colors and sys.stdout is not None and hasattr(sys.stdout, "isatty") and sys.stdout.isatty() ) ) ), ] _BUILTIN_DEFAULT_CONTEXT_CLASS = cast(Type[Context], dict) _BUILTIN_DEFAULT_WRAPPER_CLASS = make_filtering_bound_logger(0) _BUILTIN_DEFAULT_LOGGER_FACTORY = PrintLoggerFactory() _BUILTIN_CACHE_LOGGER_ON_FIRST_USE = False class _Configuration: """ Global defaults. """ is_configured: bool = False default_processors: Iterable[Processor] = _BUILTIN_DEFAULT_PROCESSORS[:] default_context_class: type[Context] = _BUILTIN_DEFAULT_CONTEXT_CLASS default_wrapper_class: Any = _BUILTIN_DEFAULT_WRAPPER_CLASS logger_factory: Callable[..., WrappedLogger] = ( _BUILTIN_DEFAULT_LOGGER_FACTORY ) cache_logger_on_first_use: bool = _BUILTIN_CACHE_LOGGER_ON_FIRST_USE _CONFIG = _Configuration() """ Global defaults used when arguments to `wrap_logger` are omitted. """ def is_configured() -> bool: """ Return whether *structlog* has been configured. If `False`, *structlog* is running with builtin defaults. .. versionadded: 18.1.0 """ return _CONFIG.is_configured def get_config() -> dict[str, Any]: """ Get a dictionary with the current configuration. .. note:: Changes to the returned dictionary do *not* affect *structlog*. .. versionadded: 18.1.0 """ return { "processors": _CONFIG.default_processors, "context_class": _CONFIG.default_context_class, "wrapper_class": _CONFIG.default_wrapper_class, "logger_factory": _CONFIG.logger_factory, "cache_logger_on_first_use": _CONFIG.cache_logger_on_first_use, } def get_logger(*args: Any, **initial_values: Any) -> Any: """ Convenience function that returns a logger according to configuration. >>> from structlog import get_logger >>> log = get_logger(y=23) >>> log.info("hello", x=42) y=23 x=42 event='hello' Args: args: *Optional* positional arguments that are passed unmodified to the logger factory. Therefore it depends on the factory what they mean. initial_values: Values that are used to pre-populate your contexts. Returns: A proxy that creates a correctly configured bound logger when necessary. The type of that bound logger depends on your configuration and is `structlog.BoundLogger` by default. See `configuration` for details. If you prefer CamelCase, there's an alias for your reading pleasure: `structlog.getLogger`. .. versionadded:: 0.4.0 *args* """ return wrap_logger(None, logger_factory_args=args, **initial_values) getLogger = get_logger # noqa: N816 """ CamelCase alias for `structlog.get_logger`. This function is supposed to be in every source file -- we don't want it to stick out like a sore thumb in frameworks like Twisted or Zope. """ def wrap_logger( logger: WrappedLogger | None, processors: Iterable[Processor] | None = None, wrapper_class: type[BindableLogger] | None = None, context_class: type[Context] | None = None, cache_logger_on_first_use: bool | None = None, logger_factory_args: Iterable[Any] | None = None, **initial_values: Any, ) -> Any: """ Create a new bound logger for an arbitrary *logger*. Default values for *processors*, *wrapper_class*, and *context_class* can be set using `configure`. If you set an attribute here, `configure` calls have *no* effect for the *respective* attribute. In other words: selective overwriting of the defaults while keeping some *is* possible. Args: initial_values: Values that are used to pre-populate your contexts. logger_factory_args: Values that are passed unmodified as ``*logger_factory_args`` to the logger factory if not `None`. Returns: A proxy that creates a correctly configured bound logger when necessary. See `configure` for the meaning of the rest of the arguments. .. versionadded:: 0.4.0 *logger_factory_args* """ return BoundLoggerLazyProxy( logger, wrapper_class=wrapper_class, processors=processors, context_class=context_class, cache_logger_on_first_use=cache_logger_on_first_use, initial_values=initial_values, logger_factory_args=logger_factory_args, ) def configure( processors: Iterable[Processor] | None = None, wrapper_class: type[BindableLogger] | None = None, context_class: type[Context] | None = None, logger_factory: Callable[..., WrappedLogger] | None = None, cache_logger_on_first_use: bool | None = None, ) -> None: """ Configures the **global** defaults. They are used if `wrap_logger` or `get_logger` are called without arguments. Can be called several times, keeping an argument at `None` leaves it unchanged from the current setting. After calling for the first time, `is_configured` starts returning `True`. Use `reset_defaults` to undo your changes. Args: processors: The processor chain. See :doc:`processors` for details. wrapper_class: Class to use for wrapping loggers instead of `structlog.BoundLogger`. See `standard-library`, :doc:`twisted`, and `custom-wrappers`. context_class: Class to be used for internal context keeping. The default is a `dict` and since dictionaries are ordered as of Python 3.6, there's few reasons to change this option. logger_factory: Factory to be called to create a new logger that shall be wrapped. cache_logger_on_first_use: `wrap_logger` doesn't return an actual wrapped logger but a proxy that assembles one when it's first used. If this option is set to `True`, this assembled logger is cached. See `performance`. .. versionadded:: 0.3.0 *cache_logger_on_first_use* """ _CONFIG.is_configured = True if processors is not None: _CONFIG.default_processors = processors if wrapper_class is not None: _CONFIG.default_wrapper_class = wrapper_class if context_class is not None: _CONFIG.default_context_class = context_class if logger_factory is not None: _CONFIG.logger_factory = logger_factory if cache_logger_on_first_use is not None: _CONFIG.cache_logger_on_first_use = cache_logger_on_first_use def configure_once( processors: Iterable[Processor] | None = None, wrapper_class: type[BindableLogger] | None = None, context_class: type[Context] | None = None, logger_factory: Callable[..., WrappedLogger] | None = None, cache_logger_on_first_use: bool | None = None, ) -> None: """ Configures if structlog isn't configured yet. It does *not* matter whether it was configured using `configure` or `configure_once` before. Raises: RuntimeWarning: if repeated configuration is attempted. """ if not _CONFIG.is_configured: configure( processors=processors, wrapper_class=wrapper_class, context_class=context_class, logger_factory=logger_factory, cache_logger_on_first_use=cache_logger_on_first_use, ) else: warnings.warn( "Repeated configuration attempted.", RuntimeWarning, stacklevel=2 ) def reset_defaults() -> None: """ Resets global default values to builtin defaults. `is_configured` starts returning `False` afterwards. """ _CONFIG.is_configured = False _CONFIG.default_processors = _BUILTIN_DEFAULT_PROCESSORS[:] _CONFIG.default_wrapper_class = _BUILTIN_DEFAULT_WRAPPER_CLASS _CONFIG.default_context_class = _BUILTIN_DEFAULT_CONTEXT_CLASS _CONFIG.logger_factory = _BUILTIN_DEFAULT_LOGGER_FACTORY _CONFIG.cache_logger_on_first_use = _BUILTIN_CACHE_LOGGER_ON_FIRST_USE class BoundLoggerLazyProxy: """ Instantiates a bound logger on first usage. Takes both configuration and instantiation parameters into account. The only points where a bound logger changes state are ``bind()``, ``unbind()``, and ``new()`` and that return the actual ``BoundLogger``. If and only if configuration says so, that actual bound logger is cached on first usage. .. versionchanged:: 0.4.0 Added support for *logger_factory_args*. """ # fulfill BindableLogger protocol without carrying accidental state @property def _context(self) -> dict[str, str]: return self._initial_values def __init__( self, logger: WrappedLogger | None, wrapper_class: type[BindableLogger] | None = None, processors: Iterable[Processor] | None = None, context_class: type[Context] | None = None, cache_logger_on_first_use: bool | None = None, initial_values: dict[str, Any] | None = None, logger_factory_args: Any = None, ) -> None: self._logger = logger self._wrapper_class = wrapper_class self._processors = processors self._context_class = context_class self._cache_logger_on_first_use = cache_logger_on_first_use self._initial_values = initial_values or {} self._logger_factory_args = logger_factory_args or () def __repr__(self) -> str: return ( f"" ) def bind(self, **new_values: Any) -> BindableLogger: """ Assemble a new BoundLogger from arguments and configuration. """ if self._context_class: ctx = self._context_class(self._initial_values) else: ctx = _CONFIG.default_context_class(self._initial_values) _logger = self._logger if not _logger: _logger = _CONFIG.logger_factory(*self._logger_factory_args) if self._processors is None: procs = _CONFIG.default_processors else: procs = self._processors cls = self._wrapper_class or _CONFIG.default_wrapper_class # Looks like Protocols ignore definitions of __init__ so we have to # silence Mypy here. logger = cls( _logger, processors=procs, context=ctx # type: ignore[call-arg] ) def finalized_bind(**new_values: Any) -> BindableLogger: """ Use cached assembled logger to bind potentially new values. """ if new_values: return logger.bind(**new_values) return logger if self._cache_logger_on_first_use is True or ( self._cache_logger_on_first_use is None and _CONFIG.cache_logger_on_first_use is True ): self.bind = finalized_bind # type: ignore[method-assign] return finalized_bind(**new_values) def unbind(self, *keys: str) -> BindableLogger: """ Same as bind, except unbind *keys* first. In our case that could be only initial values. """ return self.bind().unbind(*keys) def try_unbind(self, *keys: str) -> BindableLogger: return self.bind().try_unbind(*keys) def new(self, **new_values: Any) -> BindableLogger: """ Clear context, then bind. """ if self._context_class: self._context_class().clear() else: _CONFIG.default_context_class().clear() return self.bind(**new_values) def __getattr__(self, name: str) -> Any: """ If a logging method if called on a lazy proxy, we have to create an ephemeral BoundLogger first. """ if name == "__isabstractmethod__": raise AttributeError bl = self.bind() return getattr(bl, name) def __getstate__(self) -> dict[str, Any]: """ Our __getattr__ magic makes this necessary. """ return self.__dict__ def __setstate__(self, state: dict[str, Any]) -> None: """ Our __getattr__ magic makes this necessary. """ for k, v in state.items(): setattr(self, k, v) structlog-24.4.0/src/structlog/_frames.py0000644000000000000000000000416114645734712015377 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. from __future__ import annotations import sys import traceback from io import StringIO from types import FrameType from typing import Callable from .contextvars import _ASYNC_CALLING_STACK from .typing import ExcInfo def _format_exception(exc_info: ExcInfo) -> str: """ Prettyprint an `exc_info` tuple. Shamelessly stolen from stdlib's logging module. """ if exc_info == (None, None, None): # type: ignore[comparison-overlap] return "MISSING" sio = StringIO() traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], None, sio) s = sio.getvalue() sio.close() if s[-1:] == "\n": s = s[:-1] return s def _find_first_app_frame_and_name( additional_ignores: list[str] | None = None, *, _getframe: Callable[[], FrameType] = sys._getframe, ) -> tuple[FrameType, str]: """ Remove all intra-structlog calls and return the relevant app frame. Args: additional_ignores: Additional names with which the first frame must not start. _getframe: Callable to find current frame. Only for testing to avoid monkeypatching of sys._getframe. Returns: tuple of (frame, name) """ ignores = tuple(["structlog"] + (additional_ignores or [])) f = _ASYNC_CALLING_STACK.get(_getframe()) name = f.f_globals.get("__name__") or "?" while name.startswith(ignores): if f.f_back is None: name = "?" break f = f.f_back name = f.f_globals.get("__name__") or "?" return f, name def _format_stack(frame: FrameType) -> str: """ Pretty-print the stack of *frame* like logging would. """ sio = StringIO() sio.write("Stack (most recent call last):\n") traceback.print_stack(frame, file=sio) sinfo = sio.getvalue() if sinfo[-1] == "\n": sinfo = sinfo[:-1] sio.close() return sinfo structlog-24.4.0/src/structlog/_generic.py0000644000000000000000000000314414645734712015536 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Generic bound logger that can wrap anything. """ from __future__ import annotations from functools import partial from typing import Any from structlog._base import BoundLoggerBase class BoundLogger(BoundLoggerBase): """ A generic BoundLogger that can wrap anything. Every unknown method will be passed to the wrapped *logger*. If that's too much magic for you, try `structlog.stdlib.BoundLogger` or `structlog.twisted.BoundLogger` which also take advantage of knowing the wrapped class which generally results in better performance. Not intended to be instantiated by yourself. See :func:`~structlog.wrap_logger` and :func:`~structlog.get_logger`. """ def __getattr__(self, method_name: str) -> Any: """ If not done so yet, wrap the desired logger method & cache the result. """ if method_name == "__deepcopy__": return None wrapped = partial(self._proxy_to_logger, method_name) setattr(self, method_name, wrapped) return wrapped def __getstate__(self) -> dict[str, Any]: """ Our __getattr__ magic makes this necessary. """ return self.__dict__ def __setstate__(self, state: dict[str, Any]) -> None: """ Our __getattr__ magic makes this necessary. """ for k, v in state.items(): setattr(self, k, v) structlog-24.4.0/src/structlog/_greenlets.py0000644000000000000000000000226614645734712016116 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ greenlet-specific code that pretends to be a `threading.local`. Fails to import if not running under greenlet. """ from __future__ import annotations from typing import Any from weakref import WeakKeyDictionary from greenlet import getcurrent class GreenThreadLocal: """ threading.local() replacement for greenlets. """ def __init__(self) -> None: self.__dict__["_weakdict"] = WeakKeyDictionary() def __getattr__(self, name: str) -> Any: key = getcurrent() try: return self._weakdict[key][name] except KeyError: raise AttributeError(name) from None def __setattr__(self, name: str, val: Any) -> None: key = getcurrent() self._weakdict.setdefault(key, {})[name] = val def __delattr__(self, name: str) -> None: key = getcurrent() try: del self._weakdict[key][name] except KeyError: raise AttributeError(name) from None structlog-24.4.0/src/structlog/_log_levels.py0000644000000000000000000000367014645734712016261 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Extracted log level data used by both stdlib and native log level filters. """ from __future__ import annotations import logging from .typing import EventDict # Adapted from the stdlib CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0 NAME_TO_LEVEL = { "critical": CRITICAL, "exception": ERROR, "error": ERROR, "warn": WARNING, "warning": WARNING, "info": INFO, "debug": DEBUG, "notset": NOTSET, } LEVEL_TO_NAME = { v: k for k, v in NAME_TO_LEVEL.items() if k not in ("warn", "exception", "notset") } # Keep around for backwards-compatability in case someone imported them. _LEVEL_TO_NAME = LEVEL_TO_NAME _NAME_TO_LEVEL = NAME_TO_LEVEL def map_method_name(method_name: str) -> str: # warn is just a deprecated alias in the stdlib. if method_name == "warn": return "warning" # Calling exception("") is the same as error("", exc_info=True) if method_name == "exception": return "error" return method_name def add_log_level( logger: logging.Logger, method_name: str, event_dict: EventDict ) -> EventDict: """ Add the log level to the event dict under the ``level`` key. Since that's just the log method name, this processor works with non-stdlib logging as well. Therefore it's importable both from `structlog.processors` as well as from `structlog.stdlib`. .. versionadded:: 15.0.0 .. versionchanged:: 20.2.0 Importable from `structlog.processors` (additionally to `structlog.stdlib`). .. versionchanged:: 24.1.0 Added mapping from "exception" to "error" """ event_dict["level"] = map_method_name(method_name) return event_dict structlog-24.4.0/src/structlog/_native.py0000644000000000000000000001672114645734712015415 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ structlog's native high-performance loggers. """ from __future__ import annotations import asyncio import contextvars import sys from typing import Any, Callable from ._base import BoundLoggerBase from ._log_levels import ( CRITICAL, DEBUG, ERROR, INFO, LEVEL_TO_NAME, NOTSET, WARNING, ) from .contextvars import _ASYNC_CALLING_STACK from .typing import FilteringBoundLogger def _nop(self: Any, event: str, *args: Any, **kw: Any) -> Any: return None async def _anop(self: Any, event: str, *args: Any, **kw: Any) -> Any: return None def exception( self: FilteringBoundLogger, event: str, *args: Any, **kw: Any ) -> Any: kw.setdefault("exc_info", True) return self.error(event, *args, **kw) async def aexception( self: FilteringBoundLogger, event: str, *args: Any, **kw: Any ) -> Any: """ .. versionchanged:: 23.3.0 Callsite parameters are now also collected under asyncio. """ # Exception info has to be extracted this early, because it is no longer # available once control is passed to the executor. if kw.get("exc_info", True) is True: kw["exc_info"] = sys.exc_info() scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back) # type: ignore[arg-type] ctx = contextvars.copy_context() try: runner = await asyncio.get_running_loop().run_in_executor( None, lambda: ctx.run(lambda: self.error(event, *args, **kw)), ) finally: _ASYNC_CALLING_STACK.reset(scs_token) return runner def make_filtering_bound_logger(min_level: int) -> type[FilteringBoundLogger]: """ Create a new `FilteringBoundLogger` that only logs *min_level* or higher. The logger is optimized such that log levels below *min_level* only consist of a ``return None``. All familiar log methods are present, with async variants of each that are prefixed by an ``a``. Therefore, the async version of ``log.info("hello")`` is ``await log.ainfo("hello")``. Additionally it has a ``log(self, level: int, **kw: Any)`` method to mirror `logging.Logger.log` and `structlog.stdlib.BoundLogger.log`. Compared to using *structlog*'s standard library integration and the `structlog.stdlib.filter_by_level` processor: - It's faster because once the logger is built at program start; it's a static class. - For the same reason you can't change the log level once configured. Use the dynamic approach of `standard-library` instead, if you need this feature. - You *can* have (much) more fine-grained filtering by :ref:`writing a simple processor `. Args: min_level: The log level as an integer. You can use the constants from `logging` like ``logging.INFO`` or pass the values directly. See `this table from the logging docs `_ for possible values. .. versionadded:: 20.2.0 .. versionchanged:: 21.1.0 The returned loggers are now pickleable. .. versionadded:: 20.1.0 The ``log()`` method. .. versionadded:: 22.2.0 Async variants ``alog()``, ``adebug()``, ``ainfo()``, and so forth. """ return LEVEL_TO_FILTERING_LOGGER[min_level] def _make_filtering_bound_logger(min_level: int) -> type[FilteringBoundLogger]: """ Create a new `FilteringBoundLogger` that only logs *min_level* or higher. The logger is optimized such that log levels below *min_level* only consist of a ``return None``. """ def make_method( level: int, ) -> tuple[Callable[..., Any], Callable[..., Any]]: if level < min_level: return _nop, _anop name = LEVEL_TO_NAME[level] def meth(self: Any, event: str, *args: Any, **kw: Any) -> Any: if not args: return self._proxy_to_logger(name, event, **kw) return self._proxy_to_logger(name, event % args, **kw) async def ameth(self: Any, event: str, *args: Any, **kw: Any) -> Any: """ .. versionchanged:: 23.3.0 Callsite parameters are now also collected under asyncio. """ if args: event = event % args scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back) # type: ignore[arg-type] ctx = contextvars.copy_context() try: await asyncio.get_running_loop().run_in_executor( None, lambda: ctx.run( lambda: self._proxy_to_logger(name, event, **kw) ), ) finally: _ASYNC_CALLING_STACK.reset(scs_token) meth.__name__ = name ameth.__name__ = f"a{name}" return meth, ameth def log(self: Any, level: int, event: str, *args: Any, **kw: Any) -> Any: if level < min_level: return None name = LEVEL_TO_NAME[level] if not args: return self._proxy_to_logger(name, event, **kw) return self._proxy_to_logger(name, event % args, **kw) async def alog( self: Any, level: int, event: str, *args: Any, **kw: Any ) -> Any: """ .. versionchanged:: 23.3.0 Callsite parameters are now also collected under asyncio. """ if level < min_level: return None name = LEVEL_TO_NAME[level] if args: event = event % args scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back) # type: ignore[arg-type] ctx = contextvars.copy_context() try: runner = await asyncio.get_running_loop().run_in_executor( None, lambda: ctx.run( lambda: self._proxy_to_logger(name, event, **kw) ), ) finally: _ASYNC_CALLING_STACK.reset(scs_token) return runner meths: dict[str, Callable[..., Any]] = {"log": log, "alog": alog} for lvl, name in LEVEL_TO_NAME.items(): meths[name], meths[f"a{name}"] = make_method(lvl) meths["exception"] = exception meths["aexception"] = aexception meths["fatal"] = meths["error"] meths["afatal"] = meths["aerror"] meths["warn"] = meths["warning"] meths["awarn"] = meths["awarning"] meths["msg"] = meths["info"] meths["amsg"] = meths["ainfo"] return type( f"BoundLoggerFilteringAt{LEVEL_TO_NAME.get(min_level, 'Notset').capitalize()}", (BoundLoggerBase,), meths, ) # Pre-create all possible filters to make them pickleable. BoundLoggerFilteringAtNotset = _make_filtering_bound_logger(NOTSET) BoundLoggerFilteringAtDebug = _make_filtering_bound_logger(DEBUG) BoundLoggerFilteringAtInfo = _make_filtering_bound_logger(INFO) BoundLoggerFilteringAtWarning = _make_filtering_bound_logger(WARNING) BoundLoggerFilteringAtError = _make_filtering_bound_logger(ERROR) BoundLoggerFilteringAtCritical = _make_filtering_bound_logger(CRITICAL) LEVEL_TO_FILTERING_LOGGER = { CRITICAL: BoundLoggerFilteringAtCritical, ERROR: BoundLoggerFilteringAtError, WARNING: BoundLoggerFilteringAtWarning, INFO: BoundLoggerFilteringAtInfo, DEBUG: BoundLoggerFilteringAtDebug, NOTSET: BoundLoggerFilteringAtNotset, } structlog-24.4.0/src/structlog/_output.py0000644000000000000000000002163314645734712015465 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Logger classes responsible for output. """ from __future__ import annotations import copy import sys import threading from pickle import PicklingError from sys import stderr, stdout from typing import IO, Any, BinaryIO, TextIO WRITE_LOCKS: dict[IO[Any], threading.Lock] = {} def _get_lock_for_file(file: IO[Any]) -> threading.Lock: lock = WRITE_LOCKS.get(file) if lock is None: lock = threading.Lock() WRITE_LOCKS[file] = lock return lock class PrintLogger: """ Print events into a file. Args: file: File to print to. (default: `sys.stdout`) >>> from structlog import PrintLogger >>> PrintLogger().info("hello") hello Useful if you follow `current logging best practices `. Also very useful for testing and examples since `logging` is finicky in doctests. .. versionchanged:: 22.1.0 The implementation has been switched to use `print` for better monkeypatchability. """ def __init__(self, file: TextIO | None = None): self._file = file or stdout self._lock = _get_lock_for_file(self._file) def __getstate__(self) -> str: """ Our __getattr__ magic makes this necessary. """ if self._file is stdout: return "stdout" if self._file is stderr: return "stderr" raise PicklingError( "Only PrintLoggers to sys.stdout and sys.stderr can be pickled." ) def __setstate__(self, state: Any) -> None: """ Our __getattr__ magic makes this necessary. """ if state == "stdout": self._file = stdout else: self._file = stderr self._lock = _get_lock_for_file(self._file) def __deepcopy__(self, memodict: dict[str, object]) -> PrintLogger: """ Create a new PrintLogger with the same attributes. Similar to pickling. """ if self._file not in (stdout, stderr): raise copy.error( "Only PrintLoggers to sys.stdout and sys.stderr " "can be deepcopied." ) newself = self.__class__(self._file) newself._lock = _get_lock_for_file(newself._file) return newself def __repr__(self) -> str: return f"" def msg(self, message: str) -> None: """ Print *message*. """ f = self._file if self._file is not stdout else None with self._lock: print(message, file=f, flush=True) log = debug = info = warn = warning = msg fatal = failure = err = error = critical = exception = msg class PrintLoggerFactory: r""" Produce `PrintLogger`\ s. To be used with `structlog.configure`\ 's ``logger_factory``. Args: file: File to print to. (default: `sys.stdout`) Positional arguments are silently ignored. .. versionadded:: 0.4.0 """ def __init__(self, file: TextIO | None = None): self._file = file def __call__(self, *args: Any) -> PrintLogger: return PrintLogger(self._file) class WriteLogger: """ Write events into a file. Args: file: File to print to. (default: `sys.stdout`) >>> from structlog import WriteLogger >>> WriteLogger().info("hello") hello Useful if you follow `current logging best practices `. Also very useful for testing and examples since `logging` is finicky in doctests. A little faster and a little less versatile than `structlog.PrintLogger`. .. versionadded:: 22.1.0 """ def __init__(self, file: TextIO | None = None): self._file = file or sys.stdout self._write = self._file.write self._flush = self._file.flush self._lock = _get_lock_for_file(self._file) def __getstate__(self) -> str: """ Our __getattr__ magic makes this necessary. """ if self._file is stdout: return "stdout" if self._file is stderr: return "stderr" raise PicklingError( "Only WriteLoggers to sys.stdout and sys.stderr can be pickled." ) def __setstate__(self, state: Any) -> None: """ Our __getattr__ magic makes this necessary. """ if state == "stdout": self._file = stdout else: self._file = stderr self._lock = _get_lock_for_file(self._file) def __deepcopy__(self, memodict: dict[str, object]) -> WriteLogger: """ Create a new WriteLogger with the same attributes. Similar to pickling. """ if self._file not in (sys.stdout, sys.stderr): raise copy.error( "Only WriteLoggers to sys.stdout and sys.stderr " "can be deepcopied." ) newself = self.__class__(self._file) newself._write = newself._file.write newself._flush = newself._file.flush newself._lock = _get_lock_for_file(newself._file) return newself def __repr__(self) -> str: return f"" def msg(self, message: str) -> None: """ Write and flush *message*. """ with self._lock: self._write(message + "\n") self._flush() log = debug = info = warn = warning = msg fatal = failure = err = error = critical = exception = msg class WriteLoggerFactory: r""" Produce `WriteLogger`\ s. To be used with `structlog.configure`\ 's ``logger_factory``. Args: file: File to print to. (default: `sys.stdout`) Positional arguments are silently ignored. .. versionadded:: 22.1.0 """ def __init__(self, file: TextIO | None = None): self._file = file def __call__(self, *args: Any) -> WriteLogger: return WriteLogger(self._file) class BytesLogger: r""" Writes bytes into a file. Args: file: File to print to. (default: `sys.stdout`\ ``.buffer``) Useful if you follow `current logging best practices ` together with a formatter that returns bytes (e.g. `orjson `_). .. versionadded:: 20.2.0 """ __slots__ = ("_file", "_write", "_flush", "_lock") def __init__(self, file: BinaryIO | None = None): self._file = file or sys.stdout.buffer self._write = self._file.write self._flush = self._file.flush self._lock = _get_lock_for_file(self._file) def __getstate__(self) -> str: """ Our __getattr__ magic makes this necessary. """ if self._file is sys.stdout.buffer: return "stdout" if self._file is sys.stderr.buffer: return "stderr" raise PicklingError( "Only BytesLoggers to sys.stdout and sys.stderr can be pickled." ) def __setstate__(self, state: Any) -> None: """ Our __getattr__ magic makes this necessary. """ if state == "stdout": self._file = sys.stdout.buffer else: self._file = sys.stderr.buffer self._write = self._file.write self._flush = self._file.flush self._lock = _get_lock_for_file(self._file) def __deepcopy__(self, memodict: dict[str, object]) -> BytesLogger: """ Create a new BytesLogger with the same attributes. Similar to pickling. """ if self._file not in (sys.stdout.buffer, sys.stderr.buffer): raise copy.error( "Only BytesLoggers to sys.stdout and sys.stderr " "can be deepcopied." ) newself = self.__class__(self._file) newself._write = newself._file.write newself._flush = newself._file.flush newself._lock = _get_lock_for_file(newself._file) return newself def __repr__(self) -> str: return f"" def msg(self, message: bytes) -> None: """ Write *message*. """ with self._lock: self._write(message + b"\n") self._flush() log = debug = info = warn = warning = msg fatal = failure = err = error = critical = exception = msg class BytesLoggerFactory: r""" Produce `BytesLogger`\ s. To be used with `structlog.configure`\ 's ``logger_factory``. Args: file: File to print to. (default: `sys.stdout`\ ``.buffer``) Positional arguments are silently ignored. .. versionadded:: 20.2.0 """ __slots__ = ("_file",) def __init__(self, file: BinaryIO | None = None): self._file = file def __call__(self, *args: Any) -> BytesLogger: return BytesLogger(self._file) structlog-24.4.0/src/structlog/_utils.py0000644000000000000000000000164514645734712015266 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Generic utilities. """ from __future__ import annotations import sys from contextlib import suppress from typing import Any def get_processname() -> str: # based on code from # https://github.com/python/cpython/blob/313f92a57bc3887026ec16adb536bb2b7580ce47/Lib/logging/__init__.py#L342-L352 processname = "n/a" mp: Any = sys.modules.get("multiprocessing") if mp is not None: # Errors may occur if multiprocessing has not finished loading # yet - e.g. if a custom import hook causes third-party code # to run when multiprocessing calls import. with suppress(Exception): processname = mp.current_process().name return processname structlog-24.4.0/src/structlog/contextvars.py0000644000000000000000000001237314645734712016347 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Primitives to deal with a concurrency supporting context, as introduced in Python 3.7 as :mod:`contextvars`. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 Reimplemented without using a single dict as context carrier for improved isolation. Every key-value pair is a separate `contextvars.ContextVar` now. .. versionchanged:: 23.3.0 Callsite parameters are now also collected under asyncio. See :doc:`contextvars`. """ from __future__ import annotations import contextlib import contextvars from types import FrameType from typing import Any, Generator, Mapping import structlog from .typing import BindableLogger, EventDict, WrappedLogger STRUCTLOG_KEY_PREFIX = "structlog_" STRUCTLOG_KEY_PREFIX_LEN = len(STRUCTLOG_KEY_PREFIX) _ASYNC_CALLING_STACK: contextvars.ContextVar[FrameType] = ( contextvars.ContextVar("_ASYNC_CALLING_STACK") ) # For proper isolation, we have to use a dict of ContextVars instead of a # single ContextVar with a dict. # See https://github.com/hynek/structlog/pull/302 for details. _CONTEXT_VARS: dict[str, contextvars.ContextVar[Any]] = {} def get_contextvars() -> dict[str, Any]: """ Return a copy of the *structlog*-specific context-local context. .. versionadded:: 21.2.0 """ rv = {} ctx = contextvars.copy_context() for k in ctx: if k.name.startswith(STRUCTLOG_KEY_PREFIX) and ctx[k] is not Ellipsis: rv[k.name[STRUCTLOG_KEY_PREFIX_LEN:]] = ctx[k] return rv def get_merged_contextvars(bound_logger: BindableLogger) -> dict[str, Any]: """ Return a copy of the current context-local context merged with the context from *bound_logger*. .. versionadded:: 21.2.0 """ ctx = get_contextvars() ctx.update(structlog.get_context(bound_logger)) return ctx def merge_contextvars( logger: WrappedLogger, method_name: str, event_dict: EventDict ) -> EventDict: """ A processor that merges in a global (context-local) context. Use this as your first processor in :func:`structlog.configure` to ensure context-local context is included in all log calls. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 See toplevel note. """ ctx = contextvars.copy_context() for k in ctx: if k.name.startswith(STRUCTLOG_KEY_PREFIX) and ctx[k] is not Ellipsis: event_dict.setdefault(k.name[STRUCTLOG_KEY_PREFIX_LEN:], ctx[k]) return event_dict def clear_contextvars() -> None: """ Clear the context-local context. The typical use-case for this function is to invoke it early in request- handling code. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 See toplevel note. """ ctx = contextvars.copy_context() for k in ctx: if k.name.startswith(STRUCTLOG_KEY_PREFIX): k.set(Ellipsis) def bind_contextvars(**kw: Any) -> Mapping[str, contextvars.Token[Any]]: r""" Put keys and values into the context-local context. Use this instead of :func:`~structlog.BoundLogger.bind` when you want some context to be global (context-local). Return the mapping of `contextvars.Token`\s resulting from setting the backing :class:`~contextvars.ContextVar`\s. Suitable for passing to :func:`reset_contextvars`. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 Return the `contextvars.Token` mapping rather than None. See also the toplevel note. """ rv = {} for k, v in kw.items(): structlog_k = f"{STRUCTLOG_KEY_PREFIX}{k}" try: var = _CONTEXT_VARS[structlog_k] except KeyError: var = contextvars.ContextVar(structlog_k, default=Ellipsis) _CONTEXT_VARS[structlog_k] = var rv[k] = var.set(v) return rv def reset_contextvars(**kw: contextvars.Token[Any]) -> None: r""" Reset contextvars corresponding to the given Tokens. .. versionadded:: 21.1.0 """ for k, v in kw.items(): structlog_k = f"{STRUCTLOG_KEY_PREFIX}{k}" var = _CONTEXT_VARS[structlog_k] var.reset(v) def unbind_contextvars(*keys: str) -> None: """ Remove *keys* from the context-local context if they are present. Use this instead of :func:`~structlog.BoundLogger.unbind` when you want to remove keys from a global (context-local) context. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 See toplevel note. """ for k in keys: structlog_k = f"{STRUCTLOG_KEY_PREFIX}{k}" if structlog_k in _CONTEXT_VARS: _CONTEXT_VARS[structlog_k].set(Ellipsis) @contextlib.contextmanager def bound_contextvars(**kw: Any) -> Generator[None, None, None]: """ Bind *kw* to the current context-local context. Unbind or restore *kw* afterwards. Do **not** affect other keys. Can be used as a context manager or decorator. .. versionadded:: 21.4.0 """ context = get_contextvars() saved = {k: context[k] for k in context.keys() & kw.keys()} bind_contextvars(**kw) try: yield finally: unbind_contextvars(*kw.keys()) bind_contextvars(**saved) structlog-24.4.0/src/structlog/dev.py0000644000000000000000000005660014645734712014546 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Helpers that make development with *structlog* more pleasant. See also the narrative documentation in `console-output`. """ from __future__ import annotations import shutil import sys import warnings from dataclasses import dataclass from io import StringIO from types import ModuleType from typing import ( Any, Callable, Literal, Protocol, Sequence, TextIO, Type, Union, cast, ) from ._frames import _format_exception from .processors import _figure_out_exc_info from .typing import EventDict, ExceptionRenderer, ExcInfo, WrappedLogger try: import colorama except ImportError: colorama = None try: import better_exceptions except ImportError: better_exceptions = None try: import rich from rich.console import Console from rich.traceback import Traceback except ImportError: rich = None # type: ignore[assignment] __all__ = [ "ConsoleRenderer", "plain_traceback", "rich_traceback", "better_traceback", ] _IS_WINDOWS = sys.platform == "win32" _MISSING = "{who} requires the {package} package installed. " _EVENT_WIDTH = 30 # pad the event name to so many characters def _pad(s: str, length: int) -> str: """ Pads *s* to length *length*. """ missing = length - len(s) return s + " " * (max(0, missing)) if colorama is not None: RESET_ALL = colorama.Style.RESET_ALL BRIGHT = colorama.Style.BRIGHT DIM = colorama.Style.DIM RED = colorama.Fore.RED BLUE = colorama.Fore.BLUE CYAN = colorama.Fore.CYAN MAGENTA = colorama.Fore.MAGENTA YELLOW = colorama.Fore.YELLOW GREEN = colorama.Fore.GREEN RED_BACK = colorama.Back.RED else: # These are the same values as the Colorama color codes. Redefining them # here allows users to specify that they want color without having to # install Colorama, which is only supposed to be necessary in Windows. RESET_ALL = "\033[0m" BRIGHT = "\033[1m" DIM = "\033[2m" RED = "\033[31m" BLUE = "\033[34m" CYAN = "\033[36m" MAGENTA = "\033[35m" YELLOW = "\033[33m" GREEN = "\033[32m" RED_BACK = "\033[41m" # On Windows, colors are only available if Colorama is installed. _has_colors = not _IS_WINDOWS or colorama is not None # Prevent breakage of packages that used the old name of the variable. _use_colors = _has_colors class _Styles(Protocol): reset: str bright: str level_critical: str level_exception: str level_error: str level_warn: str level_info: str level_debug: str level_notset: str timestamp: str logger_name: str kv_key: str kv_value: str Styles = Union[_Styles, Type[_Styles]] class _ColorfulStyles: reset = RESET_ALL bright = BRIGHT level_critical = RED level_exception = RED level_error = RED level_warn = YELLOW level_info = GREEN level_debug = GREEN level_notset = RED_BACK timestamp = DIM logger_name = BLUE kv_key = CYAN kv_value = MAGENTA class _PlainStyles: reset = "" bright = "" level_critical = "" level_exception = "" level_error = "" level_warn = "" level_info = "" level_debug = "" level_notset = "" timestamp = "" logger_name = "" kv_key = "" kv_value = "" class ColumnFormatter(Protocol): """ :class:`~typing.Protocol` for column formatters. See `KeyValueColumnFormatter` and `LogLevelColumnFormatter` for examples. .. versionadded:: 23.3.0 """ def __call__(self, key: str, value: object) -> str: """ Format *value* for *key*. This method is responsible for formatting, *key*, the ``=``, and the *value*. That means that it can use any string instead of the ``=`` and it can leave out both the *key* or the *value*. If it returns an empty string, the column is omitted completely. """ @dataclass class Column: """ A column defines the way a key-value pair is formatted, and, by it's position to the *columns* argument of `ConsoleRenderer`, the order in which it is rendered. Args: key: The key for which this column is responsible. Leave empty to define it as the default formatter. formatter: The formatter for columns with *key*. .. versionadded:: 23.3.0 """ key: str formatter: ColumnFormatter @dataclass class KeyValueColumnFormatter: """ Format a key-value pair. Args: key_style: The style to apply to the key. If None, the key is omitted. value_style: The style to apply to the value. reset_style: The style to apply whenever a style is no longer needed. value_repr: A callable that returns the string representation of the value. width: The width to pad the value to. If 0, no padding is done. prefix: A string to prepend to the formatted key-value pair. May contain styles. postfix: A string to append to the formatted key-value pair. May contain styles. .. versionadded:: 23.3.0 """ key_style: str | None value_style: str reset_style: str value_repr: Callable[[object], str] width: int = 0 prefix: str = "" postfix: str = "" def __call__(self, key: str, value: object) -> str: sio = StringIO() if self.prefix: sio.write(self.prefix) sio.write(self.reset_style) if self.key_style is not None: sio.write(self.key_style) sio.write(key) sio.write(self.reset_style) sio.write("=") sio.write(self.value_style) sio.write(_pad(self.value_repr(value), self.width)) sio.write(self.reset_style) if self.postfix: sio.write(self.postfix) sio.write(self.reset_style) return sio.getvalue() class LogLevelColumnFormatter: """ Format a log level according to *level_styles*. The width is padded to the longest level name (if *level_styles* is passed -- otherwise there's no way to know the lengths of all levels). Args: level_styles: A dictionary of level names to styles that are applied to it. If None, the level is formatted as a plain ``[level]``. reset_style: What to use to reset the style after the level name. Ignored if if *level_styles* is None. width: The width to pad the level to. If 0, no padding is done. .. versionadded:: 23.3.0 .. versionadded:: 24.2.0 *width* """ level_styles: dict[str, str] | None reset_style: str width: int def __init__( self, level_styles: dict[str, str], reset_style: str, width: int | None = None, ) -> None: self.level_styles = level_styles if level_styles: self.width = ( 0 if width == 0 else len(max(self.level_styles.keys(), key=lambda e: len(e))) ) self.reset_style = reset_style else: self.width = 0 self.reset_style = "" def __call__(self, key: str, value: object) -> str: level = cast(str, value) style = ( "" if self.level_styles is None else self.level_styles.get(level, "") ) return f"[{style}{_pad(level, self.width)}{self.reset_style}]" _NOTHING = object() def plain_traceback(sio: TextIO, exc_info: ExcInfo) -> None: """ "Pretty"-print *exc_info* to *sio* using our own plain formatter. To be passed into `ConsoleRenderer`'s ``exception_formatter`` argument. Used by default if neither Rich nor *better-exceptions* are present. .. versionadded:: 21.2.0 """ sio.write("\n" + _format_exception(exc_info)) @dataclass class RichTracebackFormatter: """ A Rich traceback renderer with the given options. Pass an instance as `ConsoleRenderer`'s ``exception_formatter`` argument. See :class:`rich.traceback.Traceback` for details on the arguments. If a *width* of -1 is passed, the terminal width is used. If the width can't be determined, fall back to 80. .. versionadded:: 23.2.0 """ color_system: Literal[ "auto", "standard", "256", "truecolor", "windows" ] = "truecolor" show_locals: bool = True max_frames: int = 100 theme: str | None = None word_wrap: bool = False extra_lines: int = 3 width: int = 100 indent_guides: bool = True locals_max_length: int = 10 locals_max_string: int = 80 locals_hide_dunder: bool = True locals_hide_sunder: bool = False suppress: Sequence[str | ModuleType] = () def __call__(self, sio: TextIO, exc_info: ExcInfo) -> None: if self.width == -1: self.width, _ = shutil.get_terminal_size((80, 0)) sio.write("\n") Console( file=sio, color_system=self.color_system, width=self.width ).print( Traceback.from_exception( *exc_info, show_locals=self.show_locals, max_frames=self.max_frames, theme=self.theme, word_wrap=self.word_wrap, extra_lines=self.extra_lines, width=self.width, indent_guides=self.indent_guides, locals_max_length=self.locals_max_length, locals_max_string=self.locals_max_string, locals_hide_dunder=self.locals_hide_dunder, locals_hide_sunder=self.locals_hide_sunder, suppress=self.suppress, ) ) rich_traceback = RichTracebackFormatter() """ Pretty-print *exc_info* to *sio* using the Rich package. To be passed into `ConsoleRenderer`'s ``exception_formatter`` argument. This is a `RichTracebackFormatter` with default arguments and used by default if Rich is installed. .. versionadded:: 21.2.0 """ def better_traceback(sio: TextIO, exc_info: ExcInfo) -> None: """ Pretty-print *exc_info* to *sio* using the *better-exceptions* package. To be passed into `ConsoleRenderer`'s ``exception_formatter`` argument. Used by default if *better-exceptions* is installed and Rich is absent. .. versionadded:: 21.2.0 """ sio.write("\n" + "".join(better_exceptions.format_exception(*exc_info))) if rich is not None: default_exception_formatter = rich_traceback elif better_exceptions is not None: default_exception_formatter = better_traceback else: default_exception_formatter = plain_traceback class ConsoleRenderer: r""" Render ``event_dict`` nicely aligned, possibly in colors, and ordered. If ``event_dict`` contains a true-ish ``exc_info`` key, it will be rendered *after* the log line. If Rich_ or better-exceptions_ are present, in colors and with extra context. Args: columns: A list of `Column` objects defining both the order and format of the key-value pairs in the output. If passed, most other arguments become meaningless. **Must** contain a column with ``key=''`` that defines the default formatter. .. seealso:: `columns-config` pad_event: Pad the event to this many characters. Ignored if *columns* are passed. colors: Use colors for a nicer output. `True` by default. On Windows only if Colorama_ is installed. Ignored if *columns* are passed. force_colors: Force colors even for non-tty destinations. Use this option if your logs are stored in a file that is meant to be streamed to the console. Only meaningful on Windows. Ignored if *columns* are passed. repr_native_str: When `True`, `repr` is also applied to ``str``\ s. The ``event`` key is *never* `repr` -ed. Ignored if *columns* are passed. level_styles: When present, use these styles for colors. This must be a dict from level names (strings) to Colorama styles. The default can be obtained by calling `ConsoleRenderer.get_default_level_styles`. Ignored when *columns* are passed. exception_formatter: A callable to render ``exc_infos``. If Rich_ or better-exceptions_ are installed, they are used for pretty-printing by default (rich_ taking precedence). You can also manually set it to `plain_traceback`, `better_traceback`, an instance of `RichTracebackFormatter` like `rich_traceback`, or implement your own. sort_keys: Whether to sort keys when formatting. `True` by default. Ignored if *columns* are passed. event_key: The key to look for the main log message. Needed when you rename it e.g. using `structlog.processors.EventRenamer`. Ignored if *columns* are passed. timestamp_key: The key to look for timestamp of the log message. Needed when you rename it e.g. using `structlog.processors.EventRenamer`. Ignored if *columns* are passed. pad_level: Whether to pad log level with blanks to the longest amongst all level label. Requires the Colorama_ package if *colors* is `True` **on Windows**. Raises: ValueError: If there's not exactly one default column formatter. .. _Colorama: https://pypi.org/project/colorama/ .. _better-exceptions: https://pypi.org/project/better-exceptions/ .. _Rich: https://pypi.org/project/rich/ .. versionadded:: 16.0.0 .. versionadded:: 16.1.0 *colors* .. versionadded:: 17.1.0 *repr_native_str* .. versionadded:: 18.1.0 *force_colors* .. versionadded:: 18.1.0 *level_styles* .. versionchanged:: 19.2.0 Colorama now initializes lazily to avoid unwanted initializations as ``ConsoleRenderer`` is used by default. .. versionchanged:: 19.2.0 Can be pickled now. .. versionchanged:: 20.1.0 Colorama does not initialize lazily on Windows anymore because it breaks rendering. .. versionchanged:: 21.1.0 It is additionally possible to set the logger name using the ``logger_name`` key in the ``event_dict``. .. versionadded:: 21.2.0 *exception_formatter* .. versionchanged:: 21.2.0 `ConsoleRenderer` now handles the ``exc_info`` event dict key itself. Do **not** use the `structlog.processors.format_exc_info` processor together with `ConsoleRenderer` anymore! It will keep working, but you can't have customize exception formatting and a warning will be raised if you ask for it. .. versionchanged:: 21.2.0 The colors keyword now defaults to True on non-Windows systems, and either True or False in Windows depending on whether Colorama is installed. .. versionadded:: 21.3.0 *sort_keys* .. versionadded:: 22.1.0 *event_key* .. versionadded:: 23.2.0 *timestamp_key* .. versionadded:: 23.3.0 *columns* .. versionadded:: 24.2.0 *pad_level* """ def __init__( # noqa: PLR0912, PLR0915 self, pad_event: int = _EVENT_WIDTH, colors: bool = _has_colors, force_colors: bool = False, repr_native_str: bool = False, level_styles: Styles | None = None, exception_formatter: ExceptionRenderer = default_exception_formatter, sort_keys: bool = True, event_key: str = "event", timestamp_key: str = "timestamp", columns: list[Column] | None = None, pad_level: bool = True, ): self._exception_formatter = exception_formatter self._sort_keys = sort_keys if columns is not None: to_warn = [] def add_meaningless_arg(arg: str) -> None: to_warn.append( f"The `{arg}` argument is ignored when passing `columns`.", ) if pad_event != _EVENT_WIDTH: add_meaningless_arg("pad_event") if colors != _has_colors: add_meaningless_arg("colors") if force_colors is not False: add_meaningless_arg("force_colors") if repr_native_str is not False: add_meaningless_arg("repr_native_str") if level_styles is not None: add_meaningless_arg("level_styles") if event_key != "event": add_meaningless_arg("event_key") if timestamp_key != "timestamp": add_meaningless_arg("timestamp_key") for w in to_warn: warnings.warn(w, stacklevel=2) defaults = [col for col in columns if col.key == ""] if not defaults: raise ValueError( "Must pass a default column formatter (a column with `key=''`)." ) if len(defaults) > 1: raise ValueError("Only one default column formatter allowed.") self._default_column_formatter = defaults[0].formatter self._columns = [col for col in columns if col.key] return # Create default columns configuration. styles: Styles if colors: if _IS_WINDOWS: # pragma: no cover # On Windows, we can't do colorful output without colorama. if colorama is None: classname = self.__class__.__name__ raise SystemError( _MISSING.format( who=classname + " with `colors=True`", package="colorama", ) ) # Colorama must be init'd on Windows, but must NOT be # init'd on other OSes, because it can break colors. if force_colors: colorama.deinit() colorama.init(strip=False) else: colorama.init() styles = _ColorfulStyles else: styles = _PlainStyles self._styles = styles level_to_color = ( self.get_default_level_styles(colors) if level_styles is None else level_styles ) for key in level_to_color: level_to_color[key] += styles.bright self._longest_level = len( max(level_to_color.keys(), key=lambda e: len(e)) ) self._repr_native_str = repr_native_str self._default_column_formatter = KeyValueColumnFormatter( styles.kv_key, styles.kv_value, styles.reset, value_repr=self._repr, width=0, ) logger_name_formatter = KeyValueColumnFormatter( key_style=None, value_style=styles.bright + styles.logger_name, reset_style=styles.reset, value_repr=str, prefix="[", postfix="]", ) level_width = 0 if not pad_level else None self._columns = [ Column( timestamp_key, KeyValueColumnFormatter( key_style=None, value_style=styles.timestamp, reset_style=styles.reset, value_repr=str, ), ), Column( "level", LogLevelColumnFormatter( level_to_color, reset_style=styles.reset, width=level_width ), ), Column( event_key, KeyValueColumnFormatter( key_style=None, value_style=styles.bright, reset_style=styles.reset, value_repr=str, width=pad_event, ), ), Column("logger", logger_name_formatter), Column("logger_name", logger_name_formatter), ] def _repr(self, val: Any) -> str: """ Determine representation of *val* depending on its type & self._repr_native_str. """ if self._repr_native_str is True: return repr(val) if isinstance(val, str): return val return repr(val) def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> str: stack = event_dict.pop("stack", None) exc = event_dict.pop("exception", None) exc_info = event_dict.pop("exc_info", None) kvs = [ col.formatter(col.key, val) for col in self._columns if (val := event_dict.pop(col.key, _NOTHING)) is not _NOTHING ] + [ self._default_column_formatter(key, event_dict[key]) for key in (sorted(event_dict) if self._sort_keys else event_dict) ] sio = StringIO() sio.write((" ".join(kv for kv in kvs if kv)).rstrip(" ")) if stack is not None: sio.write("\n" + stack) if exc_info or exc is not None: sio.write("\n\n" + "=" * 79 + "\n") if exc_info: exc_info = _figure_out_exc_info(exc_info) if exc_info != (None, None, None): self._exception_formatter(sio, exc_info) elif exc is not None: if self._exception_formatter is not plain_traceback: warnings.warn( "Remove `format_exc_info` from your processor chain " "if you want pretty exceptions.", stacklevel=2, ) sio.write("\n" + exc) return sio.getvalue() @staticmethod def get_default_level_styles(colors: bool = True) -> Any: """ Get the default styles for log levels This is intended to be used with `ConsoleRenderer`'s ``level_styles`` parameter. For example, if you are adding custom levels in your home-grown :func:`~structlog.stdlib.add_log_level` you could do:: my_styles = ConsoleRenderer.get_default_level_styles() my_styles["EVERYTHING_IS_ON_FIRE"] = my_styles["critical"] renderer = ConsoleRenderer(level_styles=my_styles) Args: colors: Whether to use colorful styles. This must match the *colors* parameter to `ConsoleRenderer`. Default: `True`. """ styles: Styles styles = _ColorfulStyles if colors else _PlainStyles return { "critical": styles.level_critical, "exception": styles.level_exception, "error": styles.level_error, "warn": styles.level_warn, "warning": styles.level_warn, "info": styles.level_info, "debug": styles.level_debug, "notset": styles.level_notset, } _SENTINEL = object() def set_exc_info( logger: WrappedLogger, method_name: str, event_dict: EventDict ) -> EventDict: """ Set ``event_dict["exc_info"] = True`` if *method_name* is ``"exception"``. Do nothing if the name is different or ``exc_info`` is already set. """ if ( method_name != "exception" or event_dict.get("exc_info", _SENTINEL) is not _SENTINEL ): return event_dict event_dict["exc_info"] = True return event_dict structlog-24.4.0/src/structlog/exceptions.py0000644000000000000000000000076714645734712016154 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Exceptions factored out to avoid import loops. """ from __future__ import annotations class DropEvent(BaseException): """ If raised by an processor, the event gets silently dropped. Derives from BaseException because it's technically not an error. """ structlog-24.4.0/src/structlog/processors.py0000644000000000000000000006772514645734712016204 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Processors useful regardless of the logging framework. """ from __future__ import annotations import datetime import enum import json import logging import operator import os import sys import threading import time from types import FrameType from typing import ( Any, Callable, ClassVar, Collection, NamedTuple, Sequence, TextIO, ) from ._frames import ( _find_first_app_frame_and_name, _format_exception, _format_stack, ) from ._log_levels import NAME_TO_LEVEL, add_log_level from ._utils import get_processname from .tracebacks import ExceptionDictTransformer from .typing import EventDict, ExceptionTransformer, ExcInfo, WrappedLogger __all__ = [ "NAME_TO_LEVEL", # some people rely on it being here "add_log_level", "CallsiteParameter", "CallsiteParameterAdder", "dict_tracebacks", "EventRenamer", "ExceptionPrettyPrinter", "format_exc_info", "JSONRenderer", "KeyValueRenderer", "StackInfoRenderer", "TimeStamper", "UnicodeDecoder", "UnicodeEncoder", ] class KeyValueRenderer: """ Render ``event_dict`` as a list of ``Key=repr(Value)`` pairs. Args: sort_keys: Whether to sort keys when formatting. key_order: List of keys that should be rendered in this exact order. Missing keys will be rendered as ``None``, extra keys depending on *sort_keys* and the dict class. drop_missing: When ``True``, extra keys in *key_order* will be dropped rather than rendered as ``None``. repr_native_str: When ``True``, :func:`repr()` is also applied to native strings. .. versionadded:: 0.2.0 *key_order* .. versionadded:: 16.1.0 *drop_missing* .. versionadded:: 17.1.0 *repr_native_str* """ def __init__( self, sort_keys: bool = False, key_order: Sequence[str] | None = None, drop_missing: bool = False, repr_native_str: bool = True, ): self._ordered_items = _items_sorter(sort_keys, key_order, drop_missing) if repr_native_str is True: self._repr = repr else: def _repr(inst: Any) -> str: if isinstance(inst, str): return inst return repr(inst) self._repr = _repr def __call__( self, _: WrappedLogger, __: str, event_dict: EventDict ) -> str: return " ".join( k + "=" + self._repr(v) for k, v in self._ordered_items(event_dict) ) class LogfmtRenderer: """ Render ``event_dict`` using the logfmt_ format. .. _logfmt: https://brandur.org/logfmt Args: sort_keys: Whether to sort keys when formatting. key_order: List of keys that should be rendered in this exact order. Missing keys are rendered with empty values, extra keys depending on *sort_keys* and the dict class. drop_missing: When ``True``, extra keys in *key_order* will be dropped rather than rendered with empty values. bool_as_flag: When ``True``, render ``{"flag": True}`` as ``flag``, instead of ``flag=true``. ``{"flag": False}`` is always rendered as ``flag=false``. Raises: ValueError: If a key contains non-printable or whitespace characters. .. versionadded:: 21.5.0 """ def __init__( self, sort_keys: bool = False, key_order: Sequence[str] | None = None, drop_missing: bool = False, bool_as_flag: bool = True, ): self._ordered_items = _items_sorter(sort_keys, key_order, drop_missing) self.bool_as_flag = bool_as_flag def __call__( self, _: WrappedLogger, __: str, event_dict: EventDict ) -> str: elements: list[str] = [] for key, value in self._ordered_items(event_dict): if any(c <= " " for c in key): msg = f'Invalid key: "{key}"' raise ValueError(msg) if value is None: elements.append(f"{key}=") continue if isinstance(value, bool): if self.bool_as_flag and value: elements.append(f"{key}") continue value = "true" if value else "false" value = str(value) backslashes_need_escaping = ( " " in value or "=" in value or '"' in value ) if backslashes_need_escaping and "\\" in value: value = value.replace("\\", "\\\\") value = value.replace('"', '\\"').replace("\n", "\\n") if backslashes_need_escaping: value = f'"{value}"' elements.append(f"{key}={value}") return " ".join(elements) def _items_sorter( sort_keys: bool, key_order: Sequence[str] | None, drop_missing: bool, ) -> Callable[[EventDict], list[tuple[str, object]]]: """ Return a function to sort items from an ``event_dict``. See `KeyValueRenderer` for an explanation of the parameters. """ # Use an optimized version for each case. if key_order and sort_keys: def ordered_items(event_dict: EventDict) -> list[tuple[str, Any]]: items = [] for key in key_order: value = event_dict.pop(key, None) if value is not None or not drop_missing: items.append((key, value)) items += sorted(event_dict.items()) return items elif key_order: def ordered_items(event_dict: EventDict) -> list[tuple[str, Any]]: items = [] for key in key_order: value = event_dict.pop(key, None) if value is not None or not drop_missing: items.append((key, value)) items += event_dict.items() return items elif sort_keys: def ordered_items(event_dict: EventDict) -> list[tuple[str, Any]]: return sorted(event_dict.items()) else: ordered_items = operator.methodcaller( # type: ignore[assignment] "items" ) return ordered_items class UnicodeEncoder: """ Encode unicode values in ``event_dict``. Args: encoding: Encoding to encode to (default: ``"utf-8"``). errors: How to cope with encoding errors (default ``"backslashreplace"``). Just put it in the processor chain before the renderer. .. note:: Not very useful in a Python 3-only world. """ _encoding: str _errors: str def __init__( self, encoding: str = "utf-8", errors: str = "backslashreplace" ) -> None: self._encoding = encoding self._errors = errors def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: for key, value in event_dict.items(): if isinstance(value, str): event_dict[key] = value.encode(self._encoding, self._errors) return event_dict class UnicodeDecoder: """ Decode byte string values in ``event_dict``. Args: encoding: Encoding to decode from (default: ``"utf-8"``). errors: How to cope with encoding errors (default: ``"replace"``). Useful to prevent ``b"abc"`` being rendered as as ``'b"abc"'``. Just put it in the processor chain before the renderer. .. versionadded:: 15.4.0 """ _encoding: str _errors: str def __init__( self, encoding: str = "utf-8", errors: str = "replace" ) -> None: self._encoding = encoding self._errors = errors def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: for key, value in event_dict.items(): if isinstance(value, bytes): event_dict[key] = value.decode(self._encoding, self._errors) return event_dict class JSONRenderer: """ Render the ``event_dict`` using ``serializer(event_dict, **dumps_kw)``. Args: dumps_kw: Are passed unmodified to *serializer*. If *default* is passed, it will disable support for ``__structlog__``-based serialization. serializer: A :func:`json.dumps`-compatible callable that will be used to format the string. This can be used to use alternative JSON encoders (default: :func:`json.dumps`). .. seealso:: :doc:`performance` for examples. .. versionadded:: 0.2.0 Support for ``__structlog__`` serialization method. .. versionadded:: 15.4.0 *serializer* parameter. .. versionadded:: 18.2.0 Serializer's *default* parameter can be overwritten now. """ def __init__( self, serializer: Callable[..., str | bytes] = json.dumps, **dumps_kw: Any, ) -> None: dumps_kw.setdefault("default", _json_fallback_handler) self._dumps_kw = dumps_kw self._dumps = serializer def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> str | bytes: """ The return type of this depends on the return type of self._dumps. """ return self._dumps(event_dict, **self._dumps_kw) def _json_fallback_handler(obj: Any) -> Any: """ Serialize custom datatypes and pass the rest to __structlog__ & repr(). """ # circular imports :( from structlog.threadlocal import _ThreadLocalDictWrapper if isinstance(obj, _ThreadLocalDictWrapper): return obj._dict try: return obj.__structlog__() except AttributeError: return repr(obj) class ExceptionRenderer: """ Replace an ``exc_info`` field with an ``exception`` field which is rendered by *exception_formatter*. The contents of the ``exception`` field depends on the return value of the *exception_formatter* that is passed: - The default produces a formatted string via Python's built-in traceback formatting (this is :obj:`.format_exc_info`). - If you pass a :class:`~structlog.tracebacks.ExceptionDictTransformer`, it becomes a list of stack dicts that can be serialized to JSON. If *event_dict* contains the key ``exc_info``, there are three possible behaviors: 1. If the value is a tuple, render it into the key ``exception``. 2. If the value is an Exception render it into the key ``exception``. 3. If the value true but no tuple, obtain exc_info ourselves and render that. If there is no ``exc_info`` key, the *event_dict* is not touched. This behavior is analog to the one of the stdlib's logging. Args: exception_formatter: A callable that is used to format the exception from the ``exc_info`` field into the ``exception`` field. .. seealso:: :doc:`exceptions` for a broader explanation of *structlog*'s exception features. .. versionadded:: 22.1.0 """ def __init__( self, exception_formatter: ExceptionTransformer = _format_exception, ) -> None: self.format_exception = exception_formatter def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: exc_info = event_dict.pop("exc_info", None) if exc_info: event_dict["exception"] = self.format_exception( _figure_out_exc_info(exc_info) ) return event_dict format_exc_info = ExceptionRenderer() """ Replace an ``exc_info`` field with an ``exception`` string field using Python's built-in traceback formatting. If *event_dict* contains the key ``exc_info``, there are three possible behaviors: 1. If the value is a tuple, render it into the key ``exception``. 2. If the value is an Exception render it into the key ``exception``. 3. If the value is true but no tuple, obtain exc_info ourselves and render that. If there is no ``exc_info`` key, the *event_dict* is not touched. This behavior is analog to the one of the stdlib's logging. .. seealso:: :doc:`exceptions` for a broader explanation of *structlog*'s exception features. """ dict_tracebacks = ExceptionRenderer(ExceptionDictTransformer()) """ Replace an ``exc_info`` field with an ``exception`` field containing structured tracebacks suitable for, e.g., JSON output. It is a shortcut for :class:`ExceptionRenderer` with a :class:`~structlog.tracebacks.ExceptionDictTransformer`. The treatment of the ``exc_info`` key is identical to `format_exc_info`. .. versionadded:: 22.1.0 .. seealso:: :doc:`exceptions` for a broader explanation of *structlog*'s exception features. """ class TimeStamper: """ Add a timestamp to ``event_dict``. Args: fmt: strftime format string, or ``"iso"`` for `ISO 8601 `_, or `None` for a `UNIX timestamp `_. utc: Whether timestamp should be in UTC or local time. key: Target key in *event_dict* for added timestamps. .. versionchanged:: 19.2.0 Can be pickled now. """ __slots__ = ("_stamper", "fmt", "utc", "key") def __init__( self, fmt: str | None = None, utc: bool = True, key: str = "timestamp", ) -> None: self.fmt, self.utc, self.key = fmt, utc, key self._stamper = _make_stamper(fmt, utc, key) def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: return self._stamper(event_dict) def __getstate__(self) -> dict[str, Any]: return {"fmt": self.fmt, "utc": self.utc, "key": self.key} def __setstate__(self, state: dict[str, Any]) -> None: self.fmt = state["fmt"] self.utc = state["utc"] self.key = state["key"] self._stamper = _make_stamper(**state) def _make_stamper( fmt: str | None, utc: bool, key: str ) -> Callable[[EventDict], EventDict]: """ Create a stamper function. """ if fmt is None and not utc: msg = "UNIX timestamps are always UTC." raise ValueError(msg) now: Callable[[], datetime.datetime] if utc: def now() -> datetime.datetime: return datetime.datetime.now(tz=datetime.timezone.utc) else: def now() -> datetime.datetime: # A naive local datetime is fine here, because we only format it. return datetime.datetime.now() # noqa: DTZ005 if fmt is None: def stamper_unix(event_dict: EventDict) -> EventDict: event_dict[key] = time.time() return event_dict return stamper_unix if fmt.upper() == "ISO": def stamper_iso_local(event_dict: EventDict) -> EventDict: event_dict[key] = now().isoformat() return event_dict def stamper_iso_utc(event_dict: EventDict) -> EventDict: event_dict[key] = now().isoformat().replace("+00:00", "Z") return event_dict if utc: return stamper_iso_utc return stamper_iso_local def stamper_fmt(event_dict: EventDict) -> EventDict: event_dict[key] = now().strftime(fmt) return event_dict return stamper_fmt class MaybeTimeStamper: """ A timestamper that only adds a timestamp if there is none. This allows you to overwrite the ``timestamp`` key in the event dict for example when the event is coming from another system. It takes the same arguments as `TimeStamper`. .. versionadded:: 23.2.0 """ __slots__ = ("stamper",) def __init__( self, fmt: str | None = None, utc: bool = True, key: str = "timestamp", ): self.stamper = TimeStamper(fmt=fmt, utc=utc, key=key) def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: if "timestamp" not in event_dict: return self.stamper(logger, name, event_dict) return event_dict def _figure_out_exc_info(v: Any) -> ExcInfo: """ Depending on the Python version will try to do the smartest thing possible to transform *v* into an ``exc_info`` tuple. """ if isinstance(v, BaseException): return (v.__class__, v, v.__traceback__) if isinstance(v, tuple): return v if v: return sys.exc_info() # type: ignore[return-value] return v class ExceptionPrettyPrinter: """ Pretty print exceptions and remove them from the ``event_dict``. Args: file: Target file for output (default: ``sys.stdout``). This processor is mostly for development and testing so you can read exceptions properly formatted. It behaves like `format_exc_info` except it removes the exception data from the event dictionary after printing it. It's tolerant to having `format_exc_info` in front of itself in the processor chain but doesn't require it. In other words, it handles both ``exception`` as well as ``exc_info`` keys. .. versionadded:: 0.4.0 .. versionchanged:: 16.0.0 Added support for passing exceptions as ``exc_info`` on Python 3. """ def __init__( self, file: TextIO | None = None, exception_formatter: ExceptionTransformer = _format_exception, ) -> None: if file is not None: self._file = file else: self._file = sys.stdout def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: exc = event_dict.pop("exception", None) if exc is None: exc_info = _figure_out_exc_info(event_dict.pop("exc_info", None)) if exc_info: exc = _format_exception(exc_info) if exc: print(exc, file=self._file) return event_dict class StackInfoRenderer: """ Add stack information with key ``stack`` if ``stack_info`` is `True`. Useful when you want to attach a stack dump to a log entry without involving an exception and works analogously to the *stack_info* argument of the Python standard library logging. Args: additional_ignores: By default, stack frames coming from *structlog* are ignored. With this argument you can add additional names that are ignored, before the stack starts being rendered. They are matched using ``startswith()``, so they don't have to match exactly. The names are used to find the first relevant name, therefore once a frame is found that doesn't start with *structlog* or one of *additional_ignores*, **no filtering** is applied to subsequent frames. .. versionadded:: 0.4.0 .. versionadded:: 22.1.0 *additional_ignores* """ __slots__ = ("_additional_ignores",) def __init__(self, additional_ignores: list[str] | None = None) -> None: self._additional_ignores = additional_ignores def __call__( self, logger: WrappedLogger, name: str, event_dict: EventDict ) -> EventDict: if event_dict.pop("stack_info", None): event_dict["stack"] = _format_stack( _find_first_app_frame_and_name(self._additional_ignores)[0] ) return event_dict class CallsiteParameter(enum.Enum): """ Callsite parameters that can be added to an event dictionary with the `structlog.processors.CallsiteParameterAdder` processor class. The string values of the members of this enum will be used as the keys for the callsite parameters in the event dictionary. .. versionadded:: 21.5.0 """ #: The full path to the python source file of the callsite. PATHNAME = "pathname" #: The basename part of the full path to the python source file of the #: callsite. FILENAME = "filename" #: The python module the callsite was in. This mimics the module attribute #: of `logging.LogRecord` objects and will be the basename, without #: extension, of the full path to the python source file of the callsite. MODULE = "module" #: The name of the function that the callsite was in. FUNC_NAME = "func_name" #: The line number of the callsite. LINENO = "lineno" #: The ID of the thread the callsite was executed in. THREAD = "thread" #: The name of the thread the callsite was executed in. THREAD_NAME = "thread_name" #: The ID of the process the callsite was executed in. PROCESS = "process" #: The name of the process the callsite was executed in. PROCESS_NAME = "process_name" def _get_callsite_pathname(module: str, frame: FrameType) -> Any: return frame.f_code.co_filename def _get_callsite_filename(module: str, frame: FrameType) -> Any: return os.path.basename(frame.f_code.co_filename) def _get_callsite_module(module: str, frame: FrameType) -> Any: return os.path.splitext(os.path.basename(frame.f_code.co_filename))[0] def _get_callsite_func_name(module: str, frame: FrameType) -> Any: return frame.f_code.co_name def _get_callsite_lineno(module: str, frame: FrameType) -> Any: return frame.f_lineno def _get_callsite_thread(module: str, frame: FrameType) -> Any: return threading.get_ident() def _get_callsite_thread_name(module: str, frame: FrameType) -> Any: return threading.current_thread().name def _get_callsite_process(module: str, frame: FrameType) -> Any: return os.getpid() def _get_callsite_process_name(module: str, frame: FrameType) -> Any: return get_processname() class CallsiteParameterAdder: """ Adds parameters of the callsite that an event dictionary originated from to the event dictionary. This processor can be used to enrich events dictionaries with information such as the function name, line number and filename that an event dictionary originated from. If the event dictionary has an embedded `logging.LogRecord` object and did not originate from *structlog* then the callsite information will be determined from the `logging.LogRecord` object. For event dictionaries without an embedded `logging.LogRecord` object the callsite will be determined from the stack trace, ignoring all intra-structlog calls, calls from the `logging` module, and stack frames from modules with names that start with values in ``additional_ignores``, if it is specified. The keys used for callsite parameters in the event dictionary are the string values of `CallsiteParameter` enum members. Args: parameters: A collection of `CallsiteParameter` values that should be added to the event dictionary. additional_ignores: Additional names with which a stack frame's module name must not start for it to be considered when determening the callsite. .. note:: When used with `structlog.stdlib.ProcessorFormatter` the most efficient configuration is to either use this processor in ``foreign_pre_chain`` of `structlog.stdlib.ProcessorFormatter` and in ``processors`` of `structlog.configure`, or to use it in ``processors`` of `structlog.stdlib.ProcessorFormatter` without using it in ``processors`` of `structlog.configure` and ``foreign_pre_chain`` of `structlog.stdlib.ProcessorFormatter`. .. versionadded:: 21.5.0 """ _handlers: ClassVar[ dict[CallsiteParameter, Callable[[str, FrameType], Any]] ] = { CallsiteParameter.PATHNAME: _get_callsite_pathname, CallsiteParameter.FILENAME: _get_callsite_filename, CallsiteParameter.MODULE: _get_callsite_module, CallsiteParameter.FUNC_NAME: _get_callsite_func_name, CallsiteParameter.LINENO: _get_callsite_lineno, CallsiteParameter.THREAD: _get_callsite_thread, CallsiteParameter.THREAD_NAME: _get_callsite_thread_name, CallsiteParameter.PROCESS: _get_callsite_process, CallsiteParameter.PROCESS_NAME: _get_callsite_process_name, } _record_attribute_map: ClassVar[dict[CallsiteParameter, str]] = { CallsiteParameter.PATHNAME: "pathname", CallsiteParameter.FILENAME: "filename", CallsiteParameter.MODULE: "module", CallsiteParameter.FUNC_NAME: "funcName", CallsiteParameter.LINENO: "lineno", CallsiteParameter.THREAD: "thread", CallsiteParameter.THREAD_NAME: "threadName", CallsiteParameter.PROCESS: "process", CallsiteParameter.PROCESS_NAME: "processName", } _all_parameters: ClassVar[set[CallsiteParameter]] = set(CallsiteParameter) class _RecordMapping(NamedTuple): event_dict_key: str record_attribute: str __slots__ = ("_active_handlers", "_additional_ignores", "_record_mappings") def __init__( self, parameters: Collection[CallsiteParameter] = _all_parameters, additional_ignores: list[str] | None = None, ) -> None: if additional_ignores is None: additional_ignores = [] # Ignore stack frames from the logging module. They will occur if this # processor is used in ProcessorFormatter, and additionally the logging # module should not be logging using structlog. self._additional_ignores = ["logging", *additional_ignores] self._active_handlers: list[ tuple[CallsiteParameter, Callable[[str, FrameType], Any]] ] = [] self._record_mappings: list[CallsiteParameterAdder._RecordMapping] = [] for parameter in parameters: self._active_handlers.append( (parameter, self._handlers[parameter]) ) self._record_mappings.append( self._RecordMapping( parameter.value, self._record_attribute_map[parameter], ) ) def __call__( self, logger: logging.Logger, name: str, event_dict: EventDict ) -> EventDict: record: logging.LogRecord | None = event_dict.get("_record") from_structlog: bool | None = event_dict.get("_from_structlog") # If the event dictionary has a record, but it comes from structlog, # then the callsite parameters of the record will not be correct. if record is not None and not from_structlog: for mapping in self._record_mappings: event_dict[mapping.event_dict_key] = record.__dict__[ mapping.record_attribute ] else: frame, module = _find_first_app_frame_and_name( additional_ignores=self._additional_ignores ) for parameter, handler in self._active_handlers: event_dict[parameter.value] = handler(module, frame) return event_dict class EventRenamer: r""" Rename the ``event`` key in event dicts. This is useful if you want to use consistent log message keys across platforms and/or use the ``event`` key for something custom. .. warning:: It's recommended to put this processor right before the renderer, since some processors may rely on the presence and meaning of the ``event`` key. Args: to: Rename ``event_dict["event"]`` to ``event_dict[to]`` replace_by: Rename ``event_dict[replace_by]`` to ``event_dict["event"]``. *replace_by* missing from ``event_dict`` is handled gracefully. .. versionadded:: 22.1.0 See also the :ref:`rename-event` recipe. """ def __init__(self, to: str, replace_by: str | None = None): self.to = to self.replace_by = replace_by def __call__( self, logger: logging.Logger, name: str, event_dict: EventDict ) -> EventDict: event = event_dict.pop("event") event_dict[self.to] = event if self.replace_by is not None: replace_by = event_dict.pop(self.replace_by, None) if replace_by is not None: event_dict["event"] = replace_by return event_dict structlog-24.4.0/src/structlog/py.typed0000644000000000000000000000000014645734712015074 0ustar00structlog-24.4.0/src/structlog/stdlib.py0000644000000000000000000011223314645734712015244 0ustar00# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Processors and helpers specific to the :mod:`logging` module from the `Python standard library `_. See also :doc:`structlog's standard library support `. """ from __future__ import annotations import asyncio import contextvars import functools import logging import sys import warnings from functools import partial from typing import Any, Callable, Collection, Dict, Iterable, Sequence, cast from . import _config from ._base import BoundLoggerBase from ._frames import _find_first_app_frame_and_name, _format_stack from ._log_levels import LEVEL_TO_NAME, NAME_TO_LEVEL, add_log_level from .contextvars import _ASYNC_CALLING_STACK, merge_contextvars from .exceptions import DropEvent from .processors import StackInfoRenderer from .typing import ( Context, EventDict, ExcInfo, Processor, ProcessorReturnValue, WrappedLogger, ) __all__ = [ "add_log_level_number", "add_log_level", "add_logger_name", "ExtraAdder", "BoundLogger", "filter_by_level", "get_logger", "LoggerFactory", "PositionalArgumentsFormatter", "ProcessorFormatter", "recreate_defaults", "render_to_log_kwargs", ] def recreate_defaults(*, log_level: int | None = logging.NOTSET) -> None: """ Recreate defaults on top of standard library's logging. The output looks the same, but goes through `logging`. As with vanilla defaults, the backwards-compatibility guarantees don't apply to the settings applied here. Args: log_level: If `None`, don't configure standard library logging **at all**. Otherwise configure it to log to `sys.stdout` at *log_level* (``logging.NOTSET`` being the default). If you need more control over `logging`, pass `None` here and configure it yourself. .. versionadded:: 22.1.0 .. versionchanged:: 23.3.0 Added `add_logger_name`. """ if log_level is not None: kw = {"force": True} logging.basicConfig( format="%(message)s", stream=sys.stdout, level=log_level, **kw, # type: ignore[arg-type] ) _config.reset_defaults() _config.configure( processors=[ merge_contextvars, add_log_level, add_logger_name, StackInfoRenderer(), _config._BUILTIN_DEFAULT_PROCESSORS[-2], # TimeStamper _config._BUILTIN_DEFAULT_PROCESSORS[-1], # ConsoleRenderer ], wrapper_class=BoundLogger, logger_factory=LoggerFactory(), ) _SENTINEL = object() class _FixedFindCallerLogger(logging.Logger): """ Change the behavior of `logging.Logger.findCaller` to cope with *structlog*'s extra frames. """ def findCaller( self, stack_info: bool = False, stacklevel: int = 1 ) -> tuple[str, int, str, str | None]: """ Finds the first caller frame outside of structlog so that the caller info is populated for wrapping stdlib. This logger gets set as the default one when using LoggerFactory. """ sinfo: str | None f, name = _find_first_app_frame_and_name(["logging"]) sinfo = _format_stack(f) if stack_info else None return f.f_code.co_filename, f.f_lineno, f.f_code.co_name, sinfo class BoundLogger(BoundLoggerBase): """ Python Standard Library version of `structlog.BoundLogger`. Works exactly like the generic one except that it takes advantage of knowing the logging methods in advance. Use it like:: structlog.configure( wrapper_class=structlog.stdlib.BoundLogger, ) It also contains a bunch of properties that pass-through to the wrapped `logging.Logger` which should make it work as a drop-in replacement. .. versionadded:: 23.1.0 Async variants `alog()`, `adebug()`, `ainfo()`, and so forth. .. versionchanged:: 24.2.0 Callsite parameters are now also collected by `structlog.processors.CallsiteParameterAdder` for async log methods. """ _logger: logging.Logger def bind(self, **new_values: Any) -> BoundLogger: """ Return a new logger with *new_values* added to the existing ones. """ return super().bind(**new_values) # type: ignore[return-value] def unbind(self, *keys: str) -> BoundLogger: """ Return a new logger with *keys* removed from the context. Raises: KeyError: If the key is not part of the context. """ return super().unbind(*keys) # type: ignore[return-value] def try_unbind(self, *keys: str) -> BoundLogger: """ Like :meth:`unbind`, but best effort: missing keys are ignored. .. versionadded:: 18.2.0 """ return super().try_unbind(*keys) # type: ignore[return-value] def new(self, **new_values: Any) -> BoundLogger: """ Clear context and binds *initial_values* using `bind`. Only necessary with dict implementations that keep global state like those wrapped by `structlog.threadlocal.wrap_dict` when threads are re-used. """ return super().new(**new_values) # type: ignore[return-value] def debug(self, event: str | None = None, *args: Any, **kw: Any) -> Any: """ Process event and call `logging.Logger.debug` with the result. """ return self._proxy_to_logger("debug", event, *args, **kw) def info(self, event: str | None = None, *args: Any, **kw: Any) -> Any: """ Process event and call `logging.Logger.info` with the result. """ return self._proxy_to_logger("info", event, *args, **kw) def warning(self, event: str | None = None, *args: Any, **kw: Any) -> Any: """ Process event and call `logging.Logger.warning` with the result. """ return self._proxy_to_logger("warning", event, *args, **kw) warn = warning def error(self, event: str | None = None, *args: Any, **kw: Any) -> Any: """ Process event and call `logging.Logger.error` with the result. """ return self._proxy_to_logger("error", event, *args, **kw) def critical(self, event: str | None = None, *args: Any, **kw: Any) -> Any: """ Process event and call `logging.Logger.critical` with the result. """ return self._proxy_to_logger("critical", event, *args, **kw) def exception( self, event: str | None = None, *args: Any, **kw: Any ) -> Any: """ Process event and call `logging.Logger.exception` with the result, after setting ``exc_info`` to `True` if it's not already set. """ kw.setdefault("exc_info", True) return self._proxy_to_logger("exception", event, *args, **kw) def log( self, level: int, event: str | None = None, *args: Any, **kw: Any ) -> Any: """ Process *event* and call the appropriate logging method depending on *level*. """ return self._proxy_to_logger(LEVEL_TO_NAME[level], event, *args, **kw) fatal = critical def _proxy_to_logger( self, method_name: str, event: str | None = None, *event_args: str, **event_kw: Any, ) -> Any: """ Propagate a method call to the wrapped logger. This is the same as the superclass implementation, except that it also preserves positional arguments in the ``event_dict`` so that the stdlib's support for format strings can be used. """ if event_args: event_kw["positional_args"] = event_args return super()._proxy_to_logger(method_name, event=event, **event_kw) # Pass-through attributes and methods to mimic the stdlib's logger # interface. @property def name(self) -> str: """ Returns :attr:`logging.Logger.name` """ return self._logger.name @property def level(self) -> int: """ Returns :attr:`logging.Logger.level` """ return self._logger.level @property def parent(self) -> Any: """ Returns :attr:`logging.Logger.parent` """ return self._logger.parent @property def propagate(self) -> bool: """ Returns :attr:`logging.Logger.propagate` """ return self._logger.propagate @property def handlers(self) -> Any: """ Returns :attr:`logging.Logger.handlers` """ return self._logger.handlers @property def disabled(self) -> int: """ Returns :attr:`logging.Logger.disabled` """ return self._logger.disabled def setLevel(self, level: int) -> None: """ Calls :meth:`logging.Logger.setLevel` with unmodified arguments. """ self._logger.setLevel(level) def findCaller( self, stack_info: bool = False ) -> tuple[str, int, str, str | None]: """ Calls :meth:`logging.Logger.findCaller` with unmodified arguments. """ return self._logger.findCaller(stack_info=stack_info) def makeRecord( self, name: str, level: int, fn: str, lno: int, msg: str, args: tuple[Any, ...], exc_info: ExcInfo, func: str | None = None, extra: Any = None, ) -> logging.LogRecord: """ Calls :meth:`logging.Logger.makeRecord` with unmodified arguments. """ return self._logger.makeRecord( name, level, fn, lno, msg, args, exc_info, func=func, extra=extra ) def handle(self, record: logging.LogRecord) -> None: """ Calls :meth:`logging.Logger.handle` with unmodified arguments. """ self._logger.handle(record) def addHandler(self, hdlr: logging.Handler) -> None: """ Calls :meth:`logging.Logger.addHandler` with unmodified arguments. """ self._logger.addHandler(hdlr) def removeHandler(self, hdlr: logging.Handler) -> None: """ Calls :meth:`logging.Logger.removeHandler` with unmodified arguments. """ self._logger.removeHandler(hdlr) def hasHandlers(self) -> bool: """ Calls :meth:`logging.Logger.hasHandlers` with unmodified arguments. Exists only in Python 3. """ return self._logger.hasHandlers() def callHandlers(self, record: logging.LogRecord) -> None: """ Calls :meth:`logging.Logger.callHandlers` with unmodified arguments. """ self._logger.callHandlers(record) def getEffectiveLevel(self) -> int: """ Calls :meth:`logging.Logger.getEffectiveLevel` with unmodified arguments. """ return self._logger.getEffectiveLevel() def isEnabledFor(self, level: int) -> bool: """ Calls :meth:`logging.Logger.isEnabledFor` with unmodified arguments. """ return self._logger.isEnabledFor(level) def getChild(self, suffix: str) -> logging.Logger: """ Calls :meth:`logging.Logger.getChild` with unmodified arguments. """ return self._logger.getChild(suffix) # Non-Standard Async async def _dispatch_to_sync( self, meth: Callable[..., Any], event: str, args: tuple[Any, ...], kw: dict[str, Any], ) -> None: """ Merge contextvars and log using the sync logger in a thread pool. """ scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back.f_back) # type: ignore[union-attr, arg-type, unused-ignore] ctx = contextvars.copy_context() try: await asyncio.get_running_loop().run_in_executor( None, lambda: ctx.run(lambda: meth(event, *args, **kw)), ) finally: _ASYNC_CALLING_STACK.reset(scs_token) async def adebug(self, event: str, *args: Any, **kw: Any) -> None: """ Log using `debug()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ await self._dispatch_to_sync(self.debug, event, args, kw) async def ainfo(self, event: str, *args: Any, **kw: Any) -> None: """ Log using `info()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ await self._dispatch_to_sync(self.info, event, args, kw) async def awarning(self, event: str, *args: Any, **kw: Any) -> None: """ Log using `warning()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ await self._dispatch_to_sync(self.warning, event, args, kw) async def aerror(self, event: str, *args: Any, **kw: Any) -> None: """ Log using `error()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ await self._dispatch_to_sync(self.error, event, args, kw) async def acritical(self, event: str, *args: Any, **kw: Any) -> None: """ Log using `critical()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ await self._dispatch_to_sync(self.critical, event, args, kw) afatal = acritical async def aexception(self, event: str, *args: Any, **kw: Any) -> None: """ Log using `exception()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ # To make `log.exception("foo") work, we have to check if the user # passed an explicit exc_info and if not, supply our own. if kw.get("exc_info", True) is True and kw.get("exception") is None: kw["exc_info"] = sys.exc_info() await self._dispatch_to_sync(self.exception, event, args, kw) async def alog( self, level: Any, event: str, *args: Any, **kw: Any ) -> None: """ Log using `log()`, but asynchronously in a separate thread. .. versionadded:: 23.1.0 """ await self._dispatch_to_sync(partial(self.log, level), event, args, kw) def get_logger(*args: Any, **initial_values: Any) -> BoundLogger: """ Only calls `structlog.get_logger`, but has the correct type hints. .. warning:: Does **not** check whether -- or ensure that -- you've configured *structlog* for standard library :mod:`logging`! See :doc:`standard-library` for details. .. versionadded:: 20.2.0 """ return _config.get_logger(*args, **initial_values) class AsyncBoundLogger: """ Wraps a `BoundLogger` & exposes its logging methods as ``async`` versions. Instead of blocking the program, they are run asynchronously in a thread pool executor. This means more computational overhead per log call. But it also means that the processor chain (e.g. JSON serialization) and I/O won't block your whole application. Only available for Python 3.7 and later. .. versionadded:: 20.2.0 .. versionchanged:: 20.2.0 fix _dispatch_to_sync contextvars usage .. deprecated:: 23.1.0 Use the regular `BoundLogger` with its a-prefixed methods instead. .. versionchanged:: 23.3.0 Callsite parameters are now also collected for async log methods. """ __slots__ = ("sync_bl", "_loop") #: The wrapped synchronous logger. It is useful to be able to log #: synchronously occasionally. sync_bl: BoundLogger # Blatant lie, we use a property for _context. Need this for Protocol # though. _context: Context _executor = None _bound_logger_factory = BoundLogger def __init__( self, logger: logging.Logger, processors: Iterable[Processor], context: Context, *, # Only as an optimization for binding! _sync_bl: Any = None, # *vroom vroom* over purity. _loop: Any = None, ): if _sync_bl: self.sync_bl = _sync_bl self._loop = _loop return self.sync_bl = self._bound_logger_factory( logger=logger, processors=processors, context=context ) self._loop = asyncio.get_running_loop() # We have to ignore the type because we've already declared it to ensure # we're a BindableLogger. # Instances would've been correctly recognized as such, however the class # not and we need the class in `structlog.configure()`. @property # type: ignore[no-redef] def _context(self) -> Context: return self.sync_bl._context def bind(self, **new_values: Any) -> AsyncBoundLogger: return AsyncBoundLogger( # logger, processors and context are within sync_bl. These # arguments are ignored if _sync_bl is passed. *vroom vroom* over # purity. logger=None, # type: ignore[arg-type] processors=(), context={}, _sync_bl=self.sync_bl.bind(**new_values), _loop=self._loop, ) def new(self, **new_values: Any) -> AsyncBoundLogger: return AsyncBoundLogger( # c.f. comment in bind logger=None, # type: ignore[arg-type] processors=(), context={}, _sync_bl=self.sync_bl.new(**new_values), _loop=self._loop, ) def unbind(self, *keys: str) -> AsyncBoundLogger: return AsyncBoundLogger( # c.f. comment in bind logger=None, # type: ignore[arg-type] processors=(), context={}, _sync_bl=self.sync_bl.unbind(*keys), _loop=self._loop, ) def try_unbind(self, *keys: str) -> AsyncBoundLogger: return AsyncBoundLogger( # c.f. comment in bind logger=None, # type: ignore[arg-type] processors=(), context={}, _sync_bl=self.sync_bl.try_unbind(*keys), _loop=self._loop, ) async def _dispatch_to_sync( self, meth: Callable[..., Any], event: str, args: tuple[Any, ...], kw: dict[str, Any], ) -> None: """ Merge contextvars and log using the sync logger in a thread pool. """ scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back.f_back) # type: ignore[union-attr, arg-type, unused-ignore] ctx = contextvars.copy_context() try: await asyncio.get_running_loop().run_in_executor( self._executor, lambda: ctx.run(lambda: meth(event, *args, **kw)), ) finally: _ASYNC_CALLING_STACK.reset(scs_token) async def debug(self, event: str, *args: Any, **kw: Any) -> None: await self._dispatch_to_sync(self.sync_bl.debug, event, args, kw) async def info(self, event: str, *args: Any, **kw: Any) -> None: await self._dispatch_to_sync(self.sync_bl.info, event, args, kw) async def warning(self, event: str, *args: Any, **kw: Any) -> None: await self._dispatch_to_sync(self.sync_bl.warning, event, args, kw) warn = warning async def error(self, event: str, *args: Any, **kw: Any) -> None: await self._dispatch_to_sync(self.sync_bl.error, event, args, kw) async def critical(self, event: str, *args: Any, **kw: Any) -> None: await self._dispatch_to_sync(self.sync_bl.critical, event, args, kw) fatal = critical async def exception(self, event: str, *args: Any, **kw: Any) -> None: # To make `log.exception("foo") work, we have to check if the user # passed an explicit exc_info and if not, supply our own. ei = kw.pop("exc_info", None) if ei is None and kw.get("exception") is None: ei = sys.exc_info() kw["exc_info"] = ei await self._dispatch_to_sync(self.sync_bl.exception, event, args, kw) async def log(self, level: Any, event: str, *args: Any, **kw: Any) -> None: await self._dispatch_to_sync( partial(self.sync_bl.log, level), event, args, kw ) class LoggerFactory: """ Build a standard library logger when an *instance* is called. Sets a custom logger using :func:`logging.setLoggerClass` so variables in log format are expanded properly. >>> from structlog import configure >>> from structlog.stdlib import LoggerFactory >>> configure(logger_factory=LoggerFactory()) Args: ignore_frame_names: When guessing the name of a logger, skip frames whose names *start* with one of these. For example, in pyramid applications you'll want to set it to ``["venusian", "pyramid.config"]``. This argument is called *additional_ignores* in other APIs throughout *structlog*. """ def __init__(self, ignore_frame_names: list[str] | None = None): self._ignore = ignore_frame_names logging.setLoggerClass(_FixedFindCallerLogger) def __call__(self, *args: Any) -> logging.Logger: """ Deduce the caller's module name and create a stdlib logger. If an optional argument is passed, it will be used as the logger name instead of guesswork. This optional argument would be passed from the :func:`structlog.get_logger` call. For example ``structlog.get_logger("foo")`` would cause this method to be called with ``"foo"`` as its first positional argument. .. versionchanged:: 0.4.0 Added support for optional positional arguments. Using the first one for naming the constructed logger. """ if args: return logging.getLogger(args[0]) # We skip all frames that originate from within structlog or one of the # configured names. _, name = _find_first_app_frame_and_name(self._ignore) return logging.getLogger(name) class PositionalArgumentsFormatter: """ Apply stdlib-like string formatting to the ``event`` key. If the ``positional_args`` key in the event dict is set, it must contain a tuple that is used for formatting (using the ``%s`` string formatting operator) of the value from the ``event`` key. This works in the same way as the stdlib handles arguments to the various log methods: if the tuple contains only a single `dict` argument it is used for keyword placeholders in the ``event`` string, otherwise it will be used for positional placeholders. ``positional_args`` is populated by `structlog.stdlib.BoundLogger` or can be set manually. The *remove_positional_args* flag can be set to `False` to keep the ``positional_args`` key in the event dict; by default it will be removed from the event dict after formatting a message. """ def __init__(self, remove_positional_args: bool = True) -> None: self.remove_positional_args = remove_positional_args def __call__( self, _: WrappedLogger, __: str, event_dict: EventDict ) -> EventDict: args = event_dict.get("positional_args") # Mimic the formatting behaviour of the stdlib's logging module, which # accepts both positional arguments and a single dict argument. The # "single dict" check is the same one as the stdlib's logging module # performs in LogRecord.__init__(). if args: if len(args) == 1 and isinstance(args[0], dict) and args[0]: args = args[0] event_dict["event"] %= args if self.remove_positional_args and args is not None: del event_dict["positional_args"] return event_dict def filter_by_level( logger: logging.Logger, method_name: str, event_dict: EventDict ) -> EventDict: """ Check whether logging is configured to accept messages from this log level. Should be the first processor if stdlib's filtering by level is used so possibly expensive processors like exception formatters are avoided in the first place. >>> import logging >>> from structlog.stdlib import filter_by_level >>> logging.basicConfig(level=logging.WARN) >>> logger = logging.getLogger() >>> filter_by_level(logger, 'warn', {}) {} >>> filter_by_level(logger, 'debug', {}) Traceback (most recent call last): ... DropEvent """ if logger.isEnabledFor(NAME_TO_LEVEL[method_name]): return event_dict raise DropEvent def add_log_level_number( logger: logging.Logger, method_name: str, event_dict: EventDict ) -> EventDict: """ Add the log level number to the event dict. Log level numbers map to the log level names. The Python stdlib uses them for filtering logic. This adds the same numbers so users can leverage similar filtering. Compare:: level in ("warning", "error", "critical") level_number >= 30 The mapping of names to numbers is in ``structlog.stdlib._log_levels._NAME_TO_LEVEL``. .. versionadded:: 18.2.0 """ event_dict["level_number"] = NAME_TO_LEVEL[method_name] return event_dict def add_logger_name( logger: logging.Logger, method_name: str, event_dict: EventDict ) -> EventDict: """ Add the logger name to the event dict. """ record = event_dict.get("_record") if record is None: event_dict["logger"] = logger.name else: event_dict["logger"] = record.name return event_dict _LOG_RECORD_KEYS = logging.LogRecord( "name", 0, "pathname", 0, "msg", (), None ).__dict__.keys() class ExtraAdder: """ Add extra attributes of `logging.LogRecord` objects to the event dictionary. This processor can be used for adding data passed in the ``extra`` parameter of the `logging` module's log methods to the event dictionary. Args: allow: An optional collection of attributes that, if present in `logging.LogRecord` objects, will be copied to event dictionaries. If ``allow`` is None all attributes of `logging.LogRecord` objects that do not exist on a standard `logging.LogRecord` object will be copied to event dictionaries. .. versionadded:: 21.5.0 """ __slots__ = ("_copier",) def __init__(self, allow: Collection[str] | None = None) -> None: self._copier: Callable[[EventDict, logging.LogRecord], None] if allow is not None: # The contents of allow is copied to a new list so that changes to # the list passed into the constructor does not change the # behaviour of this processor. self._copier = functools.partial(self._copy_allowed, [*allow]) else: self._copier = self._copy_all def __call__( self, logger: logging.Logger, name: str, event_dict: EventDict ) -> EventDict: record: logging.LogRecord | None = event_dict.get("_record") if record is not None: self._copier(event_dict, record) return event_dict @classmethod def _copy_all( cls, event_dict: EventDict, record: logging.LogRecord ) -> None: for key, value in record.__dict__.items(): if key not in _LOG_RECORD_KEYS: event_dict[key] = value @classmethod def _copy_allowed( cls, allow: Collection[str], event_dict: EventDict, record: logging.LogRecord, ) -> None: for key in allow: if key in record.__dict__: event_dict[key] = record.__dict__[key] def render_to_log_kwargs( _: logging.Logger, __: str, event_dict: EventDict ) -> EventDict: """ Render ``event_dict`` into keyword arguments for `logging.log`. See `logging.Logger`'s ``_log`` method for kwargs reference. The ``event`` field is translated into ``msg`` and the rest of the *event_dict* is added as ``extra``. This allows you to defer formatting to `logging`. .. versionadded:: 17.1.0 .. versionchanged:: 22.1.0 ``exc_info``, ``stack_info``, and ``stacklevel`` are passed as proper kwargs and not put into ``extra``. .. versionchanged:: 24.2.0 ``stackLevel`` corrected to ``stacklevel``. """ return { "msg": event_dict.pop("event"), "extra": event_dict, **{ kw: event_dict.pop(kw) for kw in ("exc_info", "stack_info", "stacklevel") if kw in event_dict }, } class ProcessorFormatter(logging.Formatter): r""" Call *structlog* processors on `logging.LogRecord`\s. This is an implementation of a `logging.Formatter` that can be used to format log entries from both *structlog* and `logging`. Its static method `wrap_for_formatter` must be the final processor in *structlog*'s processor chain. Please refer to :ref:`processor-formatter` for examples. Args: foreign_pre_chain: If not `None`, it is used as a processor chain that is applied to **non**-*structlog* log entries before the event dictionary is passed to *processors*. (default: `None`) processors: A chain of *structlog* processors that is used to process **all** log entries. The last one must render to a `str` which then gets passed on to `logging` for output. Compared to *structlog*'s regular processor chains, there's a few differences: - The event dictionary contains two additional keys: #. ``_record``: a `logging.LogRecord` that either was created using `logging` APIs, **or** is a wrapped *structlog* log entry created by `wrap_for_formatter`. #. ``_from_structlog``: a `bool` that indicates whether or not ``_record`` was created by a *structlog* logger. Since you most likely don't want ``_record`` and ``_from_structlog`` in your log files, we've added the static method `remove_processors_meta` to ``ProcessorFormatter`` that you can add just before your renderer. - Since this is a `logging` *formatter*, raising `structlog.DropEvent` will crash your application. keep_exc_info: ``exc_info`` on `logging.LogRecord`\ s is added to the ``event_dict`` and removed afterwards. Set this to ``True`` to keep it on the `logging.LogRecord`. (default: False) keep_stack_info: Same as *keep_exc_info* except for ``stack_info``. (default: False) logger: Logger which we want to push through the *structlog* processor chain. This parameter is necessary for some of the processors like `filter_by_level`. (default: None) pass_foreign_args: If True, pass a foreign log record's ``args`` attribute to the ``event_dict`` under ``positional_args`` key. (default: False) processor: A single *structlog* processor used for rendering the event dictionary before passing it off to `logging`. Must return a `str`. The event dictionary does **not** contain ``_record`` and ``_from_structlog``. This parameter exists for historic reasons. Please use *processors* instead. use_get_message: If True, use ``record.getMessage`` to get a fully rendered log message, otherwise use ``str(record.msg)``. (default: True) Raises: TypeError: If both or neither *processor* and *processors* are passed. .. versionadded:: 17.1.0 .. versionadded:: 17.2.0 *keep_exc_info* and *keep_stack_info* .. versionadded:: 19.2.0 *logger* .. versionadded:: 19.2.0 *pass_foreign_args* .. versionadded:: 21.3.0 *processors* .. deprecated:: 21.3.0 *processor* (singular) in favor of *processors* (plural). Removal is not planned. .. versionadded:: 23.3.0 *use_get_message* """ def __init__( self, processor: Processor | None = None, processors: Sequence[Processor] | None = (), foreign_pre_chain: Sequence[Processor] | None = None, keep_exc_info: bool = False, keep_stack_info: bool = False, logger: logging.Logger | None = None, pass_foreign_args: bool = False, use_get_message: bool = True, *args: Any, **kwargs: Any, ) -> None: fmt = kwargs.pop("fmt", "%(message)s") super().__init__(*args, fmt=fmt, **kwargs) # type: ignore[misc] if processor and processors: msg = ( "The `processor` and `processors` arguments are mutually" " exclusive." ) raise TypeError(msg) self.processors: Sequence[Processor] if processor is not None: self.processors = (self.remove_processors_meta, processor) elif processors: self.processors = processors else: msg = "Either `processor` or `processors` must be passed." raise TypeError(msg) self.foreign_pre_chain = foreign_pre_chain self.keep_exc_info = keep_exc_info self.keep_stack_info = keep_stack_info self.logger = logger self.pass_foreign_args = pass_foreign_args self.use_get_message = use_get_message def format(self, record: logging.LogRecord) -> str: """ Extract *structlog*'s `event_dict` from ``record.msg`` and format it. *record* has been patched by `wrap_for_formatter` first though, so the type isn't quite right. """ # Make a shallow copy of the record to let other handlers/formatters # process the original one record = logging.makeLogRecord(record.__dict__) logger = getattr(record, "_logger", _SENTINEL) meth_name = getattr(record, "_name", "__structlog_sentinel__") ed: ProcessorReturnValue if logger is not _SENTINEL and meth_name != "__structlog_sentinel__": # Both attached by wrap_for_formatter if self.logger is not None: logger = self.logger meth_name = cast(str, record._name) # type:ignore[attr-defined] # We need to copy because it's possible that the same record gets # processed by multiple logging formatters. LogRecord.getMessage # would transform our dict into a str. ed = cast(Dict[str, Any], record.msg).copy() ed["_record"] = record ed["_from_structlog"] = True else: logger = self.logger meth_name = record.levelname.lower() ed = { "event": ( record.getMessage() if self.use_get_message else str(record.msg) ), "_record": record, "_from_structlog": False, } if self.pass_foreign_args: ed["positional_args"] = record.args record.args = () # Add stack-related attributes to the event dict if record.exc_info: ed["exc_info"] = record.exc_info if record.stack_info: ed["stack_info"] = record.stack_info # Non-structlog allows to run through a chain to prepare it for the # final processor (e.g. adding timestamps and log levels). for proc in self.foreign_pre_chain or (): ed = cast(EventDict, proc(logger, meth_name, ed)) # If required, unset stack-related attributes on the record copy so # that the base implementation doesn't append stacktraces to the # output. if not self.keep_exc_info: record.exc_text = None record.exc_info = None if not self.keep_stack_info: record.stack_info = None for p in self.processors: ed = p(logger, meth_name, cast(EventDict, ed)) if not isinstance(ed, str): warnings.warn( "The last processor in ProcessorFormatter.processors must " f"return a string, but {self.processors[-1]} returned a " f"{type(ed)} instead.", category=RuntimeWarning, stacklevel=1, ) ed = cast(str, ed) record.msg = ed return super().format(record) @staticmethod def wrap_for_formatter( logger: logging.Logger, name: str, event_dict: EventDict ) -> tuple[tuple[EventDict], dict[str, dict[str, Any]]]: """ Wrap *logger*, *name*, and *event_dict*. The result is later unpacked by `ProcessorFormatter` when formatting log entries. Use this static method as the renderer (in other words, final processor) if you want to use `ProcessorFormatter` in your `logging` configuration. """ return (event_dict,), {"extra": {"_logger": logger, "_name": name}} @staticmethod def remove_processors_meta( _: WrappedLogger, __: str, event_dict: EventDict ) -> EventDict: """ Remove ``_record`` and ``_from_structlog`` from *event_dict*. These keys are added to the event dictionary, before `ProcessorFormatter`'s *processors* are run. .. versionadded:: 21.3.0 """ del event_dict["_record"] del event_dict["_from_structlog"] return event_dict structlog-24.4.0/src/structlog/testing.py0000644000000000000000000001244614645734712015445 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Helpers to test your application's logging behavior. .. versionadded:: 20.1.0 See :doc:`testing`. """ from __future__ import annotations from contextlib import contextmanager from typing import Any, Generator, NamedTuple, NoReturn from ._config import configure, get_config from ._log_levels import map_method_name from .exceptions import DropEvent from .typing import EventDict, WrappedLogger __all__ = [ "CapturedCall", "CapturingLogger", "CapturingLoggerFactory", "LogCapture", "ReturnLogger", "ReturnLoggerFactory", "capture_logs", ] class LogCapture: """ Class for capturing log messages in its entries list. Generally you should use `structlog.testing.capture_logs`, but you can use this class if you want to capture logs with other patterns. :ivar List[structlog.typing.EventDict] entries: The captured log entries. .. versionadded:: 20.1.0 .. versionchanged:: 24.3.0 Added mapping from "exception" to "error" Added mapping from "warn" to "warning" """ entries: list[EventDict] def __init__(self) -> None: self.entries = [] def __call__( self, _: WrappedLogger, method_name: str, event_dict: EventDict ) -> NoReturn: event_dict["log_level"] = map_method_name(method_name) self.entries.append(event_dict) raise DropEvent @contextmanager def capture_logs() -> Generator[list[EventDict], None, None]: """ Context manager that appends all logging statements to its yielded list while it is active. Disables all configured processors for the duration of the context manager. Attention: this is **not** thread-safe! .. versionadded:: 20.1.0 """ cap = LogCapture() # Modify `_Configuration.default_processors` set via `configure` but always # keep the list instance intact to not break references held by bound # loggers. processors = get_config()["processors"] old_processors = processors.copy() try: # clear processors list and use LogCapture for testing processors.clear() processors.append(cap) configure(processors=processors) yield cap.entries finally: # remove LogCapture and restore original processors processors.clear() processors.extend(old_processors) configure(processors=processors) class ReturnLogger: """ Return the arguments that it's called with. >>> from structlog import ReturnLogger >>> ReturnLogger().info("hello") 'hello' >>> ReturnLogger().info("hello", when="again") (('hello',), {'when': 'again'}) .. versionchanged:: 0.3.0 Allow for arbitrary arguments and keyword arguments to be passed in. """ def msg(self, *args: Any, **kw: Any) -> Any: """ Return tuple of ``args, kw`` or just ``args[0]`` if only one arg passed """ # Slightly convoluted for backwards compatibility. if len(args) == 1 and not kw: return args[0] return args, kw log = debug = info = warn = warning = msg fatal = failure = err = error = critical = exception = msg class ReturnLoggerFactory: r""" Produce and cache `ReturnLogger`\ s. To be used with `structlog.configure`\ 's *logger_factory*. Positional arguments are silently ignored. .. versionadded:: 0.4.0 """ def __init__(self) -> None: self._logger = ReturnLogger() def __call__(self, *args: Any) -> ReturnLogger: return self._logger class CapturedCall(NamedTuple): """ A call as captured by `CapturingLogger`. Can also be unpacked like a tuple. Args: method_name: The method name that got called. args: A tuple of the positional arguments. kwargs: A dict of the keyword arguments. .. versionadded:: 20.2.0 """ method_name: str args: tuple[Any, ...] kwargs: dict[str, Any] class CapturingLogger: """ Store the method calls that it's been called with. This is nicer than `ReturnLogger` for unit tests because the bound logger doesn't have to cooperate. **Any** method name is supported. .. versionadded:: 20.2.0 """ calls: list[CapturedCall] def __init__(self) -> None: self.calls = [] def __repr__(self) -> str: return f"" def __getattr__(self, name: str) -> Any: """ Capture call to `calls` """ def log(*args: Any, **kw: Any) -> None: self.calls.append(CapturedCall(name, args, kw)) return log class CapturingLoggerFactory: r""" Produce and cache `CapturingLogger`\ s. Each factory produces and re-uses only **one** logger. You can access it via the ``logger`` attribute. To be used with `structlog.configure`\ 's *logger_factory*. Positional arguments are silently ignored. .. versionadded:: 20.2.0 """ logger: CapturingLogger def __init__(self) -> None: self.logger = CapturingLogger() def __call__(self, *args: Any) -> CapturingLogger: return self.logger structlog-24.4.0/src/structlog/threadlocal.py0000644000000000000000000002174114645734712016250 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ **Deprecated** primitives to keep context global but thread (and greenlet) local. See `thread-local`, but please use :doc:`contextvars` instead. .. deprecated:: 22.1.0 """ from __future__ import annotations import contextlib import sys import threading import uuid import warnings from typing import Any, Generator, Iterator, TypeVar import structlog from ._config import BoundLoggerLazyProxy from .typing import BindableLogger, Context, EventDict, WrappedLogger def _determine_threadlocal() -> type[Any]: """ Return a dict-like threadlocal storage depending on whether we run with greenlets or not. """ try: from ._greenlets import GreenThreadLocal except ImportError: from threading import local return local return GreenThreadLocal # pragma: no cover ThreadLocal = _determine_threadlocal() def _deprecated() -> None: """ Raise a warning with best-effort stacklevel adjustment. """ callsite = "" with contextlib.suppress(Exception): f = sys._getframe() callsite = f.f_back.f_back.f_globals[ # type: ignore[union-attr] "__name__" ] # Avoid double warnings if TL functions call themselves. if callsite == "structlog.threadlocal": return stacklevel = 3 # If a function is used as a decorator, we need to add two stack levels. # This logic will probably break eventually, but it's not worth any more # complexity. if callsite == "contextlib": stacklevel += 2 warnings.warn( "`structlog.threadlocal` is deprecated, please use " "`structlog.contextvars` instead.", DeprecationWarning, stacklevel=stacklevel, ) def wrap_dict(dict_class: type[Context]) -> type[Context]: """ Wrap a dict-like class and return the resulting class. The wrapped class and used to keep global in the current thread. Args: dict_class: Class used for keeping context. .. deprecated:: 22.1.0 """ _deprecated() Wrapped = type( "WrappedDict-" + str(uuid.uuid4()), (_ThreadLocalDictWrapper,), {} ) Wrapped._tl = ThreadLocal() # type: ignore[attr-defined] Wrapped._dict_class = dict_class # type: ignore[attr-defined] return Wrapped TLLogger = TypeVar("TLLogger", bound=BindableLogger) def as_immutable(logger: TLLogger) -> TLLogger: """ Extract the context from a thread local logger into an immutable logger. Args: logger (structlog.typing.BindableLogger): A logger with *possibly* thread local state. Returns: :class:`~structlog.BoundLogger` with an immutable context. .. deprecated:: 22.1.0 """ _deprecated() if isinstance(logger, BoundLoggerLazyProxy): logger = logger.bind() try: ctx = logger._context._tl.dict_.__class__( # type: ignore[union-attr] logger._context._dict # type: ignore[union-attr] ) bl = logger.__class__( logger._logger, # type: ignore[attr-defined, call-arg] processors=logger._processors, # type: ignore[attr-defined] context={}, ) bl._context = ctx return bl except AttributeError: return logger @contextlib.contextmanager def tmp_bind( logger: TLLogger, **tmp_values: Any ) -> Generator[TLLogger, None, None]: """ Bind *tmp_values* to *logger* & memorize current state. Rewind afterwards. Only works with `structlog.threadlocal.wrap_dict`-based contexts. Use :func:`~structlog.threadlocal.bound_threadlocal` for new code. .. deprecated:: 22.1.0 """ _deprecated() if isinstance(logger, BoundLoggerLazyProxy): logger = logger.bind() saved = as_immutable(logger)._context try: yield logger.bind(**tmp_values) # type: ignore[misc] finally: logger._context.clear() logger._context.update(saved) class _ThreadLocalDictWrapper: """ Wrap a dict-like class and keep the state *global* but *thread-local*. Attempts to re-initialize only updates the wrapped dictionary. Useful for short-lived threaded applications like requests in web app. Use :func:`wrap` to instantiate and use :func:`structlog.BoundLogger.new` to clear the context. """ _tl: Any _dict_class: type[dict[str, Any]] def __init__(self, *args: Any, **kw: Any) -> None: """ We cheat. A context dict gets never recreated. """ if args and isinstance(args[0], self.__class__): # our state is global, no need to look at args[0] if it's of our # class self._dict.update(**kw) else: self._dict.update(*args, **kw) @property def _dict(self) -> Context: """ Return or create and return the current context. """ try: return self.__class__._tl.dict_ except AttributeError: self.__class__._tl.dict_ = self.__class__._dict_class() return self.__class__._tl.dict_ def __repr__(self) -> str: return f"<{self.__class__.__name__}({self._dict!r})>" def __eq__(self, other: object) -> bool: # Same class == same dictionary return self.__class__ == other.__class__ def __ne__(self, other: object) -> bool: return not self.__eq__(other) # Proxy methods necessary for structlog. # Dunder methods don't trigger __getattr__ so we need to proxy by hand. def __iter__(self) -> Iterator[str]: return self._dict.__iter__() def __setitem__(self, key: str, value: Any) -> None: self._dict[key] = value def __delitem__(self, key: str) -> None: self._dict.__delitem__(key) def __len__(self) -> int: return self._dict.__len__() def __getattr__(self, name: str) -> Any: return getattr(self._dict, name) _CONTEXT = threading.local() def get_threadlocal() -> Context: """ Return a copy of the current thread-local context. .. versionadded:: 21.2.0 .. deprecated:: 22.1.0 """ _deprecated() return _get_context().copy() def get_merged_threadlocal(bound_logger: BindableLogger) -> Context: """ Return a copy of the current thread-local context merged with the context from *bound_logger*. .. versionadded:: 21.2.0 .. deprecated:: 22.1.0 """ _deprecated() ctx = _get_context().copy() ctx.update(structlog.get_context(bound_logger)) return ctx def merge_threadlocal( logger: WrappedLogger, method_name: str, event_dict: EventDict ) -> EventDict: """ A processor that merges in a global (thread-local) context. Use this as your first processor in :func:`structlog.configure` to ensure thread-local context is included in all log calls. .. versionadded:: 19.2.0 .. versionchanged:: 20.1.0 This function used to be called ``merge_threadlocal_context`` and that name is still kept around for backward compatibility. .. deprecated:: 22.1.0 """ _deprecated() context = _get_context().copy() context.update(event_dict) return context # Alias that shouldn't be used anymore. merge_threadlocal_context = merge_threadlocal def clear_threadlocal() -> None: """ Clear the thread-local context. The typical use-case for this function is to invoke it early in request-handling code. .. versionadded:: 19.2.0 .. deprecated:: 22.1.0 """ _deprecated() _CONTEXT.context = {} def bind_threadlocal(**kw: Any) -> None: """ Put keys and values into the thread-local context. Use this instead of :func:`~structlog.BoundLogger.bind` when you want some context to be global (thread-local). .. versionadded:: 19.2.0 .. deprecated:: 22.1.0 """ _deprecated() _get_context().update(kw) def unbind_threadlocal(*keys: str) -> None: """ Tries to remove bound *keys* from threadlocal logging context if present. .. versionadded:: 20.1.0 .. deprecated:: 22.1.0 """ _deprecated() context = _get_context() for key in keys: context.pop(key, None) @contextlib.contextmanager def bound_threadlocal(**kw: Any) -> Generator[None, None, None]: """ Bind *kw* to the current thread-local context. Unbind or restore *kw* afterwards. Do **not** affect other keys. Can be used as a context manager or decorator. .. versionadded:: 21.4.0 .. deprecated:: 22.1.0 """ _deprecated() context = get_threadlocal() saved = {k: context[k] for k in context.keys() & kw.keys()} bind_threadlocal(**kw) try: yield finally: unbind_threadlocal(*kw.keys()) bind_threadlocal(**saved) def _get_context() -> Context: try: return _CONTEXT.context except AttributeError: _CONTEXT.context = {} return _CONTEXT.context structlog-24.4.0/src/structlog/tracebacks.py0000644000000000000000000003401214645734712016063 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Extract a structured traceback from an exception. Based on work by Will McGugan `_ from `rich.traceback `_. """ from __future__ import annotations import os import os.path from dataclasses import asdict, dataclass, field from traceback import walk_tb from types import ModuleType, TracebackType from typing import Any, Iterable, Sequence, Tuple, Union try: import rich import rich.pretty except ImportError: rich = None # type: ignore[assignment] from .typing import ExcInfo __all__ = [ "ExceptionDictTransformer", "Frame", "Stack", "SyntaxError_", "Trace", "extract", "safe_str", "to_repr", ] SHOW_LOCALS = True LOCALS_MAX_LENGTH = 10 LOCALS_MAX_STRING = 80 MAX_FRAMES = 50 OptExcInfo = Union[ExcInfo, Tuple[None, None, None]] @dataclass class Frame: """ Represents a single stack frame. """ filename: str lineno: int name: str locals: dict[str, str] | None = None @dataclass class SyntaxError_: # noqa: N801 """ Contains detailed information about :exc:`SyntaxError` exceptions. """ offset: int filename: str line: str lineno: int msg: str @dataclass class Stack: """ Represents an exception and a list of stack frames. """ exc_type: str exc_value: str syntax_error: SyntaxError_ | None = None is_cause: bool = False frames: list[Frame] = field(default_factory=list) @dataclass class Trace: """ Container for a list of stack traces. """ stacks: list[Stack] def safe_str(_object: Any) -> str: """Don't allow exceptions from __str__ to propagate.""" try: return str(_object) except Exception as error: # noqa: BLE001 return f"" def to_repr( obj: Any, max_length: int | None = None, max_string: int | None = None, use_rich: bool = True, ) -> str: """ Get repr string for an object, but catch errors. :func:`repr()` is used for strings, too, so that secret wrappers that inherit from :func:`str` and overwrite ``__repr__()`` are handled correctly (i.e. secrets are not logged in plain text). Args: obj: Object to get a string representation for. max_length: Maximum length of containers before abbreviating, or ``None`` for no abbreviation. max_string: Maximum length of string before truncating, or ``None`` to disable truncating. use_rich: If ``True`` (the default), use rich_ to compute the repr. If ``False`` or if rich_ is not installed, fall back to a simpler algorithm. Returns: The string representation of *obj*. .. versionchanged:: 24.3.0 Added *max_length* argument. Use :program:`rich` to render locals if it is available. Call :func:`repr()` on strings in fallback implementation. """ if use_rich and rich is not None: # Let rich render the repr if it is available. # It produces much better results for containers and dataclasses/attrs. obj_repr = rich.pretty.traverse( obj, max_length=max_length, max_string=max_string ).render() else: # Generate a (truncated) repr if rich is not available. # Handle str/bytes differently to get better results for truncated # representations. Also catch all errors, similarly to "safe_str()". try: if isinstance(obj, (str, bytes)): if max_string is not None and len(obj) > max_string: truncated = len(obj) - max_string obj_repr = f"{obj[:max_string]!r}+{truncated}" else: obj_repr = repr(obj) else: obj_repr = repr(obj) if max_string is not None and len(obj_repr) > max_string: truncated = len(obj_repr) - max_string obj_repr = f"{obj_repr[:max_string]!r}+{truncated}" except Exception as error: # noqa: BLE001 obj_repr = f"" return obj_repr def extract( exc_type: type[BaseException], exc_value: BaseException, traceback: TracebackType | None, *, show_locals: bool = False, locals_max_length: int = LOCALS_MAX_LENGTH, locals_max_string: int = LOCALS_MAX_STRING, locals_hide_dunder: bool = True, locals_hide_sunder: bool = False, use_rich: bool = True, ) -> Trace: """ Extract traceback information. Args: exc_type: Exception type. exc_value: Exception value. traceback: Python Traceback object. show_locals: Enable display of local variables. Defaults to False. locals_max_length: Maximum length of containers before abbreviating, or ``None`` for no abbreviation. locals_max_string: Maximum length of string before truncating, or ``None`` to disable truncating. locals_hide_dunder: Hide locals prefixed with double underscore. Defaults to True. locals_hide_sunder: Hide locals prefixed with single underscore. This implies hiding *locals_hide_dunder*. Defaults to False. use_rich: If ``True`` (the default), use rich_ to compute the repr. If ``False`` or if rich_ is not installed, fall back to a simpler algorithm. Returns: A Trace instance with structured information about all exceptions. .. versionadded:: 22.1.0 .. versionchanged:: 24.3.0 Added *locals_max_length*, *locals_hide_sunder*, *locals_hide_dunder* and *use_rich* arguments. """ stacks: list[Stack] = [] is_cause = False while True: stack = Stack( exc_type=safe_str(exc_type.__name__), exc_value=safe_str(exc_value), is_cause=is_cause, ) if isinstance(exc_value, SyntaxError): stack.syntax_error = SyntaxError_( offset=exc_value.offset or 0, filename=exc_value.filename or "?", lineno=exc_value.lineno or 0, line=exc_value.text or "", msg=exc_value.msg, ) stacks.append(stack) append = stack.frames.append # pylint: disable=no-member def get_locals( iter_locals: Iterable[tuple[str, object]], ) -> Iterable[tuple[str, object]]: """Extract locals from an iterator of key pairs.""" if not (locals_hide_dunder or locals_hide_sunder): yield from iter_locals return for key, value in iter_locals: if locals_hide_dunder and key.startswith("__"): continue if locals_hide_sunder and key.startswith("_"): continue yield key, value for frame_summary, line_no in walk_tb(traceback): filename = frame_summary.f_code.co_filename if filename and not filename.startswith("<"): filename = os.path.abspath(filename) # Rich has this, but we are not rich and like to keep all frames: # if frame_summary.f_locals.get("_rich_traceback_omit", False): # continue # noqa: ERA001 frame = Frame( filename=filename or "?", lineno=line_no, name=frame_summary.f_code.co_name, locals=( { key: to_repr( value, max_length=locals_max_length, max_string=locals_max_string, use_rich=use_rich, ) for key, value in get_locals( frame_summary.f_locals.items() ) } if show_locals else None ), ) append(frame) cause = getattr(exc_value, "__cause__", None) if cause and cause.__traceback__: exc_type = cause.__class__ exc_value = cause traceback = cause.__traceback__ is_cause = True continue cause = exc_value.__context__ if ( cause and cause.__traceback__ and not getattr(exc_value, "__suppress_context__", False) ): exc_type = cause.__class__ exc_value = cause traceback = cause.__traceback__ is_cause = False continue # No cover, code is reached but coverage doesn't recognize it. break # pragma: no cover return Trace(stacks=stacks) class ExceptionDictTransformer: """ Return a list of exception stack dictionaries for an exception. These dictionaries are based on :class:`Stack` instances generated by :func:`extract()` and can be dumped to JSON. Args: show_locals: Whether or not to include the values of a stack frame's local variables. locals_max_length: Maximum length of containers before abbreviating, or ``None`` for no abbreviation. locals_max_string: Maximum length of string before truncating, or ``None`` to disable truncating. locals_hide_dunder: Hide locals prefixed with double underscore. Defaults to True. locals_hide_sunder: Hide locals prefixed with single underscore. This implies hiding *locals_hide_dunder*. Defaults to False. suppress: Optional sequence of modules or paths for which to suppress the display of locals even if *show_locals* is ``True``. max_frames: Maximum number of frames in each stack. Frames are removed from the inside out. The idea is, that the first frames represent your code responsible for the exception and last frames the code where the exception actually happened. With larger web frameworks, this does not always work, so you should stick with the default. use_rich: If ``True`` (the default), use rich_ to compute the repr of locals. If ``False`` or if rich_ is not installed, fall back to a simpler algorithm. .. seealso:: :doc:`exceptions` for a broader explanation of *structlog*'s exception features. .. versionchanged:: 24.3.0 Added *locals_max_length*, *locals_hide_sunder*, *locals_hide_dunder*, *suppress* and *use_rich* arguments. """ def __init__( self, *, show_locals: bool = SHOW_LOCALS, locals_max_length: int = LOCALS_MAX_LENGTH, locals_max_string: int = LOCALS_MAX_STRING, locals_hide_dunder: bool = True, locals_hide_sunder: bool = False, suppress: Iterable[str | ModuleType] = (), max_frames: int = MAX_FRAMES, use_rich: bool = True, ) -> None: if locals_max_length < 0: msg = f'"locals_max_length" must be >= 0: {locals_max_length}' raise ValueError(msg) if locals_max_string < 0: msg = f'"locals_max_string" must be >= 0: {locals_max_string}' raise ValueError(msg) if max_frames < 2: msg = f'"max_frames" must be >= 2: {max_frames}' raise ValueError(msg) self.show_locals = show_locals self.locals_max_length = locals_max_length self.locals_max_string = locals_max_string self.locals_hide_dunder = locals_hide_dunder self.locals_hide_sunder = locals_hide_sunder self.suppress: Sequence[str] = [] for suppress_entity in suppress: if not isinstance(suppress_entity, str): if suppress_entity.__file__ is None: msg = ( f'"suppress" item {suppress_entity!r} must be a ' f"module with '__file__' attribute" ) raise ValueError(msg) path = os.path.dirname(suppress_entity.__file__) else: path = suppress_entity path = os.path.normpath(os.path.abspath(path)) self.suppress.append(path) self.max_frames = max_frames self.use_rich = use_rich def __call__(self, exc_info: ExcInfo) -> list[dict[str, Any]]: trace = extract( *exc_info, show_locals=self.show_locals, locals_max_length=self.locals_max_length, locals_max_string=self.locals_max_string, locals_hide_dunder=self.locals_hide_dunder, locals_hide_sunder=self.locals_hide_sunder, use_rich=self.use_rich, ) for stack in trace.stacks: if len(stack.frames) <= self.max_frames: continue half = ( self.max_frames // 2 ) # Force int division to handle odd numbers correctly fake_frame = Frame( filename="", lineno=-1, name=f"Skipped frames: {len(stack.frames) - (2 * half)}", ) stack.frames[:] = [ *stack.frames[:half], fake_frame, *stack.frames[-half:], ] stacks = [asdict(stack) for stack in trace.stacks] for stack_dict in stacks: for frame_dict in stack_dict["frames"]: if frame_dict["locals"] is None or any( frame_dict["filename"].startswith(path) for path in self.suppress ): del frame_dict["locals"] return stacks structlog-24.4.0/src/structlog/twisted.py0000644000000000000000000002355314645734712015454 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Processors and tools specific to the `Twisted `_ networking engine. See also :doc:`structlog's Twisted support `. """ from __future__ import annotations import json import sys from typing import Any, Callable, Sequence, TextIO from twisted.python import log from twisted.python.failure import Failure from twisted.python.log import ILogObserver, textFromEventDict from zope.interface import implementer from ._base import BoundLoggerBase from ._config import _BUILTIN_DEFAULT_PROCESSORS from .processors import JSONRenderer as GenericJSONRenderer from .typing import EventDict, WrappedLogger class BoundLogger(BoundLoggerBase): """ Twisted-specific version of `structlog.BoundLogger`. Works exactly like the generic one except that it takes advantage of knowing the logging methods in advance. Use it like:: configure( wrapper_class=structlog.twisted.BoundLogger, ) """ def msg(self, event: str | None = None, **kw: Any) -> Any: """ Process event and call ``log.msg()`` with the result. """ return self._proxy_to_logger("msg", event, **kw) def err(self, event: str | None = None, **kw: Any) -> Any: """ Process event and call ``log.err()`` with the result. """ return self._proxy_to_logger("err", event, **kw) class LoggerFactory: """ Build a Twisted logger when an *instance* is called. >>> from structlog import configure >>> from structlog.twisted import LoggerFactory >>> configure(logger_factory=LoggerFactory()) """ def __call__(self, *args: Any) -> WrappedLogger: """ Positional arguments are silently ignored. :rvalue: A new Twisted logger. .. versionchanged:: 0.4.0 Added support for optional positional arguments. """ return log _FAIL_TYPES = (BaseException, Failure) def _extractStuffAndWhy(eventDict: EventDict) -> tuple[Any, Any, EventDict]: """ Removes all possible *_why*s and *_stuff*s, analyzes exc_info and returns a tuple of ``(_stuff, _why, eventDict)``. **Modifies** *eventDict*! """ _stuff = eventDict.pop("_stuff", None) _why = eventDict.pop("_why", None) event = eventDict.pop("event", None) if isinstance(_stuff, _FAIL_TYPES) and isinstance(event, _FAIL_TYPES): raise ValueError("Both _stuff and event contain an Exception/Failure.") # `log.err('event', _why='alsoEvent')` is ambiguous. if _why and isinstance(event, str): raise ValueError("Both `_why` and `event` supplied.") # Two failures are ambiguous too. if not isinstance(_stuff, _FAIL_TYPES) and isinstance(event, _FAIL_TYPES): _why = _why or "error" _stuff = event if isinstance(event, str): _why = event if not _stuff and sys.exc_info() != (None, None, None): _stuff = Failure() # type: ignore[no-untyped-call] # Either we used the error ourselves or the user supplied one for # formatting. Avoid log.err() to dump another traceback into the log. if isinstance(_stuff, BaseException) and not isinstance(_stuff, Failure): _stuff = Failure(_stuff) # type: ignore[no-untyped-call] return _stuff, _why, eventDict class ReprWrapper: """ Wrap a string and return it as the ``__repr__``. This is needed for ``twisted.python.log.err`` that calls `repr` on ``_stuff``: >>> repr("foo") "'foo'" >>> repr(ReprWrapper("foo")) 'foo' Note the extra quotes in the unwrapped example. """ def __init__(self, string: str) -> None: self.string = string def __eq__(self, other: object) -> bool: """ Check for equality, just for tests. """ return ( isinstance(other, self.__class__) and self.string == other.string ) def __repr__(self) -> str: return self.string class JSONRenderer(GenericJSONRenderer): """ Behaves like `structlog.processors.JSONRenderer` except that it formats tracebacks and failures itself if called with ``err()``. .. note:: This ultimately means that the messages get logged out using ``msg()``, and *not* ``err()`` which renders failures in separate lines. Therefore it will break your tests that contain assertions using `flushLoggedErrors `_. *Not* an adapter like `EventAdapter` but a real formatter. Also does *not* require to be adapted using it. Use together with a `JSONLogObserverWrapper`-wrapped Twisted logger like `plainJSONStdOutLogger` for pure-JSON logs. """ def __call__( # type: ignore[override] self, logger: WrappedLogger, name: str, eventDict: EventDict, ) -> tuple[Sequence[Any], dict[str, Any]]: _stuff, _why, eventDict = _extractStuffAndWhy(eventDict) if name == "err": eventDict["event"] = _why if isinstance(_stuff, Failure): eventDict["exception"] = _stuff.getTraceback(detail="verbose") _stuff.cleanFailure() # type: ignore[no-untyped-call] else: eventDict["event"] = _why return ( ( ReprWrapper( GenericJSONRenderer.__call__( # type: ignore[arg-type] self, logger, name, eventDict ) ), ), {"_structlog": True}, ) @implementer(ILogObserver) class PlainFileLogObserver: """ Write only the plain message without timestamps or anything else. Great to just print JSON to stdout where you catch it with something like runit. Args: file: File to print to. .. versionadded:: 0.2.0 """ def __init__(self, file: TextIO) -> None: self._write = file.write self._flush = file.flush def __call__(self, eventDict: EventDict) -> None: self._write( textFromEventDict(eventDict) # type: ignore[arg-type, operator] + "\n", ) self._flush() @implementer(ILogObserver) class JSONLogObserverWrapper: """ Wrap a log *observer* and render non-`JSONRenderer` entries to JSON. Args: observer (ILogObserver): Twisted log observer to wrap. For example :class:`PlainFileObserver` or Twisted's stock `FileLogObserver `_ .. versionadded:: 0.2.0 """ def __init__(self, observer: Any) -> None: self._observer = observer def __call__(self, eventDict: EventDict) -> str: if "_structlog" not in eventDict: eventDict["message"] = ( json.dumps( { "event": textFromEventDict( eventDict # type: ignore[arg-type] ), "system": eventDict.get("system"), } ), ) eventDict["_structlog"] = True return self._observer(eventDict) def plainJSONStdOutLogger() -> JSONLogObserverWrapper: """ Return a logger that writes only the message to stdout. Transforms non-`JSONRenderer` messages to JSON. Ideal for JSONifying log entries from Twisted plugins and libraries that are outside of your control:: $ twistd -n --logger structlog.twisted.plainJSONStdOutLogger web {"event": "Log opened.", "system": "-"} {"event": "twistd 13.1.0 (python 2.7.3) starting up.", "system": "-"} {"event": "reactor class: twisted...EPollReactor.", "system": "-"} {"event": "Site starting on 8080", "system": "-"} {"event": "Starting factory ", ...} ... Composes `PlainFileLogObserver` and `JSONLogObserverWrapper` to a usable logger. .. versionadded:: 0.2.0 """ return JSONLogObserverWrapper(PlainFileLogObserver(sys.stdout)) class EventAdapter: """ Adapt an ``event_dict`` to Twisted logging system. Particularly, make a wrapped `twisted.python.log.err `_ behave as expected. Args: dictRenderer: Renderer that is used for the actual log message. Please note that structlog comes with a dedicated `JSONRenderer`. **Must** be the last processor in the chain and requires a *dictRenderer* for the actual formatting as an constructor argument in order to be able to fully support the original behaviors of ``log.msg()`` and ``log.err()``. """ def __init__( self, dictRenderer: ( Callable[[WrappedLogger, str, EventDict], str] | None ) = None, ) -> None: self._dictRenderer = dictRenderer or _BUILTIN_DEFAULT_PROCESSORS[-1] def __call__( self, logger: WrappedLogger, name: str, eventDict: EventDict ) -> Any: if name == "err": # This aspires to handle the following cases correctly: # 1. log.err(failure, _why='event', **kw) # 2. log.err('event', **kw) # 3. log.err(_stuff=failure, _why='event', **kw) _stuff, _why, eventDict = _extractStuffAndWhy(eventDict) eventDict["event"] = _why return ( (), { "_stuff": _stuff, "_why": self._dictRenderer(logger, name, eventDict), }, ) return self._dictRenderer(logger, name, eventDict) structlog-24.4.0/src/structlog/types.py0000644000000000000000000000137414645734712015132 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Deprecated name for :mod:`structlog.typing`. .. versionadded:: 20.2.0 .. deprecated:: 22.2.0 """ from __future__ import annotations from .typing import ( BindableLogger, Context, EventDict, ExceptionRenderer, ExceptionTransformer, ExcInfo, FilteringBoundLogger, Processor, WrappedLogger, ) __all__ = ( "WrappedLogger", "Context", "EventDict", "Processor", "ExcInfo", "ExceptionRenderer", "ExceptionTransformer", "BindableLogger", "FilteringBoundLogger", ) structlog-24.4.0/src/structlog/typing.py0000644000000000000000000001745314645734712015305 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Type information used throughout *structlog*. For now, they are considered provisional. Especially `BindableLogger` will probably change to something more elegant. .. versionadded:: 22.2.0 """ from __future__ import annotations from types import TracebackType from typing import ( Any, Callable, Dict, Mapping, MutableMapping, Optional, Protocol, TextIO, Tuple, Type, Union, runtime_checkable, ) WrappedLogger = Any """ A logger that is wrapped by a bound logger and is ultimately responsible for the output of the log entries. *structlog* makes *no* assumptions about it. .. versionadded:: 20.2.0 """ Context = Union[Dict[str, Any], Dict[Any, Any]] """ A dict-like context carrier. .. versionadded:: 20.2.0 """ EventDict = MutableMapping[str, Any] """ An event dictionary as it is passed into processors. It's created by copying the configured `Context` but doesn't need to support copy itself. .. versionadded:: 20.2.0 """ ProcessorReturnValue = Union[ Mapping[str, Any], str, bytes, bytearray, Tuple[Any, ...] ] """ A value returned by a processor. """ Processor = Callable[[WrappedLogger, str, EventDict], ProcessorReturnValue] """ A callable that is part of the processor chain. See :doc:`processors`. .. versionadded:: 20.2.0 """ ExcInfo = Tuple[Type[BaseException], BaseException, Optional[TracebackType]] """ An exception info tuple as returned by `sys.exc_info`. .. versionadded:: 20.2.0 """ ExceptionRenderer = Callable[[TextIO, ExcInfo], None] """ A callable that pretty-prints an `ExcInfo` into a file-like object. Used by `structlog.dev.ConsoleRenderer`. .. versionadded:: 21.2.0 """ @runtime_checkable class ExceptionTransformer(Protocol): """ **Protocol:** A callable that transforms an `ExcInfo` into another datastructure. The result should be something that your renderer can work with, e.g., a ``str`` or a JSON-serializable ``dict``. Used by `structlog.processors.format_exc_info()` and `structlog.processors.ExceptionPrettyPrinter`. Args: exc_info: Is the exception tuple to format Returns: Anything that can be rendered by the last processor in your chain, for example, a string or a JSON-serializable structure. .. versionadded:: 22.1.0 """ def __call__(self, exc_info: ExcInfo) -> Any: ... @runtime_checkable class BindableLogger(Protocol): """ **Protocol**: Methods shared among all bound loggers and that are relied on by *structlog*. .. versionadded:: 20.2.0 """ _context: Context def bind(self, **new_values: Any) -> BindableLogger: ... def unbind(self, *keys: str) -> BindableLogger: ... def try_unbind(self, *keys: str) -> BindableLogger: ... def new(self, **new_values: Any) -> BindableLogger: ... class FilteringBoundLogger(BindableLogger, Protocol): """ **Protocol**: A `BindableLogger` that filters by a level. The only way to instantiate one is using `make_filtering_bound_logger`. .. versionadded:: 20.2.0 .. versionadded:: 22.2.0 String interpolation using positional arguments. .. versionadded:: 22.2.0 Async variants ``alog()``, ``adebug()``, ``ainfo()``, and so forth. .. versionchanged:: 22.3.0 String interpolation is only attempted if positional arguments are passed. """ def bind(self, **new_values: Any) -> FilteringBoundLogger: """ Return a new logger with *new_values* added to the existing ones. .. versionadded:: 22.1.0 """ def unbind(self, *keys: str) -> FilteringBoundLogger: """ Return a new logger with *keys* removed from the context. .. versionadded:: 22.1.0 """ def try_unbind(self, *keys: str) -> FilteringBoundLogger: """ Like :meth:`unbind`, but best effort: missing keys are ignored. .. versionadded:: 22.1.0 """ def new(self, **new_values: Any) -> FilteringBoundLogger: """ Clear context and binds *initial_values* using `bind`. .. versionadded:: 22.1.0 """ def debug(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **debug** level. """ async def adebug(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **debug** level. ..versionadded:: 22.2.0 """ def info(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **info** level. """ async def ainfo(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **info** level. ..versionadded:: 22.2.0 """ def warning(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **warn** level. """ async def awarning(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **warn** level. ..versionadded:: 22.2.0 """ def warn(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **warn** level. """ async def awarn(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **warn** level. ..versionadded:: 22.2.0 """ def error(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **error** level. """ async def aerror(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **error** level. ..versionadded:: 22.2.0 """ def err(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **error** level. """ def fatal(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **critical** level. """ async def afatal(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **critical** level. ..versionadded:: 22.2.0 """ def exception(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **error** level and ensure that ``exc_info`` is set in the event dictionary. """ async def aexception(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **error** level and ensure that ``exc_info`` is set in the event dictionary. ..versionadded:: 22.2.0 """ def critical(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **critical** level. """ async def acritical(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **critical** level. ..versionadded:: 22.2.0 """ def msg(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **info** level. """ async def amsg(self, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at **info** level. """ def log(self, level: int, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at *level*. """ async def alog(self, level: int, event: str, *args: Any, **kw: Any) -> Any: """ Log ``event % args`` with **kw** at *level*. """ structlog-24.4.0/tests/__init__.py0000644000000000000000000000034214645734712014044 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. structlog-24.4.0/tests/additional_frame.py0000644000000000000000000000075014645734712015572 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Helper function for testing the deduction of stdlib logger names. Since the logger factories are called from within structlog._config, they have to skip a frame. Calling them here emulates that. """ def additional_frame(callable): return callable() structlog-24.4.0/tests/conftest.py0000644000000000000000000000267114645734712014141 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import logging from io import StringIO import pytest import structlog from structlog._log_levels import NAME_TO_LEVEL from structlog.testing import CapturingLogger try: import twisted except ImportError: twisted = None LOGGER = logging.getLogger() @pytest.fixture(autouse=True) def _ensure_logging_framework_not_altered(): """ Prevents 'ValueError: I/O operation on closed file.' errors. """ before_handlers = list(LOGGER.handlers) yield LOGGER.handlers = before_handlers @pytest.fixture(name="sio") def _sio(): """ A new StringIO instance. """ return StringIO() @pytest.fixture(name="event_dict") def _event_dict(): """ An example event dictionary with multiple value types w/o the event itself. """ class A: def __repr__(self): return r"" return {"a": A(), "b": [3, 4], "x": 7, "y": "test", "z": (1, 2)} @pytest.fixture( name="stdlib_log_method", params=[m for m in NAME_TO_LEVEL if m != "notset"], ) def _stdlib_log_methods(request): return request.param @pytest.fixture(name="cl") def _cl(): return CapturingLogger() @pytest.fixture(autouse=True) def _reset_config(): structlog.reset_defaults() structlog-24.4.0/tests/test_base.py0000644000000000000000000001620214645734712014260 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import pytest from pretend import raiser, stub from structlog import get_context from structlog._base import BoundLoggerBase from structlog._config import _CONFIG from structlog.exceptions import DropEvent from structlog.processors import KeyValueRenderer from structlog.testing import ReturnLogger from tests.utils import CustomError def build_bl(logger=None, processors=None, context=None): """ Convenience function to build BoundLoggerBases with sane defaults. """ return BoundLoggerBase( logger if logger is not None else ReturnLogger(), processors if processors is not None else _CONFIG.default_processors, context if context is not None else _CONFIG.default_context_class(), ) class TestBinding: def test_repr(self): """ repr() of a BoundLoggerBase shows its context and processors. """ bl = build_bl(processors=[1, 2, 3], context={"A": "B"}) assert ( "" ) == repr(bl) def test_binds_independently(self): """ Ensure BoundLogger is immutable by default. """ b = build_bl(processors=[KeyValueRenderer(sort_keys=True)]) b = b.bind(x=42, y=23) b1 = b.bind(foo="bar") b2 = b.bind(foo="qux") assert b._context != b1._context != b2._context def test_new_clears_state(self): """ Calling new() on a logger clears the context. """ b = build_bl() b = b.bind(x=42) assert 42 == get_context(b)["x"] b = b.bind() assert 42 == get_context(b)["x"] b = b.new() assert {} == dict(get_context(b)) def test_comparison(self): """ Two bound loggers are equal if their context is equal. """ b = build_bl() assert b == b.bind() assert b is not b.bind() assert b != b.bind(x=5) assert b != "test" def test_bind_keeps_class(self): """ Binding values does not change the type of the bound logger. """ class Wrapper(BoundLoggerBase): pass b = Wrapper(None, [], {}) assert isinstance(b.bind(), Wrapper) def test_new_keeps_class(self): """ Clearing context does not change the type of the bound logger. """ class Wrapper(BoundLoggerBase): pass b = Wrapper(None, [], {}) assert isinstance(b.new(), Wrapper) def test_unbind(self): """ unbind() removes keys from context. """ b = build_bl().bind(x=42, y=23).unbind("x", "y") assert {} == b._context def test_unbind_fail(self): """ unbind() raises KeyError if the key is missing. """ with pytest.raises(KeyError): build_bl().bind(x=42, y=23).unbind("x", "z") def test_try_unbind(self): """ try_unbind() removes keys from context. """ b = build_bl().bind(x=42, y=23).try_unbind("x", "y") assert {} == b._context def test_try_unbind_fail(self): """ try_unbind() does nothing if the key is missing. """ b = build_bl().bind(x=42, y=23).try_unbind("x", "z") assert {"y": 23} == b._context class TestProcessing: def test_event_empty_string(self): """ Empty strings are a valid event. """ b = build_bl(processors=[], context={}) args, kw = b._process_event("meth", "", {"foo": "bar"}) assert () == args assert {"event": "", "foo": "bar"} == kw def test_copies_context_before_processing(self): """ BoundLoggerBase._process_event() gets called before relaying events to wrapped loggers. """ def chk(_, __, event_dict): assert b._context is not event_dict return "" b = build_bl(processors=[chk]) assert (("",), {}) == b._process_event("", "event", {}) assert "event" not in b._context def test_chain_does_not_swallow_all_exceptions(self): """ If the chain raises anything else than DropEvent, the error is not swallowed. """ b = build_bl(processors=[raiser(CustomError)]) with pytest.raises(CustomError): b._process_event("", "boom", {}) def test_last_processor_returns_string(self): """ If the final processor returns a string, ``(the_string,), {}`` is returned. """ logger = stub(msg=lambda *args, **kw: (args, kw)) b = build_bl(logger, processors=[lambda *_: "foo"]) assert (("foo",), {}) == b._process_event("", "foo", {}) def test_last_processor_returns_bytes(self): """ If the final processor returns bytes, ``(the_bytes,), {}`` is returned. """ logger = stub(msg=lambda *args, **kw: (args, kw)) b = build_bl(logger, processors=[lambda *_: b"foo"]) assert ((b"foo",), {}) == b._process_event(None, "name", {}) def test_last_processor_returns_bytearray(self): """ If the final processor returns a bytearray, ``(the_array,), {}`` is returned. """ logger = stub(msg=lambda *args, **kw: (args, kw)) b = build_bl(logger, processors=[lambda *_: bytearray(b"foo")]) assert ((bytearray(b"foo"),), {}) == b._process_event(None, "name", {}) def test_last_processor_returns_tuple(self): """ If the final processor returns a tuple, it is just passed through. """ logger = stub(msg=lambda *args, **kw: (args, kw)) b = build_bl( logger, processors=[lambda *_: (("foo",), {"key": "value"})] ) assert (("foo",), {"key": "value"}) == b._process_event("", "foo", {}) def test_last_processor_returns_dict(self): """ If the final processor returns a dict, ``(), the_dict`` is returned. """ logger = stub(msg=lambda *args, **kw: (args, kw)) b = build_bl(logger, processors=[lambda *_: {"event": "foo"}]) assert ((), {"event": "foo"}) == b._process_event("", "foo", {}) def test_last_processor_returns_unknown_value(self): """ If the final processor returns something unexpected, raise ValueError with a helpful error message. """ logger = stub(msg=lambda *args, **kw: (args, kw)) b = build_bl(logger, processors=[lambda *_: object()]) with pytest.raises(ValueError, match="Last processor didn't return"): b._process_event("", "foo", {}) class TestProxying: def test_processor_raising_DropEvent_silently_aborts_chain(self, capsys): """ If a processor raises DropEvent, the chain is aborted and nothing is proxied to the logger. """ b = build_bl(processors=[raiser(DropEvent), raiser(ValueError)]) b._proxy_to_logger("", None, x=5) assert ("", "") == capsys.readouterr() structlog-24.4.0/tests/test_config.py0000644000000000000000000003167014645734712014621 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import abc import pickle import warnings import pytest from pretend import call, call_recorder, stub import structlog from structlog._base import BoundLoggerBase from structlog._config import ( _BUILTIN_DEFAULT_CONTEXT_CLASS, _BUILTIN_DEFAULT_LOGGER_FACTORY, _BUILTIN_DEFAULT_PROCESSORS, _BUILTIN_DEFAULT_WRAPPER_CLASS, _CONFIG, BoundLoggerLazyProxy, configure, configure_once, get_logger, wrap_logger, ) from structlog.typing import BindableLogger @pytest.fixture(name="proxy") def _proxy(): """ Returns a BoundLoggerLazyProxy constructed w/o parameters & None as logger. """ return BoundLoggerLazyProxy(None) class Wrapper(BoundLoggerBase): """ Custom wrapper class for testing. """ def test_lazy_logger_is_not_detected_as_abstract_method(): """ If someone defines an attribute on an ABC with a logger, that logger is not detected as an abstract method. See https://github.com/hynek/structlog/issues/229 """ class Foo(metaclass=abc.ABCMeta): # noqa: B024 log = structlog.get_logger() Foo() def test_lazy_logger_is_an_instance_of_bindable_logger(): """ The BoundLoggerLazyProxy returned by get_logger fulfills the BindableLogger protocol. See https://github.com/hynek/structlog/issues/560 """ assert isinstance(get_logger(), BindableLogger) def test_lazy_logger_context_is_initial_values(): """ If a user asks for _context (e.g., using get_context) return initial_values. """ logger = get_logger(context="a") assert {"context": "a"} == structlog.get_context(logger) def test_default_context_class(): """ Default context class is dict. """ assert dict is _BUILTIN_DEFAULT_CONTEXT_CLASS class TestConfigure: def test_get_config_is_configured(self): """ Return value of structlog.get_config() works as input for structlog.configure(). is_configured() reflects the state of configuration. """ assert False is structlog.is_configured() structlog.configure(**structlog.get_config()) assert True is structlog.is_configured() structlog.reset_defaults() assert False is structlog.is_configured() def test_configure_all(self, proxy): """ All configurations are applied and land on the bound logger. """ x = stub() configure(processors=[x], context_class=dict) b = proxy.bind() assert [x] == b._processors assert dict is b._context.__class__ def test_reset(self, proxy): """ Reset resets all settings to their default values. """ x = stub() configure(processors=[x], context_class=dict, wrapper_class=Wrapper) structlog.reset_defaults() b = proxy.bind() assert [x] != b._processors assert _BUILTIN_DEFAULT_PROCESSORS == b._processors assert isinstance(b, _BUILTIN_DEFAULT_WRAPPER_CLASS) assert _BUILTIN_DEFAULT_CONTEXT_CLASS == b._context.__class__ assert _BUILTIN_DEFAULT_LOGGER_FACTORY is _CONFIG.logger_factory def test_just_processors(self, proxy): """ It's possible to only configure processors. """ x = stub() configure(processors=[x]) b = proxy.bind() assert [x] == b._processors assert _BUILTIN_DEFAULT_PROCESSORS != b._processors assert _BUILTIN_DEFAULT_CONTEXT_CLASS == b._context.__class__ def test_just_context_class(self, proxy): """ It's possible to only configure the context class. """ configure(context_class=dict) b = proxy.bind() assert dict is b._context.__class__ assert _BUILTIN_DEFAULT_PROCESSORS == b._processors def test_configure_sets_is_configured(self): """ After configure() is_configured() returns True. """ assert False is _CONFIG.is_configured configure() assert True is _CONFIG.is_configured def test_configures_logger_factory(self): """ It's possible to configure the logger factory. """ def f(): pass configure(logger_factory=f) assert f is _CONFIG.logger_factory class TestBoundLoggerLazyProxy: def test_repr(self): """ repr reflects all attributes. """ p = BoundLoggerLazyProxy( None, processors=[1, 2, 3], context_class=dict, initial_values={"foo": 42}, logger_factory_args=(4, 5), ) assert ( ", " "initial_values={'foo': 42}, " "logger_factory_args=(4, 5))>" ) == repr(p) def test_returns_bound_logger_on_bind(self, proxy): """ bind gets proxied to the wrapped bound logger. """ assert isinstance(proxy.bind(), BoundLoggerBase) def test_returns_bound_logger_on_new(self, proxy): """ new gets proxied to the wrapped bound logger. """ assert isinstance(proxy.new(), BoundLoggerBase) def test_returns_bound_logger_on_try_unbind(self, proxy): """ try_unbind gets proxied to the wrapped bound logger. """ assert isinstance(proxy.try_unbind(), BoundLoggerBase) def test_prefers_args_over_config(self): """ Configuration can be overridden by passing arguments. """ p = BoundLoggerLazyProxy( None, processors=[1, 2, 3], context_class=dict ) b = p.bind() assert isinstance(b._context, dict) assert [1, 2, 3] == b._processors class Class: def __init__(self, *args, **kw): pass def update(self, *args, **kw): pass configure(processors=[4, 5, 6], context_class=Class) b = p.bind() assert not isinstance(b._context, Class) assert [1, 2, 3] == b._processors def test_falls_back_to_config(self, proxy): """ Configuration is used if no arguments are passed. """ b = proxy.bind() assert isinstance(b._context, _CONFIG.default_context_class) assert _CONFIG.default_processors == b._processors def test_bind_honors_initial_values(self): """ Passed initial_values are merged on binds. """ p = BoundLoggerLazyProxy(None, initial_values={"a": 1, "b": 2}) b = p.bind() assert {"a": 1, "b": 2} == b._context b = p.bind(c=3) assert {"a": 1, "b": 2, "c": 3} == b._context def test_bind_binds_new_values(self, proxy): """ Values passed to bind arrive in the context. """ b = proxy.bind(c=3) assert {"c": 3} == b._context def test_unbind_unbinds_from_initial_values(self): """ It's possible to unbind a value that came from initial_values. """ p = BoundLoggerLazyProxy(None, initial_values={"a": 1, "b": 2}) b = p.unbind("a") assert {"b": 2} == b._context def test_honors_wrapper_class(self): """ Passed wrapper_class is used. """ p = BoundLoggerLazyProxy(None, wrapper_class=Wrapper) b = p.bind() assert isinstance(b, Wrapper) def test_honors_wrapper_from_config(self, proxy): """ Configured wrapper_class is used if not overridden. """ configure(wrapper_class=Wrapper) b = proxy.bind() assert isinstance(b, Wrapper) def test_new_binds_only_initial_values_implicit_ctx_class(self, proxy): """ new() doesn't clear initial_values if context_class comes from config. """ proxy = BoundLoggerLazyProxy(None, initial_values={"a": 1, "b": 2}) b = proxy.new(foo=42) assert {"a": 1, "b": 2, "foo": 42} == b._context def test_new_binds_only_initial_values_explicit_ctx_class(self, proxy): """ new() doesn't clear initial_values if context_class is passed explicitly.. """ proxy = BoundLoggerLazyProxy( None, initial_values={"a": 1, "b": 2}, context_class=dict ) b = proxy.new(foo=42) assert {"a": 1, "b": 2, "foo": 42} == b._context def test_rebinds_bind_method(self, proxy): """ To save time, be rebind the bind method once the logger has been cached. """ configure(cache_logger_on_first_use=True) bind = proxy.bind proxy.bind() assert bind != proxy.bind def test_does_not_cache_by_default(self, proxy): """ Proxy's bind method doesn't change by default. """ bind = proxy.bind proxy.bind() assert bind == proxy.bind @pytest.mark.parametrize("cache", [True, False]) def test_argument_takes_precedence_over_configuration(self, cache): """ Passing cache_logger_on_first_use as an argument overrides config. """ configure(cache_logger_on_first_use=cache) proxy = BoundLoggerLazyProxy(None, cache_logger_on_first_use=not cache) bind = proxy.bind proxy.bind() if cache: assert bind == proxy.bind else: assert bind != proxy.bind def test_bind_doesnt_cache_logger(self): """ Calling configure() changes BoundLoggerLazyProxys immediately. Previous uses of the BoundLoggerLazyProxy don't interfere. """ class F: "New logger factory with a new attribute" def info(self, *args): return 5 proxy = BoundLoggerLazyProxy(None) proxy.bind() configure(logger_factory=F) new_b = proxy.bind() assert new_b.info("test") == 5 def test_emphemeral(self): """ Calling an unknown method proxy creates a new wrapped bound logger first. """ class Foo(BoundLoggerBase): def foo(self): return 42 proxy = BoundLoggerLazyProxy( None, wrapper_class=Foo, cache_logger_on_first_use=False ) assert 42 == proxy.foo() @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1)) def test_pickle(self, proto): """ Can be pickled and unpickled. """ bllp = BoundLoggerLazyProxy(None) assert repr(bllp) == repr(pickle.loads(pickle.dumps(bllp, proto))) class TestFunctions: def test_wrap_passes_args(self): """ wrap_logger propagates all arguments to the wrapped bound logger. """ logger = object() p = wrap_logger(logger, processors=[1, 2, 3], context_class=dict) assert logger is p._logger assert [1, 2, 3] == p._processors assert dict is p._context_class def test_empty_processors(self): """ An empty list is a valid value for processors so it must be preserved. """ # We need to do a bind such that we get an actual logger and not just # a lazy proxy. logger = wrap_logger(object(), processors=[]).new() assert [] == logger._processors def test_wrap_returns_proxy(self): """ wrap_logger always returns a lazy proxy. """ assert isinstance(wrap_logger(None), BoundLoggerLazyProxy) def test_configure_once_issues_warning_on_repeated_call(self): """ configure_once raises a warning when it's after configuration. """ with warnings.catch_warnings(record=True) as warns: configure_once() assert 0 == len(warns) with warnings.catch_warnings(record=True) as warns: configure_once() assert 1 == len(warns) assert RuntimeWarning is warns[0].category assert "Repeated configuration attempted." == warns[0].message.args[0] def test_get_logger_configures_according_to_config(self): """ get_logger returns a correctly configured bound logger. """ b = get_logger().bind() assert isinstance( b._logger, _BUILTIN_DEFAULT_LOGGER_FACTORY().__class__ ) assert _BUILTIN_DEFAULT_PROCESSORS == b._processors assert isinstance(b, _BUILTIN_DEFAULT_WRAPPER_CLASS) assert _BUILTIN_DEFAULT_CONTEXT_CLASS == b._context.__class__ def test_get_logger_passes_positional_arguments_to_logger_factory(self): """ Ensure `get_logger` passes optional positional arguments through to the logger factory. """ factory = call_recorder(lambda *args: object()) configure(logger_factory=factory) get_logger("test").bind(x=42) assert [call("test")] == factory.calls structlog-24.4.0/tests/test_contextvars.py0000644000000000000000000002157714645734712015741 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import asyncio import inspect import secrets import pytest import structlog from structlog.contextvars import ( _CONTEXT_VARS, bind_contextvars, bound_contextvars, clear_contextvars, get_contextvars, get_merged_contextvars, merge_contextvars, reset_contextvars, unbind_contextvars, ) @pytest.fixture(autouse=True) def _clear_contextvars(): """ Make sure all tests start with a clean slate. """ clear_contextvars() class TestContextvars: async def test_bind(self): """ Binding a variable causes it to be included in the result of merge_contextvars. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1) return merge_contextvars(None, None, {"b": 2}) assert {"a": 1, "b": 2} == await event_loop.create_task(coro()) async def test_multiple_binds(self): """ Multiple calls to bind_contextvars accumulate values instead of replacing them. But they override redefined ones. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1, c=3) bind_contextvars(c=333, d=4) return merge_contextvars(None, None, {"b": 2}) assert { "a": 1, "b": 2, "c": 333, "d": 4, } == await event_loop.create_task(coro()) async def test_reset(self): """ reset_contextvars allows resetting contexvars to previously-set values. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1) assert {"a": 1} == get_contextvars() await event_loop.create_task(nested_coro()) async def nested_coro(): tokens = bind_contextvars(a=2, b=3) assert {"a": 2, "b": 3} == get_contextvars() reset_contextvars(**tokens) assert {"a": 1} == get_contextvars() await event_loop.create_task(coro()) async def test_nested_async_bind(self): """ Context is passed correctly between "nested" concurrent operations. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1) return await event_loop.create_task(nested_coro()) async def nested_coro(): bind_contextvars(c=3) return merge_contextvars(None, None, {"b": 2}) assert {"a": 1, "b": 2, "c": 3} == await event_loop.create_task(coro()) async def test_merge_works_without_bind(self): """ merge_contextvars returns values as normal even when there has been no previous calls to bind_contextvars. """ event_loop = asyncio.get_running_loop() async def coro(): return merge_contextvars(None, None, {"b": 2}) assert {"b": 2} == await event_loop.create_task(coro()) async def test_merge_overrides_bind(self): """ Variables included in merge_contextvars override previously bound variables. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1) return merge_contextvars(None, None, {"a": 111, "b": 2}) assert {"a": 111, "b": 2} == await event_loop.create_task(coro()) async def test_clear(self): """ The context-local context can be cleared, causing any previously bound variables to not be included in merge_contextvars's result. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1) clear_contextvars() return merge_contextvars(None, None, {"b": 2}) assert {"b": 2} == await event_loop.create_task(coro()) async def test_clear_without_bind(self): """ The context-local context can be cleared, causing any previously bound variables to not be included in merge_contextvars's result. """ event_loop = asyncio.get_running_loop() async def coro(): clear_contextvars() return merge_contextvars(None, None, {}) assert {} == await event_loop.create_task(coro()) async def test_unbind(self): """ Unbinding a previously bound variable causes it to be removed from the result of merge_contextvars. """ event_loop = asyncio.get_running_loop() async def coro(): bind_contextvars(a=1) unbind_contextvars("a") return merge_contextvars(None, None, {"b": 2}) assert {"b": 2} == await event_loop.create_task(coro()) async def test_unbind_not_bound(self): """ Unbinding a not bound variable causes doesn't raise an exception. """ event_loop = asyncio.get_running_loop() async def coro(): # Since unbinding means "setting to Ellipsis", we have to make # some effort to ensure that the ContextVar never existed. unbind_contextvars("a" + secrets.token_hex()) return merge_contextvars(None, None, {"b": 2}) assert {"b": 2} == await event_loop.create_task(coro()) async def test_parallel_binds(self): """ Binding a variable causes it to be included in the result of merge_contextvars. """ event_loop = asyncio.get_running_loop() coro1_bind = asyncio.Event() coro2_bind = asyncio.Event() bind_contextvars(c=3) async def coro1(): bind_contextvars(a=1) coro1_bind.set() await coro2_bind.wait() return merge_contextvars(None, None, {"b": 2}) async def coro2(): bind_contextvars(a=2) await coro1_bind.wait() coro2_bind.set() return merge_contextvars(None, None, {"b": 2}) coro1_task = event_loop.create_task(coro1()) coro2_task = event_loop.create_task(coro2()) assert {"a": 1, "b": 2, "c": 3} == await coro1_task assert {"a": 2, "b": 2, "c": 3} == await coro2_task def test_get_only_gets_structlog_without_deleted(self): """ get_contextvars returns only the structlog-specific key-values with the prefix removed. Deleted keys (= Ellipsis) are ignored. """ bind_contextvars(a=1, b=2) unbind_contextvars("b") _CONTEXT_VARS["foo"] = "bar" assert {"a": 1} == get_contextvars() def test_get_merged_merges_context(self): """ get_merged_contextvars merges a bound context into the copy. """ bind_contextvars(x=1) log = structlog.get_logger().bind(y=2) assert {"x": 1, "y": 2} == get_merged_contextvars(log) class TestBoundContextvars: def test_cleanup(self): """ Bindings are cleaned up """ with bound_contextvars(x=42, y="foo"): assert {"x": 42, "y": "foo"} == get_contextvars() assert {} == get_contextvars() def test_cleanup_conflict(self): """ Overwritten keys are restored after the clean up """ bind_contextvars(x="original", z="unrelated") with bound_contextvars(x=42, y="foo"): assert {"x": 42, "y": "foo", "z": "unrelated"} == get_contextvars() assert {"x": "original", "z": "unrelated"} == get_contextvars() def test_preserve_independent_bind(self): """ New bindings inside bound_contextvars are preserved after the clean up """ with bound_contextvars(x=42): bind_contextvars(y="foo") assert {"x": 42, "y": "foo"} == get_contextvars() assert {"y": "foo"} == get_contextvars() def test_nesting_works(self): """ bound_contextvars binds and unbinds even when nested """ with bound_contextvars(l1=1): assert {"l1": 1} == get_contextvars() with bound_contextvars(l2=2): assert {"l1": 1, "l2": 2} == get_contextvars() assert {"l1": 1} == get_contextvars() assert {} == get_contextvars() def test_as_decorator(self): """ bound_contextvars can be used as a decorator and it preserves the name, signature and documentation of the wrapped function. """ @bound_contextvars(x=42) def wrapped(arg1): """Wrapped documentation""" bind_contextvars(y=arg1) assert {"x": 42, "y": arg1} == get_contextvars() wrapped(23) assert "wrapped" == wrapped.__name__ assert "(arg1)" == str(inspect.signature(wrapped)) assert "Wrapped documentation" == wrapped.__doc__ structlog-24.4.0/tests/test_dev.py0000644000000000000000000004637414645734712014141 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import pickle import sys from io import StringIO from unittest import mock import pytest from structlog import dev class TestPad: def test_normal(self): """ If chars are missing, adequate number of " " are added. """ assert 100 == len(dev._pad("test", 100)) def test_negative(self): """ If string is already too long, don't do anything. """ assert len("test") == len(dev._pad("test", 2)) @pytest.fixture(name="cr", scope="session") def _cr(): return dev.ConsoleRenderer( colors=dev._has_colors, exception_formatter=dev.plain_traceback ) @pytest.fixture(name="styles", scope="session") def _styles(cr): return cr._styles @pytest.fixture(name="padded", scope="session") def _padded(styles): return styles.bright + dev._pad("test", dev._EVENT_WIDTH) + styles.reset class TestConsoleRenderer: @pytest.mark.skipif(dev.colorama, reason="Colorama must be missing.") @pytest.mark.skipif( not dev._IS_WINDOWS, reason="Must be running on Windows." ) def test_missing_colorama(self): """ ConsoleRenderer(colors=True) raises SystemError on initialization if Colorama is missing and _IS_WINDOWS is True. """ with pytest.raises(SystemError) as e: dev.ConsoleRenderer(colors=True) assert ( "ConsoleRenderer with `colors=True` requires the Colorama package " "installed." ) in e.value.args[0] def test_plain(self, cr, padded): """ Works with a plain event_dict with only the event. """ rv = cr(None, None, {"event": "test"}) assert padded == rv def test_timestamp(self, cr, styles, padded): """ Timestamps get prepended. """ rv = cr(None, None, {"event": "test", "timestamp": 42}) assert (styles.timestamp + "42" + styles.reset + " " + padded) == rv def test_event_stringified(self, cr, padded): """ Event is cast to string. """ not_a_string = Exception("test") rv = cr(None, None, {"event": not_a_string}) assert padded == rv def test_event_renamed(self): """ The main event key can be renamed. """ cr = dev.ConsoleRenderer(colors=False, event_key="msg") assert "new event name event=something custom" == cr( None, None, {"msg": "new event name", "event": "something custom"} ) def test_timestamp_renamed(self): """ The timestamp key can be renamed. """ cr = dev.ConsoleRenderer(colors=False, timestamp_key="ts") assert ( "2023-09-07 le event" == cr( None, None, {"ts": "2023-09-07", "event": "le event"}, ).rstrip() ) def test_level(self, cr, styles, padded): """ Levels are rendered aligned, in square brackets, and color-coded. """ rv = cr( None, None, {"event": "test", "level": "critical", "foo": "bar"} ) assert ( "[" + dev.RED + styles.bright + dev._pad("critical", cr._longest_level) + styles.reset + "] " + padded + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset ) == rv def test_init_accepts_overriding_levels(self, styles, padded): """ Stdlib levels are rendered aligned, in brackets, and color coded. """ my_styles = dev.ConsoleRenderer.get_default_level_styles( colors=dev._has_colors ) my_styles["MY_OH_MY"] = my_styles["critical"] cr = dev.ConsoleRenderer( colors=dev._has_colors, level_styles=my_styles ) # this would blow up if the level_styles override failed rv = cr( None, None, {"event": "test", "level": "MY_OH_MY", "foo": "bar"} ) assert ( "[" + dev.RED + styles.bright + dev._pad("MY_OH_MY", cr._longest_level) + styles.reset + "] " + padded + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset ) == rv def test_logger_name(self, cr, styles, padded): """ Logger names are appended after the event. """ rv = cr(None, None, {"event": "test", "logger": "some_module"}) assert ( padded + " [" + styles.reset + styles.bright + dev.BLUE + "some_module" + styles.reset + "]" + styles.reset ) == rv def test_logger_name_name(self, cr, padded, styles): """ It's possible to set the logger name using a "logger_name" key. """ assert ( padded + " [" + styles.reset + styles.bright + dev.BLUE + "yolo" + styles.reset + "]" + styles.reset ) == cr(None, None, {"event": "test", "logger_name": "yolo"}) def test_key_values(self, cr, styles, padded): """ Key-value pairs go sorted alphabetically to the end. """ rv = cr(None, None, {"event": "test", "key": "value", "foo": "bar"}) assert ( padded + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset + " " + styles.kv_key + "key" + styles.reset + "=" + styles.kv_value + "value" + styles.reset ) == rv def test_key_values_unsorted(self, styles, padded): """ Key-value pairs go in original order to the end. """ cr = dev.ConsoleRenderer(sort_keys=False) rv = cr( None, None, {"event": "test", "key": "value", "foo": "bar"}, ) assert ( padded + " " + styles.kv_key + "key" + styles.reset + "=" + styles.kv_value + "value" + styles.reset + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset ) == rv @pytest.mark.parametrize("wrap", [True, False]) def test_exception_rendered( self, cr, recwarn, wrap, styles, padded, monkeypatch ): """ Exceptions are rendered after a new line if they are already rendered in the event dict. A warning is emitted if exception printing is "customized". """ exc = "Traceback:\nFake traceback...\nFakeError: yolo" # Wrap the formatter to provoke the warning. if wrap: monkeypatch.setattr( cr, "_exception_formatter", lambda s, ei: dev.plain_traceback(s, ei), ) rv = cr(None, None, {"event": "test", "exception": exc}) assert (f"{padded}\n" + exc) == rv if wrap: (w,) = recwarn.list assert ( "Remove `format_exc_info` from your processor chain " "if you want pretty exceptions.", ) == w.message.args def test_stack_info(self, cr, styles, padded): """ Stack traces are rendered after a new line. """ stack = "fake stack" rv = cr(None, None, {"event": "test", "stack": stack}) assert (f"{padded}\n" + stack) == rv def test_exc_info_tuple(self, cr, styles, padded): """ If exc_info is a tuple, it is used. """ try: 0 / 0 except ZeroDivisionError: ei = sys.exc_info() rv = cr(None, None, {"event": "test", "exc_info": ei}) exc = dev._format_exception(ei) assert (f"{padded}\n" + exc) == rv def test_exc_info_bool(self, cr, styles, padded): """ If exc_info is True, it is obtained using sys.exc_info(). """ try: 0 / 0 except ZeroDivisionError: ei = sys.exc_info() rv = cr(None, None, {"event": "test", "exc_info": True}) exc = dev._format_exception(ei) assert (f"{padded}\n" + exc) == rv def test_exc_info_exception(self, cr, styles, padded): """ If exc_info is an exception, it is used by converting to a tuple. """ try: 0 / 0 except ZeroDivisionError as e: ei = e rv = cr(None, None, {"event": "test", "exc_info": ei}) exc = dev._format_exception((ei.__class__, ei, ei.__traceback__)) assert (f"{padded}\n" + exc) == rv def test_pad_event_param(self, styles): """ `pad_event` parameter works. """ rv = dev.ConsoleRenderer(42, dev._has_colors)( None, None, {"event": "test", "foo": "bar"} ) assert ( styles.bright + dev._pad("test", 42) + styles.reset + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset ) == rv @pytest.mark.parametrize("explicit_ei", ["tuple", "exception", False]) def test_everything(self, cr, styles, padded, explicit_ei): """ Put all cases together. """ if explicit_ei: try: 0 / 0 except ZeroDivisionError as e: if explicit_ei == "tuple": ei = sys.exc_info() elif explicit_ei == "exception": ei = e else: raise ValueError from None else: ei = True stack = "fake stack trace" ed = { "event": "test", "exc_info": ei, "key": "value", "foo": "bar", "timestamp": "13:13", "logger": "some_module", "level": "error", "stack": stack, } if explicit_ei: rv = cr(None, None, ed) else: try: 0 / 0 except ZeroDivisionError: rv = cr(None, None, ed) ei = sys.exc_info() if isinstance(ei, BaseException): ei = (ei.__class__, ei, ei.__traceback__) exc = dev._format_exception(ei) assert ( styles.timestamp + "13:13" + styles.reset + " [" + styles.level_error + styles.bright + dev._pad("error", cr._longest_level) + styles.reset + "] " + padded + " [" + styles.reset + styles.bright + dev.BLUE + "some_module" + styles.reset + "]" + styles.reset + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset + " " + styles.kv_key + "key" + styles.reset + "=" + styles.kv_value + "value" + styles.reset + "\n" + stack + "\n\n" + "=" * 79 + "\n" + "\n" + exc ) == rv def test_colorama_colors_false(self): """ If colors is False, don't use colors or styles ever. """ plain_cr = dev.ConsoleRenderer(colors=False) rv = plain_cr( None, None, {"event": "event", "level": "info", "foo": "bar"} ) assert dev._PlainStyles is plain_cr._styles assert "[info ] event foo=bar" == rv def test_colorama_force_colors(self, styles, padded): """ If force_colors is True, use colors even if the destination is non-tty. """ cr = dev.ConsoleRenderer( colors=dev._has_colors, force_colors=dev._has_colors ) rv = cr( None, None, {"event": "test", "level": "critical", "foo": "bar"} ) assert ( "[" + dev.RED + styles.bright + dev._pad("critical", cr._longest_level) + styles.reset + "] " + padded + " " + styles.kv_key + "foo" + styles.reset + "=" + styles.kv_value + "bar" + styles.reset ) == rv assert not dev._has_colors or dev._ColorfulStyles is cr._styles @pytest.mark.parametrize("rns", [True, False]) def test_repr_native_str(self, rns): """ repr_native_str=False doesn't repr on native strings. "event" is never repr'ed. """ rv = dev.ConsoleRenderer(colors=False, repr_native_str=rns)( None, None, {"event": "哈", "key": 42, "key2": "哈"} ) cnt = rv.count("哈") assert 2 == cnt @pytest.mark.parametrize("repr_native_str", [True, False]) @pytest.mark.parametrize("force_colors", [True, False]) @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1)) def test_pickle(self, repr_native_str, force_colors, proto): """ ConsoleRenderer can be pickled and unpickled. """ r = dev.ConsoleRenderer( repr_native_str=repr_native_str, force_colors=force_colors ) assert r(None, None, {"event": "foo"}) == pickle.loads( pickle.dumps(r, proto) )(None, None, {"event": "foo"}) def test_no_exception(self): """ If there is no exception, don't blow up. """ r = dev.ConsoleRenderer(colors=False) assert ( "hi" == r( None, None, {"event": "hi", "exc_info": (None, None, None)} ).rstrip() ) def test_columns_warns_about_meaningless_arguments(self, recwarn): """ If columns is set, a warning is emitted for all ignored arguments. """ dev.ConsoleRenderer( columns=[dev.Column("", lambda k, v: "")], pad_event=42, colors=not dev._has_colors, force_colors=True, repr_native_str=True, level_styles=dev._PlainStyles, event_key="not event", timestamp_key="not timestamp", ) assert { f"The `{arg}` argument is ignored when passing `columns`." for arg in ( "pad_event", "colors", "force_colors", "repr_native_str", "level_styles", "event_key", "timestamp_key", ) } == {str(w.message) for w in recwarn.list} def test_detects_default_column(self): """ The default renderer is detected and removed from the columns list. """ fake_formatter = object() llcf = dev.Column("log_level", dev.LogLevelColumnFormatter(None, "")) cr = dev.ConsoleRenderer( columns=[dev.Column("", fake_formatter), llcf] ) assert fake_formatter is cr._default_column_formatter assert [llcf] == cr._columns def test_enforces_presence_of_exactly_one_default_formatter(self): """ If there is no, or more than one, default formatter, raise ValueError. """ with pytest.raises( ValueError, match="Must pass a default column formatter", ): dev.ConsoleRenderer(columns=[]) with pytest.raises( ValueError, match="Only one default column formatter allowed.", ): dev.ConsoleRenderer( columns=[ dev.Column("", lambda k, v: ""), dev.Column("", lambda k, v: ""), ] ) class TestSetExcInfo: def test_wrong_name(self): """ Do nothing if name is not exception. """ assert {} == dev.set_exc_info(None, "foo", {}) @pytest.mark.parametrize("ei", [False, None, ()]) def test_already_set(self, ei): """ Do nothing if exc_info is already set. """ assert {"exc_info": ei} == dev.set_exc_info( None, "foo", {"exc_info": ei} ) def test_set_it(self): """ Set exc_info to True if its not set and if the method name is exception. """ assert {"exc_info": True} == dev.set_exc_info(None, "exception", {}) @pytest.mark.skipif(dev.rich is None, reason="Needs Rich.") class TestRichTracebackFormatter: def test_default(self): """ If Rich is present, it's the default. """ assert dev.default_exception_formatter is dev.rich_traceback def test_does_not_blow_up(self, sio): """ We trust Rich to do the right thing, so we just exercise the function and check the first new line that we add manually is present. """ try: 0 / 0 except ZeroDivisionError: dev.rich_traceback(sio, sys.exc_info()) assert sio.getvalue().startswith("\n") def test_width_minus_one(self, sio): """ If width is -1, it's replaced by the terminal width on first use. """ rtf = dev.RichTracebackFormatter(width=-1) with mock.patch("shutil.get_terminal_size", return_value=(42, 0)): try: 0 / 0 except ZeroDivisionError: rtf(sio, sys.exc_info()) assert 42 == rtf.width @pytest.mark.skipif( dev.better_exceptions is None, reason="Needs better-exceptions." ) class TestBetterTraceback: def test_default(self): """ If better-exceptions is present and Rich is NOT present, it's the default. """ assert ( dev.rich is not None or dev.default_exception_formatter is dev.better_traceback ) def test_does_not_blow_up(self): """ We trust better-exceptions to do the right thing, so we just exercise the function. """ sio = StringIO() try: 0 / 0 except ZeroDivisionError: dev.better_traceback(sio, sys.exc_info()) assert sio.getvalue().startswith("\n") class TestLogLevelColumnFormatter: def test_no_style(self): """ No level_styles means no control characters and no padding. """ assert "[critical]" == dev.LogLevelColumnFormatter(None, "foo")( "", "critical" ) structlog-24.4.0/tests/test_frames.py0000644000000000000000000001050314645734712014621 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import sys import pytest from pretend import stub from structlog._frames import ( _find_first_app_frame_and_name, _format_exception, _format_stack, ) class TestFindFirstAppFrameAndName: def test_ignores_structlog_by_default(self): """ No matter what you pass in, structlog frames get always ignored. """ f1 = stub(f_globals={"__name__": "test"}, f_back=None) f2 = stub(f_globals={"__name__": "structlog.blubb"}, f_back=f1) f, n = _find_first_app_frame_and_name(_getframe=lambda: f2) assert (f1, "test") == (f, n) def test_ignoring_of_additional_frame_names_works(self): """ Additional names are properly ignored too. """ f1 = stub(f_globals={"__name__": "test"}, f_back=None) f2 = stub(f_globals={"__name__": "ignored.bar"}, f_back=f1) f3 = stub(f_globals={"__name__": "structlog.blubb"}, f_back=f2) f, n = _find_first_app_frame_and_name( additional_ignores=["ignored"], _getframe=lambda: f3 ) assert (f1, "test") == (f, n) def test_tolerates_missing_name(self): """ Use ``?`` if `f_globals` lacks a `__name__` key """ f1 = stub(f_globals={}, f_back=None) f, n = _find_first_app_frame_and_name(_getframe=lambda: f1) assert (f1, "?") == (f, n) def test_tolerates_name_explicitly_None_oneframe(self): """ Use ``?`` if `f_globals` has a `None` valued `__name__` key """ f1 = stub(f_globals={"__name__": None}, f_back=None) f, n = _find_first_app_frame_and_name(_getframe=lambda: f1) assert (f1, "?") == (f, n) def test_tolerates_name_explicitly_None_manyframe(self): """ Use ``?`` if `f_globals` has a `None` valued `__name__` key, multiple frames up. """ f1 = stub(f_globals={"__name__": None}, f_back=None) f2 = stub(f_globals={"__name__": "structlog.blubb"}, f_back=f1) f, n = _find_first_app_frame_and_name(_getframe=lambda: f2) assert (f1, "?") == (f, n) def test_tolerates_f_back_is_None(self): """ Use ``?`` if all frames are in ignored frames. """ f1 = stub(f_globals={"__name__": "structlog"}, f_back=None) f, n = _find_first_app_frame_and_name(_getframe=lambda: f1) assert (f1, "?") == (f, n) @pytest.fixture() def exc_info(): """ Fake a valid exc_info. """ try: raise ValueError except ValueError: return sys.exc_info() class TestFormatException: def test_returns_str(self, exc_info): """ Always returns a native string. """ assert isinstance(_format_exception(exc_info), str) def test_formats(self, exc_info): """ The passed exc_info is formatted. """ assert _format_exception(exc_info).startswith( "Traceback (most recent call last):\n" ) def test_no_trailing_nl(self, exc_info, monkeypatch): """ Trailing newlines are snipped off but if the string does not contain one nothing is removed. """ from structlog._frames import traceback monkeypatch.setattr( traceback, "print_exception", lambda *a: a[-1].write("foo") ) assert "foo" == _format_exception(exc_info) class TestFormatStack: def test_returns_str(self): """ Always returns a native string. """ assert isinstance(_format_stack(sys._getframe()), str) def test_formats(self): """ The passed stack is formatted. """ assert _format_stack(sys._getframe()).startswith( "Stack (most recent call last):\n" ) def test_no_trailing_nl(self, monkeypatch): """ Trailing newlines are snipped off but if the string does not contain one nothing is removed. """ from structlog._frames import traceback monkeypatch.setattr( traceback, "print_stack", lambda frame, file: file.write("foo") ) assert _format_stack(sys._getframe()).endswith("foo") structlog-24.4.0/tests/test_generic.py0000644000000000000000000000320714645734712014763 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import pickle import pytest from freezegun import freeze_time from structlog._config import _CONFIG from structlog._generic import BoundLogger from structlog.testing import ReturnLogger class TestLogger: def log(self, msg): return "log", msg def gol(self, msg): return "gol", msg class TestGenericBoundLogger: def test_caches(self): """ __getattr__() gets called only once per logger method. """ b = BoundLogger( ReturnLogger(), _CONFIG.default_processors, _CONFIG.default_context_class(), ) assert "msg" not in b.__dict__ b.msg("foo") assert "msg" in b.__dict__ @pytest.mark.parametrize("proto", range(3, pickle.HIGHEST_PROTOCOL + 1)) @freeze_time("2023-05-22 17:00") def test_pickle(self, proto): """ Can be pickled and unpickled. """ b = BoundLogger( ReturnLogger(), _CONFIG.default_processors, _CONFIG.default_context_class(), ).bind(x=1) assert b.info("hi") == pickle.loads(pickle.dumps(b, proto)).info("hi") def test_deepcopy(self): """ __getattr__ returns None for '__deepcopy__' """ b = BoundLogger( ReturnLogger(), _CONFIG.default_processors, _CONFIG.default_context_class(), ) assert b.__deepcopy__ is None structlog-24.4.0/tests/test_log_levels.py0000644000000000000000000002155214645734712015505 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import logging import pickle import pytest from structlog import make_filtering_bound_logger from structlog._log_levels import LEVEL_TO_NAME from structlog.contextvars import ( bind_contextvars, clear_contextvars, merge_contextvars, ) @pytest.fixture(name="bl") def _bl(cl): return make_filtering_bound_logger(logging.INFO)(cl, [], {}) class TestFilteringLogger: def test_exact_level(self, bl, cl): """ if log level is exactly the min_level, log. """ bl.info("yep") assert [("info", (), {"event": "yep"})] == cl.calls async def test_async_exact_level(self, bl, cl): """ if log level is exactly the min_level, log. """ await bl.ainfo("yep") assert [("info", (), {"event": "yep"})] == cl.calls def test_one_below(self, bl, cl): """ if log level is below the min_level, don't log. """ bl.debug("nope") assert [] == cl.calls async def test_async_one_below(self, bl, cl): """ if log level is below the min_level, don't log. """ await bl.adebug("nope") assert [] == cl.calls def test_filtered_interp(self, bl, cl): """ Passing interpolation args works if the log entry is filtered out. """ bl.debug("hello %s!", "world") assert [] == cl.calls async def test_async_filtered_interp(self, bl, cl): """ Passing interpolation args works if the log entry is filtered out. """ await bl.adebug("hello %s!", "world") assert [] == cl.calls def test_no_args(self, bl, cl): """ If no args are passed, don't attempt interpolation. See also #473 """ bl.info(42) assert 42 == cl.calls[0][2]["event"] async def test_async_no_args(self, bl, cl): """ If no args are passed, don't attempt interpolation. See also #473 """ await bl.ainfo(42) assert 42 == cl.calls[0][2]["event"] def test_log_exact_level(self, bl, cl): """ if log level is exactly the min_level, log. """ bl.log(logging.INFO, "yep") assert [("info", (), {"event": "yep"})] == cl.calls async def test_alog_exact_level(self, bl, cl): """ if log level is exactly the min_level, log. """ await bl.alog(logging.INFO, "yep") assert [("info", (), {"event": "yep"})] == cl.calls def test_log_one_below(self, bl, cl): """ if log level is below the min_level, don't log. """ bl.log(logging.DEBUG, "nope") assert [] == cl.calls async def test_alog_one_below(self, bl, cl): """ if log level is below the min_level, don't log. """ await bl.alog(logging.DEBUG, "nope") assert [] == cl.calls async def test_alog_no_args(self, bl, cl): """ If no args are passed, interpolation is not attempted. See also #473 """ await bl.alog(logging.INFO, 42) assert 42 == cl.calls[0][2]["event"] def test_log_interp(self, bl, cl): """ Interpolation happens if args are passed. """ bl.log(logging.INFO, "answer is %d.", 42) assert "answer is 42." == cl.calls[0][2]["event"] async def test_alog_interp(self, bl, cl): """ Interpolation happens if args are passed. """ await bl.alog(logging.INFO, "answer is %d.", 42) assert "answer is 42." == cl.calls[0][2]["event"] def test_filter_bound_below_missing_event_string(self, bl): """ Missing event arg causes exception below min_level. """ with pytest.raises(TypeError) as exc_info: bl.debug(missing="event string!") assert exc_info.type is TypeError message = "missing 1 required positional argument: 'event'" assert message in exc_info.value.args[0] def test_filter_bound_exact_missing_event_string(self, bl): """ Missing event arg causes exception even at min_level. """ with pytest.raises(TypeError) as exc_info: bl.info(missing="event string!") assert exc_info.type is TypeError message = "missing 1 required positional argument: 'event'" assert message in exc_info.value.args[0] def test_exception(self, bl, cl): """ exception ensures that exc_info is set to True, unless it's already set. """ bl.exception("boom") assert [("error", (), {"event": "boom", "exc_info": True})] == cl.calls async def test_async_exception(self, bl, cl): """ aexception sets exc_info to current exception info, if it's not already set. """ try: raise Exception("boom") except Exception as e: await bl.aexception("foo") exc = e assert 1 == len(cl.calls) assert isinstance(cl.calls[0][2]["exc_info"], tuple) assert exc == cl.calls[0][2]["exc_info"][1] def test_exception_positional_args(self, bl, cl): """ exception allows for positional args """ bl.exception("%s %s", "boom", "bastic") assert [ ("error", (), {"event": "boom bastic", "exc_info": True}) ] == cl.calls async def test_aexception_positional_args(self, bl, cl): """ aexception allows for positional args """ await bl.aexception("%s %s", "boom", "bastic") assert 1 == len(cl.calls) assert "boom bastic" == cl.calls[0][2]["event"] async def test_async_exception_true(self, bl, cl): """ aexception replaces exc_info with current exception info, if exc_info is True. """ try: raise Exception("boom") except Exception as e: await bl.aexception("foo", exc_info=True) exc = e assert 1 == len(cl.calls) assert isinstance(cl.calls[0][2]["exc_info"], tuple) assert exc is cl.calls[0][2]["exc_info"][1] def test_exception_passed(self, bl, cl): """ exception if exc_info has a value, exception doesn't tamper with it. """ bl.exception("boom", exc_info=42) assert [("error", (), {"event": "boom", "exc_info": 42})] == cl.calls async def test_async_exception_passed(self, bl, cl): """ exception if exc_info has a value (other than True), exception doesn't tamper with it. """ await bl.aexception("boom", exc_info=42) assert [("error", (), {"event": "boom", "exc_info": 42})] == cl.calls def test_exception_pass_exception(self, bl, cl): """ If an Exception is passed for the event, don't explode. Not a documented feature, but a regression for some people. See #473. """ try: raise Exception("foo") except Exception as e: bl.exception(e) exc = e assert exc is cl.calls[0][2]["event"] @pytest.mark.parametrize("level", tuple(LEVEL_TO_NAME.keys())) def test_pickle(self, level): """ FilteringBoundLogger are pickleable. """ bl = make_filtering_bound_logger(level) assert bl == pickle.loads(pickle.dumps(bl)) def test_pos_args(self, bl, cl): """ Positional arguments are used for string interpolation. """ bl.info("hello %s -- %d!", "world", 42) assert [("info", (), {"event": "hello world -- 42!"})] == cl.calls async def test_async_pos_args(self, bl, cl): """ Positional arguments are used for string interpolation. """ await bl.ainfo("hello %s -- %d!", "world", 42) assert [("info", (), {"event": "hello world -- 42!"})] == cl.calls @pytest.mark.parametrize( ("meth", "args"), [ ("aexception", ("ev",)), ("ainfo", ("ev",)), ("alog", (logging.INFO, "ev")), ], ) async def test_async_contextvars_merged(self, meth, args, cl): """ Contextvars are merged into the event dict. """ clear_contextvars() bl = make_filtering_bound_logger(logging.INFO)( cl, [merge_contextvars], {} ) bind_contextvars(context_included="yep") await getattr(bl, meth)(*args) assert len(cl.calls) == 1 assert "context_included" in cl.calls[0].kwargs def test_log_percent(self, bl, cl): """ As long as there's no positional args passed, logging % is possible. """ bl.info("hey %! %%!") assert [("info", (), {"event": "hey %! %%!"})] == cl.calls structlog-24.4.0/tests/test_output.py0000644000000000000000000002215514645734712014712 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import copy import pickle from io import BytesIO, StringIO import pytest from structlog import ( BytesLogger, BytesLoggerFactory, PrintLogger, PrintLoggerFactory, WriteLogger, WriteLoggerFactory, ) from structlog._output import WRITE_LOCKS, stderr, stdout from .utils import stdlib_log_methods class TestLoggers: """ Tests common to the Print and WriteLoggers. """ @pytest.fixture(name="logger_cls", params=(WriteLogger, PrintLogger)) @staticmethod def _logger_cls(request): return request.param def test_prints_to_stdout_by_default(self, logger_cls, capsys): """ Instantiating without arguments gives conveniently a logger to standard out. """ logger_cls().msg("hello") out, err = capsys.readouterr() assert "hello\n" == out assert "" == err def test_prints_to_correct_file(self, logger_cls, tmp_path, capsys): """ Supplied files are respected. """ p = tmp_path / "test.log" with p.open("w") as f: logger_cls(f).msg("hello") out, err = capsys.readouterr() assert "" == out == err assert "hello\n" == p.read_text() def test_lock(self, logger_cls, sio): """ Creating a logger adds a lock to WRITE_LOCKS. """ assert sio not in WRITE_LOCKS logger_cls(sio) assert sio in WRITE_LOCKS @pytest.mark.parametrize("method", stdlib_log_methods) def test_stdlib_methods_support(self, logger_cls, method, sio): """ Print/WriteLogger implements methods of stdlib loggers. """ getattr(logger_cls(sio), method)("hello") assert "hello" in sio.getvalue() @pytest.mark.parametrize("file", [None, stdout, stderr]) @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1)) def test_pickle(self, logger_cls, file, proto): """ Can be pickled and unpickled for stdout and stderr. Can't compare output because capsys et all would confuse the logic. """ pl = logger_cls(file=file) rv = pickle.loads(pickle.dumps(pl, proto)) assert pl._file is rv._file assert pl._lock is rv._lock @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1)) def test_pickle_not_stdout_stderr(self, logger_cls, tmpdir, proto): """ Loggers with different files than stdout/stderr raise a PickingError. """ f = tmpdir.join("file.log") f.write("") pl = logger_cls(file=f.open()) with pytest.raises(pickle.PicklingError, match="Only (.+)Loggers to"): pickle.dumps(pl, proto) def test_deepcopy(self, logger_cls, capsys): """ Deepcopied logger works. """ copied_logger = copy.deepcopy(logger_cls()) copied_logger.msg("hello") out, err = capsys.readouterr() assert "hello\n" == out assert "" == err def test_deepcopy_no_stdout(self, logger_cls, tmp_path): """ Only loggers that log to stdout or stderr can be deepcopy-ed. """ p = tmp_path / "log.txt" with p.open(mode="w") as f: logger = logger_cls(f) logger.msg("hello") with pytest.raises(copy.error): copy.deepcopy(logger) assert "hello\n" == p.read_text() def test_repr(self, logger_cls): """ __repr__ makes sense. """ assert repr(logger_cls()).startswith(f"<{logger_cls.__name__}(file=") def test_stdout_monkeypatch(self, monkeypatch, capsys): """ If stdout gets monkeypatched, the new instance receives the output. """ import sys p = PrintLogger() new_stdout = StringIO() monkeypatch.setattr(sys, "stdout", new_stdout) p.msg("hello") out, err = capsys.readouterr() assert "hello\n" == new_stdout.getvalue() assert "" == out assert "" == err class TestPrintLoggerFactory: def test_does_not_cache(self): """ Due to doctest weirdness, we must not reuse PrintLoggers. """ f = PrintLoggerFactory() assert f() is not f() def test_passes_file(self): """ If a file is passed to the factory, it get passed on to the logger. """ pl = PrintLoggerFactory(stderr)() assert stderr is pl._file def test_ignores_args(self): """ PrintLogger doesn't take positional arguments. If any are passed to the factory, they are not passed to the logger. """ PrintLoggerFactory()(1, 2, 3) class TestWriteLoggerFactory: def test_does_not_cache(self): """ Due to doctest weirdness, we must not reuse WriteLoggers. """ f = WriteLoggerFactory() assert f() is not f() def test_passes_file(self): """ If a file is passed to the factory, it get passed on to the logger. """ pl = WriteLoggerFactory(stderr)() assert stderr is pl._file def test_ignores_args(self): """ WriteLogger doesn't take positional arguments. If any are passed to the factory, they are not passed to the logger. """ WriteLoggerFactory()(1, 2, 3) class TestBytesLogger: def test_prints_to_stdout_by_default(self, capsys): """ Instantiating without arguments gives conveniently a logger to standard out. """ BytesLogger().msg(b"hell\xc3\xb6") out, err = capsys.readouterr() assert "hellö\n" == out assert "" == err def test_prints_to_correct_file(self, tmp_path, capsys): """ Supplied files are respected. """ p = tmp_path / "test.log" with p.open("wb") as f: BytesLogger(f).msg(b"hello") out, err = capsys.readouterr() assert "" == out == err assert "hello\n" == p.read_text() def test_repr(self): """ __repr__ makes sense. """ assert repr(BytesLogger()).startswith(" dict[str, Any]: """ A dict to be passed in the `extra` parameter of the `logging` module's log methods. """ return { "this": "is", "some": "extra values", "x_int": 4, "x_bool": True, } @pytest.fixture(name="extra_dict") def extra_dict_fixture(): return extra_dict() class TestExtraAdder: @pytest.mark.parametrize( ("allow", "misses"), [ (None, None), ({}, None), *[({key}, None) for key in extra_dict()], ({"missing"}, {"missing"}), ({"missing", "keys"}, {"missing"}), ({"this", "x_int"}, None), ], ) def test_add_extra( self, make_log_record: Callable[[], logging.LogRecord], extra_dict: dict[str, Any], allow: Collection[str] | None, misses: set[str] | None, ): """ Extra attributes of a LogRecord object are added to the event dict. """ record: logging.LogRecord = make_log_record() record.__dict__.update(extra_dict) event_dict = {"_record": record, "ed_key": "ed_value"} expected = self._copy_allowed(event_dict, extra_dict, allow) if allow is None: actual = ExtraAdder()(None, None, event_dict) assert expected == actual actual = ExtraAdder(allow)(None, None, event_dict) assert expected == actual if misses: assert misses.isdisjoint(expected.keys()) def test_no_record(self): """ If the event_dict has no LogRecord, do nothing. """ actual = ExtraAdder()(None, None, {}) assert {} == actual @pytest.mark.parametrize( ("allow", "misses"), [ (None, None), ({}, None), *[({key}, None) for key in extra_dict()], ({"missing"}, {"missing"}), ({"missing", "keys"}, {"missing"}), ({"this", "x_int"}, None), ], ) def test_add_extra_e2e( self, extra_dict: dict[str, Any], allow: Collection[str] | None, misses: set[str] | None, ): """ Values passed in the `extra` parameter of the `logging` module's log methods pass through to log output. """ logger = logging.Logger(sys._getframe().f_code.co_name) string_io = StringIO() handler = logging.StreamHandler(string_io) formatter = ProcessorFormatter( foreign_pre_chain=[ExtraAdder(allow)], processors=[JSONRenderer()], ) handler.setFormatter(formatter) handler.setLevel(0) logger.addHandler(handler) logger.setLevel(0) logging.warning("allow = %s", allow) event_dict = {"event": "Some text"} expected = self._copy_allowed(event_dict, extra_dict, allow) logger.info("Some %s", "text", extra=extra_dict) actual = { key: value for key, value in json.loads(string_io.getvalue()).items() if not key.startswith("_") } assert expected == actual if misses: assert misses.isdisjoint(expected.keys()) @classmethod def _copy_allowed( cls, event_dict: EventDict, extra_dict: dict[str, Any], allow: Collection[str] | None, ) -> EventDict: if allow is None: return {**event_dict, **extra_dict} return { **event_dict, **{ key: value for key, value in extra_dict.items() if key in allow }, } @pytest.fixture(name="stdlib_logger") def _stdlib_logger(): logger = logging.getLogger("test_logger") logger.setLevel(logging.DEBUG) yield logger logging.basicConfig() class TestRenderToLogKW: def test_default(self, stdlib_logger): """ Translates `event` to `msg` and handles otherwise empty `event_dict`s. """ d = render_to_log_kwargs(None, None, {"event": "message"}) assert {"msg": "message", "extra": {}} == d # now check stdlib logger likes those kwargs with patch.object(stdlib_logger, "_log") as mock_log: stdlib_logger.info(**d) mock_log.assert_called_once_with(logging.INFO, "message", (), extra={}) def test_add_extra_event_dict(self, event_dict, stdlib_logger): """ Adds all remaining data from `event_dict` into `extra`. """ event_dict["event"] = "message" d = render_to_log_kwargs(None, None, event_dict) assert {"msg": "message", "extra": event_dict} == d # now check stdlib logger likes those kwargs with patch.object(stdlib_logger, "_log") as mock_log: stdlib_logger.info(**d) mock_log.assert_called_once_with( logging.INFO, "message", (), extra=event_dict ) def test_handles_special_kw(self, event_dict, stdlib_logger): """ "exc_info", "stack_info", and "stacklevel" aren't passed as extras. Cf. https://github.com/hynek/structlog/issues/424 """ del event_dict["a"] # needs a repr event_dict["event"] = "message" event_dict["exc_info"] = True event_dict["stack_info"] = False event_dict["stacklevel"] = 1 event_dict["stackLevel"] = 1 # not a reserved kw d = render_to_log_kwargs(None, None, event_dict) expected = { "msg": "message", "exc_info": True, "stack_info": False, "stacklevel": 1, "extra": { "b": [3, 4], "x": 7, "y": "test", "z": (1, 2), "stackLevel": 1, }, } assert expected == d # now check stdlib logger likes those kwargs with patch.object(stdlib_logger, "_log") as mock_log: stdlib_logger.info(**d) expected.pop("msg") mock_log.assert_called_once_with( logging.INFO, "message", (), **expected ) def test_integration_special_kw(self, event_dict, stdlib_logger): """ render_to_log_kwargs with a wrapped logger calls the stdlib logger correctly reserved stdlib keywords are in logging.Logger._log https://github.com/python/cpython/blob/ae7b17673f29efe17b416cbcfbf43b5b3ff5977c/Lib/logging/__init__.py#L1632 """ expected = { "msg": "message", "exc_info": True, "stack_info": False, "stacklevel": 1, "extra": {**event_dict}, } event_dict["exc_info"] = True event_dict["stack_info"] = False event_dict["stacklevel"] = 1 struct_logger = wrap_logger( stdlib_logger, processors=[render_to_log_kwargs], ) # now check struct logger passes those kwargs to stdlib with patch.object(stdlib_logger, "_log") as mock_log: struct_logger.info("message", **event_dict) expected.pop("msg") mock_log.assert_called_once_with( logging.INFO, "message", (), **expected ) @pytest.fixture(name="configure_for_processor_formatter") def _configure_for_processor_formatter(): """ Configure structlog to use ProcessorFormatter. Reset logging setting after the test (structlog is reset automatically before all tests). """ configure( processors=[add_log_level, ProcessorFormatter.wrap_for_formatter], logger_factory=LoggerFactory(), wrapper_class=BoundLogger, ) yield logging.basicConfig() def configure_logging( pre_chain, logger=None, pass_foreign_args=False, renderer=ConsoleRenderer(colors=False), # noqa: B008 ): """ Configure logging to use ProcessorFormatter. Return a list that is filled with event dicts form calls. """ event_dicts = [] def capture(_, __, ed): event_dicts.append(ed.copy()) return ed logging.config.dictConfig( { "version": 1, "disable_existing_loggers": False, "formatters": { "plain": { "()": ProcessorFormatter, "processors": [ capture, ProcessorFormatter.remove_processors_meta, renderer, ], "foreign_pre_chain": pre_chain, "format": "%(message)s [in %(funcName)s]", "logger": logger, "pass_foreign_args": pass_foreign_args, } }, "handlers": { "default": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "plain", } }, "loggers": { "": { "handlers": ["default"], "level": "DEBUG", "propagate": True, } }, } ) return event_dicts @pytest.mark.usefixtures("configure_for_processor_formatter") class TestProcessorFormatter: """ These are all integration tests because they're all about integration. """ def test_foreign_delegate(self, capsys): """ If foreign_pre_chain is None, non-structlog log entries are delegated to logging. The processor chain's event dict is invoked with `_from_structlog=False` """ calls = configure_logging(None) logging.getLogger().warning("foo") assert ("", "foo [in test_foreign_delegate]\n") == capsys.readouterr() assert calls[0]["_from_structlog"] is False assert isinstance(calls[0]["_record"], logging.LogRecord) def test_clears_args(self, capsys): """ We render our log records before sending it back to logging. Therefore we must clear `LogRecord.args` otherwise the user gets an `TypeError: not all arguments converted during string formatting.` if they use positional formatting in stdlib logging. """ configure_logging(None) logging.getLogger().warning("hello %s.", "world") assert ( "", "hello world. [in test_clears_args]\n", ) == capsys.readouterr() def test_pass_foreign_args_true_sets_positional_args_key(self): """ If `pass_foreign_args` is `True` we set the `positional_args` key in the `event_dict` before clearing args. """ test_processor = call_recorder(lambda _, __, event_dict: event_dict) configure_logging((test_processor,), pass_foreign_args=True) positional_args = {"foo": "bar"} logging.getLogger().info("okay %(foo)s", positional_args) event_dict = test_processor.calls[0].args[2] assert "positional_args" in event_dict assert positional_args == event_dict["positional_args"] def test_log_dict(self, capsys): """ dicts can be logged with std library loggers. """ configure_logging(None) logging.getLogger().warning({"foo": "bar"}) assert ( "", "{'foo': 'bar'} [in test_log_dict]\n", ) == capsys.readouterr() def test_foreign_pre_chain(self, capsys): """ If foreign_pre_chain is an iterable, it's used to pre-process non-structlog log entries. """ configure_logging([add_log_level]) logging.getLogger().warning("foo") assert ( "", "[warning ] foo [in test_foreign_pre_chain]\n", ) == capsys.readouterr() def test_foreign_pre_chain_add_logger_name(self, capsys): """ foreign_pre_chain works with add_logger_name processor. """ configure_logging((add_logger_name,)) logging.getLogger("sample-name").warning("foo") assert ( "", "foo [sample-name] [in test_foreign_pr" "e_chain_add_logger_name]\n", ) == capsys.readouterr() def test_foreign_chain_can_pass_dictionaries_without_excepting( self, capsys ): """ If a foreign logger passes a dictionary to a logging function, check we correctly identify that it did not come from structlog. """ configure_logging(None) configure( processors=[ProcessorFormatter.wrap_for_formatter], logger_factory=LoggerFactory(), wrapper_class=BoundLogger, ) logging.getLogger().warning({"foo": "bar"}) assert ( "", "{'foo': 'bar'} [in " "test_foreign_chain_can_pass_dictionaries_without_excepting]\n", ) == capsys.readouterr() def test_foreign_pre_chain_gets_exc_info(self): """ If non-structlog record contains exc_info, foreign_pre_chain functions have access to it. """ test_processor = call_recorder(lambda _, __, event_dict: event_dict) configure_logging((test_processor,), renderer=KeyValueRenderer()) try: raise RuntimeError("oh no") except Exception: logging.getLogger().exception("okay") event_dict = test_processor.calls[0].args[2] assert "exc_info" in event_dict assert isinstance(event_dict["exc_info"], tuple) def test_foreign_pre_chain_sys_exc_info(self): """ If a foreign_pre_chain function accesses sys.exc_info(), ProcessorFormatter should not have changed it. """ class MyError(Exception): pass def add_excinfo(logger, log_method, event_dict): event_dict["exc_info"] = sys.exc_info() return event_dict test_processor = call_recorder(lambda _, __, event_dict: event_dict) configure_logging( (add_excinfo, test_processor), renderer=KeyValueRenderer() ) try: raise MyError("oh no") except Exception: logging.getLogger().error("okay") event_dict = test_processor.calls[0].args[2] assert MyError is event_dict["exc_info"][0] def test_other_handlers_get_original_record(self): """ Logging handlers that come after the handler with ProcessorFormatter should receive original, unmodified record. """ configure_logging(None) handler1 = logging.StreamHandler() handler1.setFormatter(ProcessorFormatter(JSONRenderer())) handler2 = stub( handle=call_recorder(lambda record: None), level=logging.INFO, ) logger = logging.getLogger() logger.addHandler(handler1) logger.addHandler(handler2) logger.info("meh") assert 1 == len(handler2.handle.calls) handler2_record = handler2.handle.calls[0].args[0] assert "meh" == handler2_record.msg @pytest.mark.parametrize("keep", [True, False]) def test_formatter_unsets_exc_info(self, capsys, keep): """ Stack traces doesn't get printed outside of the json document when keep_exc_info are set to False but preserved if set to True. """ configure_logging(None) logger = logging.getLogger() def format_exc_info_fake(logger, name, event_dict): del event_dict["exc_info"] event_dict["exception"] = "Exception!" return event_dict formatter = ProcessorFormatter( processor=JSONRenderer(), keep_stack_info=keep, keep_exc_info=keep, foreign_pre_chain=[format_exc_info_fake], ) logger.handlers[0].setFormatter(formatter) try: raise RuntimeError("oh no") except Exception: logging.getLogger().exception("seen worse") out, err = capsys.readouterr() assert "" == out if keep is False: assert ( '{"event": "seen worse", "exception": "Exception!"}\n' ) == err else: assert "Traceback (most recent call last):" in err @pytest.mark.parametrize("keep", [True, False]) def test_formatter_unsets_stack_info(self, capsys, keep): """ Stack traces doesn't get printed outside of the json document when keep_stack_info are set to False but preserved if set to True. """ configure_logging(None) logger = logging.getLogger() formatter = ProcessorFormatter( processor=JSONRenderer(), keep_stack_info=keep, keep_exc_info=keep, foreign_pre_chain=[], ) logger.handlers[0].setFormatter(formatter) logging.getLogger().warning("have a stack trace", stack_info=True) out, err = capsys.readouterr() assert "" == out if keep is False: assert 1 == err.count("Stack (most recent call last):") else: assert 2 == err.count("Stack (most recent call last):") def test_native(self, capsys): """ If the log entry comes from structlog, it's unpackaged and processed. """ eds = configure_logging(None) get_logger().warning("foo") assert ( "", "[warning ] foo [in test_native]\n", ) == capsys.readouterr() assert eds[0]["_from_structlog"] is True assert isinstance(eds[0]["_record"], logging.LogRecord) def test_native_logger(self, capsys): """ If the log entry comes from structlog, it's unpackaged and processed. """ logger = logging.getLogger() eds = configure_logging(None, logger=logger) get_logger().warning("foo") assert ( "", "[warning ] foo [in test_native_logger]\n", ) == capsys.readouterr() assert eds[0]["_from_structlog"] is True assert isinstance(eds[0]["_record"], logging.LogRecord) def test_foreign_pre_chain_filter_by_level(self, capsys): """ foreign_pre_chain works with filter_by_level processor. """ logger = logging.getLogger() configure_logging([filter_by_level], logger=logger) configure( processors=[ProcessorFormatter.wrap_for_formatter], logger_factory=LoggerFactory(), wrapper_class=BoundLogger, ) logger.warning("foo") assert ( "", "foo [in test_foreign_pre_chain_filter_by_level]\n", ) == capsys.readouterr() def test_processor_and_processors(self): """ Passing both processor and processors raises a TypeError. """ with pytest.raises(TypeError, match="mutually exclusive"): ProcessorFormatter(processor=1, processors=[1]) def test_no_renderer(self): """ Passing neither processor nor processors raises a TypeError. """ with pytest.raises(TypeError, match="must be passed"): ProcessorFormatter() def test_remove_processors_meta(self): """ remove_processors_meta removes _record and _from_structlog. And only them. """ assert {"foo": "bar"} == ProcessorFormatter.remove_processors_meta( None, None, {"foo": "bar", "_record": "foo", "_from_structlog": True}, ) def test_non_string_message_warning(self): """ A warning is raised if the last processor in ProcessorFormatter.processors doesn't return a string. """ configure_logging(None) logger = logging.getLogger() formatter = ProcessorFormatter( processors=[lambda *args, **kwargs: {"foo": "bar"}], ) logger.handlers[0].setFormatter(formatter) with pytest.warns( RuntimeWarning, match="The last processor in ProcessorFormatter.processors must return a string", ): logger.info("baz") def test_logrecord_exc_info(self): """ LogRecord.exc_info is set consistently for structlog and non-structlog log records. """ configure_logging(None) # This doesn't test ProcessorFormatter itself directly, but it's # relevant to setups where ProcessorFormatter is used, i.e. where # handlers will receive LogRecord objects that come from both structlog # and non-structlog loggers. records: Dict[ # noqa: UP006 - dict isn't generic until Python 3.9 str, logging.LogRecord ] = {} class DummyHandler(logging.Handler): def emit(self, record): # Don't do anything; just store the record in the records dict # by its message, so we can assert things about it. if isinstance(record.msg, dict): records[record.msg["event"]] = record else: records[record.msg] = record stdlib_logger = logging.getLogger() structlog_logger = get_logger() # It doesn't matter which logger we add the handler to here. stdlib_logger.addHandler(DummyHandler()) try: raise Exception("foo") except Exception: stdlib_logger.exception("bar") structlog_logger.exception("baz") stdlib_record = records.pop("bar") assert "bar" == stdlib_record.msg assert stdlib_record.exc_info assert Exception is stdlib_record.exc_info[0] assert ("foo",) == stdlib_record.exc_info[1].args structlog_record = records.pop("baz") assert "baz" == structlog_record.msg["event"] assert True is structlog_record.msg["exc_info"] assert structlog_record.exc_info assert Exception is structlog_record.exc_info[0] assert ("foo",) == structlog_record.exc_info[1].args assert not records def test_use_get_message_false(self): """ If use_get_message_is False, the event is obtained using str(record.msg) instead of calling record.getMessage. That means positional formatting is not performed. """ event_dicts = [] def capture(_, __, ed): event_dicts.append(ed.copy()) return str(ed) proc = ProcessorFormatter(processors=[capture], use_get_message=False) record = logging.LogRecord( "foo", logging.INFO, "path.py", 42, "le msg: %s", ("keep separate",), None, ) assert proc.format(record) assert "le msg: %s" == event_dicts[0]["event"] @pytest_asyncio.fixture(name="abl") async def _abl(cl): return AsyncBoundLogger(cl, context={}, processors=[]) class TestAsyncBoundLogger: def test_sync_bl(self, abl, cl): """ AsyncBoungLogger.sync_bl works outside of loops. """ abl.sync_bl.info("test") assert [ CapturedCall(method_name="info", args=(), kwargs={"event": "test"}) ] == cl.calls @pytest.mark.asyncio() async def test_protocol(self, abl): """ AsyncBoundLogger is a proper BindableLogger. """ assert isinstance(abl, BindableLogger) @pytest.mark.asyncio() async def test_correct_levels(self, abl, cl, stdlib_log_method): """ The proxy methods call the correct upstream methods. """ await getattr(abl.bind(foo="bar"), stdlib_log_method)("42") aliases = {"warn": "warning"} alias = aliases.get(stdlib_log_method) expect = alias if alias else stdlib_log_method assert expect == cl.calls[0].method_name @pytest.mark.asyncio() async def test_log_method(self, abl, cl): """ The `log` method is proxied too. """ await abl.bind(foo="bar").log(logging.ERROR, "42") assert "error" == cl.calls[0].method_name @pytest.mark.asyncio() async def test_exception(self, abl, cl): """ `exception` makes sure 'exc_info" is set, if it's not set already. """ try: raise ValueError("omg") except ValueError: await abl.exception("oops") ei = cl.calls[0].kwargs["exc_info"] assert ValueError is ei[0] assert ("omg",) == ei[1].args @pytest.mark.asyncio() async def test_exception_do_not_overwrite(self, abl, cl): """ `exception` leaves exc_info be, if it's set. """ o1 = object() o2 = object() o3 = object() try: raise ValueError("omg") except ValueError: await abl.exception("oops", exc_info=(o1, o2, o3)) ei = cl.calls[0].kwargs["exc_info"] assert (o1, o2, o3) == ei @pytest.mark.asyncio() async def test_bind_unbind(self, cl): """ new/bind/unbind/try_unbind are correctly propagated. """ l1 = AsyncBoundLogger(cl, context={}, processors=[]) l2 = l1.bind(x=42) assert l1 is not l2 assert l1.sync_bl is not l2.sync_bl assert {} == l1._context assert {"x": 42} == l2._context l3 = l2.new(y=23) assert l2 is not l3 assert l2.sync_bl is not l3.sync_bl assert {"y": 23} == l3._context l4 = l3.unbind("y") assert {} == l4._context assert l3 is not l4 # N.B. x isn't bound anymore. l5 = l4.try_unbind("x") assert {} == l5._context assert l4 is not l5 @pytest.mark.asyncio() async def test_integration(self, capsys): """ Configure and log an actual entry. """ configure( processors=[add_log_level, JSONRenderer()], logger_factory=PrintLogger, wrapper_class=AsyncBoundLogger, cache_logger_on_first_use=True, ) logger = get_logger() await logger.bind(foo="bar").info("baz", x="42") assert { "foo": "bar", "x": "42", "event": "baz", "level": "info", } == json.loads(capsys.readouterr().out) @pytest.mark.parametrize("log_level", [None, 45]) def test_recreate_defaults(log_level): """ Recreate defaults configures structlog and -- if asked -- logging. """ logging.basicConfig( stream=sys.stderr, level=1, force=True, ) recreate_defaults(log_level=log_level) assert BoundLogger is _CONFIG.default_wrapper_class assert dict is _CONFIG.default_context_class assert isinstance(_CONFIG.logger_factory, LoggerFactory) log = get_logger().bind() if log_level is not None: assert log_level == log.getEffectiveLevel() else: assert 1 == log.getEffectiveLevel() structlog-24.4.0/tests/test_testing.py0000644000000000000000000001307214645734712015025 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import pytest import structlog from structlog import get_config, get_logger, testing from structlog.testing import ( CapturedCall, CapturingLogger, CapturingLoggerFactory, LogCapture, ReturnLogger, ReturnLoggerFactory, ) class TestCaptureLogs: def test_captures_logs(self): """ Log entries are captured and retain their structure. """ with testing.capture_logs() as logs: get_logger().bind(x="y").info("hello", answer=42) get_logger().bind(a="b").info("goodbye", foo={"bar": "baz"}) assert [ {"event": "hello", "log_level": "info", "x": "y", "answer": 42}, { "a": "b", "event": "goodbye", "log_level": "info", "foo": {"bar": "baz"}, }, ] == logs def get_active_procs(self): return get_config()["processors"] def test_restores_processors_on_success(self): """ Processors are patched within the contextmanager and restored on exit. """ orig_procs = self.get_active_procs() assert len(orig_procs) > 1 with testing.capture_logs(): modified_procs = self.get_active_procs() assert len(modified_procs) == 1 assert isinstance(modified_procs[0], LogCapture) restored_procs = self.get_active_procs() assert orig_procs is restored_procs assert len(restored_procs) > 1 def test_restores_processors_on_error(self): """ Processors are restored even on errors. """ orig_procs = self.get_active_procs() with pytest.raises(NotImplementedError), testing.capture_logs(): raise NotImplementedError("from test") assert orig_procs is self.get_active_procs() def test_captures_bound_logers(self): """ Even logs from already bound loggers are captured and their processors restored on exit. """ logger = get_logger("bound").bind(foo="bar") logger.info("ensure logger is bound") with testing.capture_logs() as logs: logger.info("hello", answer=42) assert logs == [ { "event": "hello", "answer": 42, "foo": "bar", "log_level": "info", } ] def test_captures_log_level_mapping(self): """ exceptions and warn log levels are mapped like in regular loggers. """ structlog.configure( processors=[ structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, ) with testing.capture_logs() as logs: get_logger().exception("hello", answer=42) get_logger().warn("again", answer=23) assert [ { "event": "hello", "answer": 42, "exc_info": True, "log_level": "error", }, { "answer": 23, "event": "again", "log_level": "warning", }, ] == logs class TestReturnLogger: # @pytest.mark.parametrize("method", stdlib_log_methods) def test_stdlib_methods_support(self, stdlib_log_method): """ ReturnLogger implements methods of stdlib loggers. """ v = getattr(ReturnLogger(), stdlib_log_method)("hello") assert "hello" == v def test_return_logger(self): """ Return logger returns exactly what's sent in. """ obj = ["hello"] assert obj is ReturnLogger().msg(obj) class TestReturnLoggerFactory: def test_builds_returnloggers(self): """ Factory returns ReturnLoggers. """ f = ReturnLoggerFactory() assert isinstance(f(), ReturnLogger) def test_caches(self): """ There's no need to have several loggers so we return the same one on each call. """ f = ReturnLoggerFactory() assert f() is f() def test_ignores_args(self): """ ReturnLogger doesn't take positional arguments. If any are passed to the factory, they are not passed to the logger. """ ReturnLoggerFactory()(1, 2, 3) class TestCapturingLogger: def test_factory_caches(self): """ CapturingLoggerFactory returns one CapturingLogger over and over again. """ clf = CapturingLoggerFactory() cl1 = clf() cl2 = clf() assert cl1 is cl2 def test_repr(self): """ repr says how many calls there were. """ cl = CapturingLogger() cl.info("hi") cl.error("yolo") assert "" == repr(cl) def test_captures(self): """ All calls to all names are captured. """ cl = CapturingLogger() cl.info("hi", val=42) cl.trololo("yolo", foo={"bar": "baz"}) assert [ CapturedCall(method_name="info", args=("hi",), kwargs={"val": 42}), CapturedCall( method_name="trololo", args=("yolo",), kwargs={"foo": {"bar": "baz"}}, ), ] == cl.calls structlog-24.4.0/tests/test_threadlocal.py0000644000000000000000000003274414645734712015641 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import inspect import threading import pytest import structlog from structlog import get_logger, wrap_logger from structlog._base import BoundLoggerBase from structlog._config import BoundLoggerLazyProxy from structlog.testing import ReturnLogger from structlog.threadlocal import ( _CONTEXT, as_immutable, bind_threadlocal, bound_threadlocal, clear_threadlocal, get_merged_threadlocal, get_threadlocal, merge_threadlocal, merge_threadlocal_context, tmp_bind, unbind_threadlocal, wrap_dict, ) from tests.utils import CustomError try: import greenlet except ImportError: greenlet = None @pytest.fixture(autouse=True) def _clear_threadlocal(): """ Make sure all tests start with a clean slate. """ with pytest.deprecated_call(): clear_threadlocal() @pytest.fixture(name="D") def _D(): """ Returns a dict wrapped in _ThreadLocalDictWrapper. """ with pytest.deprecated_call(): return wrap_dict(dict) @pytest.fixture(name="log") def _log(logger): """ Returns a ReturnLogger with a freshly wrapped dict. """ with pytest.deprecated_call(): return wrap_logger(logger, context_class=wrap_dict(dict)) @pytest.fixture(name="logger") def _logger(): """ Returns a simple logger stub with a *msg* method that takes one argument which gets returned. """ return ReturnLogger() class TestTmpBind: def test_bind(self, log): """ tmp_bind does not modify the thread-local state. """ log = log.bind(y=23) with pytest.deprecated_call(), tmp_bind(log, x=42, y="foo") as tmp_log: assert ( {"y": "foo", "x": 42} == tmp_log._context._dict == log._context._dict ) assert {"y": 23} == log._context._dict def test_bind_exc(self, log): """ tmp_bind cleans up properly on exceptions. """ log = log.bind(y=23) with pytest.raises( # noqa: PT012 CustomError ), pytest.deprecated_call(), tmp_bind(log, x=42, y="foo") as tmp_log: assert ( {"y": "foo", "x": 42} == tmp_log._context._dict == log._context._dict ) raise CustomError assert {"y": 23} == log._context._dict def test_tmp_bind_lazy(self): """ tmp_bind works with a BoundLoggerLazyProxy -- i.e. before the first bind. """ with pytest.deprecated_call(): structlog.configure(context_class=wrap_dict(dict)) log = get_logger() assert isinstance(log, BoundLoggerLazyProxy) with pytest.deprecated_call(), tmp_bind(log, x=42) as tmp_log: assert {"x": 42} == tmp_log._context._dict assert {} == log._context class TestAsImmutable: def test_does_not_affect_global(self, log): """ A logger from as_mutable is independent from thread local state. """ log = log.new(x=42) with pytest.deprecated_call(): il = as_immutable(log) assert isinstance(il._context, dict) il = il.bind(y=23) assert {"x": 42, "y": 23} == il._context assert {"x": 42} == log._context._dict def test_converts_proxy(self, log): """ as_immutable converts a BoundLoggerLazyProxy into a concrete bound logger. """ with pytest.deprecated_call(): il = as_immutable(log) assert isinstance(il._context, dict) assert isinstance(il, BoundLoggerBase) def test_idempotency(self, log): """ as_immutable on an as_immutable logger works. """ with pytest.deprecated_call(): il = as_immutable(log) with pytest.deprecated_call(): assert isinstance(as_immutable(il), BoundLoggerBase) class TestThreadLocalDict: def test_wrap_returns_distinct_classes(self): """ Each call to wrap_dict returns a distinct new class whose context is independent from others. """ with pytest.deprecated_call(): D1 = wrap_dict(dict) D2 = wrap_dict(dict) assert D1 != D2 assert D1 is not D2 D1.x = 42 D2.x = 23 assert D1.x != D2.x @pytest.mark.skipif( greenlet is not None, reason="Don't mix threads and greenlets." ) def test_is_thread_local(self, D): """ The context is *not* shared between threads. """ class TestThread(threading.Thread): def __init__(self, d): self._d = d threading.Thread.__init__(self) def run(self): assert "tl" not in self._d._dict self._d["tl"] = 23 with pytest.deprecated_call(): d = wrap_dict(dict)() d["tl"] = 42 t = TestThread(d) t.start() t.join() assert 42 == d._dict["tl"] def test_context_is_global_to_thread(self, D): """ The context is shared between all instances of a wrapped class. """ d1 = D({"a": 42}) d2 = D({"b": 23}) d3 = D() assert {"a": 42, "b": 23} == d1._dict == d2._dict == d3._dict assert d1 == d2 == d3 with pytest.deprecated_call(): D_ = wrap_dict(dict) d_ = D_({"a": 42, "b": 23}) assert d1 != d_ def test_init_with_itself_works(self, D): """ Initializing with an instance of the wrapped class will use its values. """ d = D({"a": 42}) assert {"a": 42, "b": 23} == D(d, b=23)._dict def test_iter_works(self, D): """ ___iter__ is proxied to the wrapped class. """ d = D({"a": 42}) assert ["a"] == list(iter(d)) def test_non_dunder_proxy_works(self, D): """ Calls to a non-dunder method get proxied to the wrapped class. """ d = D({"a": 42}) d.clear() assert 0 == len(d) def test_repr(self, D): """ ___repr__ takes the repr of the wrapped class into account. """ r = repr(D({"a": 42})) assert r.startswith("") @pytest.mark.skipif(greenlet is None, reason="Needs greenlet.") def test_is_greenlet_local(self, D): """ Context is shared between greenlets. """ with pytest.deprecated_call(): d = wrap_dict(dict)() d["switch"] = 42 def run(): assert "x" not in d._dict d["switch"] = 23 greenlet.greenlet(run).switch() assert 42 == d._dict["switch"] def test_delattr(self, D): """ ___delattr__ is proxied to the wrapped class. """ d = D() d["delattr"] = 42 assert 42 == d._dict["delattr"] del d.__class__._tl.dict_ def test_delattr_missing(self, D): """ __delattr__ on an inexisting attribute raises AttributeError. """ d = D() with pytest.raises(AttributeError) as e: d._tl.__delattr__("does_not_exist") assert e.value.args[0] in ( "does_not_exist", "'_thread._local' object has no attribute 'does_not_exist'", ) def test_del(self, D): """ ___del__ is proxied to the wrapped class. """ d = D() d["del"] = 13 assert 13 == d._dict["del"] del d["del"] assert "del" not in d._dict def test_new_class(self, D): """ The context of a new wrapped class is empty. """ assert 0 == len(D()) class TestNewThreadLocal: def test_alias(self): """ We're keeping the old alias around. """ assert merge_threadlocal_context is merge_threadlocal def test_bind_and_merge(self): """ Binding a variable causes it to be included in the result of merge_threadlocal. """ with pytest.deprecated_call(): bind_threadlocal(a=1) with pytest.deprecated_call(): assert {"a": 1, "b": 2} == merge_threadlocal(None, None, {"b": 2}) def test_clear(self): """ The thread-local context can be cleared, causing any previously bound variables to not be included in merge_threadlocal's result. """ with pytest.deprecated_call(): bind_threadlocal(a=1) with pytest.deprecated_call(): clear_threadlocal() with pytest.deprecated_call(): assert {"b": 2} == merge_threadlocal(None, None, {"b": 2}) def test_merge_works_without_bind(self): """ merge_threadlocal returns values as normal even when there has been no previous calls to bind_threadlocal. """ with pytest.deprecated_call(): assert {"b": 2} == merge_threadlocal(None, None, {"b": 2}) def test_multiple_binds(self): """ Multiple calls to bind_threadlocal accumulate values instead of replacing them. """ with pytest.deprecated_call(): bind_threadlocal(a=1, b=2) bind_threadlocal(c=3) with pytest.deprecated_call(): assert {"a": 1, "b": 2, "c": 3} == merge_threadlocal( None, None, {"b": 2} ) def test_unbind_threadlocal(self): """ Test that unbinding from threadlocal works for keys that exist and does not raise error when they do not exist. """ with pytest.deprecated_call(): bind_threadlocal(a=234, b=34) with pytest.deprecated_call(): assert {"a": 234, "b": 34} == get_threadlocal() with pytest.deprecated_call(): unbind_threadlocal("a") with pytest.deprecated_call(): assert {"b": 34} == get_threadlocal() with pytest.deprecated_call(): unbind_threadlocal("non-existing-key") with pytest.deprecated_call(): assert {"b": 34} == get_threadlocal() def test_get_context_no_context(self): """ If there is no context yet, _get_context will add it. """ # Don't rely on test order. if hasattr(_CONTEXT, "context"): del _CONTEXT.context with pytest.raises(AttributeError): _CONTEXT.context with pytest.deprecated_call(): assert {} == get_threadlocal() def test_get_merged(self): """ Returns a copy of the threadlocal context merged with the logger's context. """ with pytest.deprecated_call(): bind_threadlocal(x=1) log = structlog.get_logger().bind(y=2) with pytest.deprecated_call(): assert {"x": 1, "y": 2} == get_merged_threadlocal(log) class TestBoundThreadlocal: def test_cleanup(self): """ Bindings are cleaned up """ with pytest.deprecated_call(), bound_threadlocal(x=42, y="foo"): assert {"x": 42, "y": "foo"} == get_threadlocal() with pytest.deprecated_call(): assert {} == get_threadlocal() def test_cleanup_conflict(self): """ Overwritten keys are restored after the clean up """ with pytest.deprecated_call(): bind_threadlocal(x="original", z="unrelated") with bound_threadlocal(x=42, y="foo"): assert { "x": 42, "y": "foo", "z": "unrelated", } == get_threadlocal() with pytest.deprecated_call(): assert {"x": "original", "z": "unrelated"} == get_threadlocal() def test_preserve_independent_bind(self): """ New bindings inside bound_threadlocal are preserved after the clean up """ with pytest.deprecated_call(), bound_threadlocal(x=42): bind_threadlocal(y="foo") assert {"x": 42, "y": "foo"} == get_threadlocal() with pytest.deprecated_call(): assert {"y": "foo"} == get_threadlocal() def test_nesting_works(self): """ bound_threadlocal binds and unbinds even when nested """ with pytest.deprecated_call(): with bound_threadlocal(l1=1): assert {"l1": 1} == get_threadlocal() with bound_threadlocal(l2=2): assert {"l1": 1, "l2": 2} == get_threadlocal() assert {"l1": 1} == get_threadlocal() assert {} == get_threadlocal() def test_as_decorator(self): """ bound_threadlocal can be used as a decorator and it preserves the name, signature and documentation of the wrapped function. """ @bound_threadlocal(x=42) def wrapped(arg1): """Wrapped documentation""" with pytest.deprecated_call(): bind_threadlocal(y=arg1) with pytest.deprecated_call(): assert {"x": 42, "y": arg1} == get_threadlocal() # I can't find a way for the warnings to be raised from the decorator. with pytest.deprecated_call(): wrapped(23) assert "wrapped" == wrapped.__name__ assert "(arg1)" == str(inspect.signature(wrapped)) assert "Wrapped documentation" == wrapped.__doc__ structlog-24.4.0/tests/test_tracebacks.py0000644000000000000000000004657314645734712015466 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. from __future__ import annotations import inspect import json import sys from pathlib import Path from types import ModuleType from typing import Any import pytest from structlog import tracebacks class SecretStr(str): # noqa: SLOT000 """ Secrets representation as used in Typed Settings or Pydantic. """ def __repr__(self) -> str: return "*******" @pytest.fixture(autouse=True) def _unimport_rich(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(tracebacks, "rich", None) def get_next_lineno() -> int: return inspect.currentframe().f_back.f_lineno + 1 @pytest.mark.parametrize(("data", "expected"), [(3, "3"), ("spam", "spam")]) def test_save_str(data: Any, expected: str): """ "safe_str()" returns the str repr of an object. """ assert expected == tracebacks.safe_str(data) def test_safe_str_error(): """ "safe_str()" does not fail if __str__() raises an exception. """ class Baam: def __str__(self) -> str: raise ValueError("BAAM!") with pytest.raises(ValueError, match="BAAM!"): str(Baam()) assert "" == tracebacks.safe_str(Baam()) @pytest.mark.parametrize( ("data", "max_len", "expected"), [ (3, None, "3"), ("spam", None, "'spam'"), (b"spam", None, "b'spam'"), ("bacon", 3, "'bac'+2"), ("bacon", 4, "'baco'+1"), ("bacon", 5, "'bacon'"), (SecretStr("password"), None, "*******"), (["spam", "eggs", "bacon"], 10, "\"['spam', '\"+15"), ], ) def test_to_repr(data: Any, max_len: int | None, expected: str) -> None: """ "to_repr()" returns the repr of an object, trimmed to max_len. """ assert expected == tracebacks.to_repr(data, max_string=max_len) @pytest.mark.parametrize( ("use_rich", "data", "max_len", "expected"), [ (True, 3, None, "3"), (True, "spam", None, "'spam'"), (True, b"spam", None, "b'spam'"), (True, "bacon", 3, "'bac'+2"), (True, "bacon", 5, "'bacon'"), (True, SecretStr("password"), None, "*******"), (True, ["spam", "eggs", "bacon"], 4, "['spam', 'eggs', 'baco'+1]"), (False, "bacon", 3, "'bac'+2"), (False, ["spam", "eggs", "bacon"], 4, '"[\'sp"+21'), ], ) def test_to_repr_rich( use_rich: bool, data: Any, max_len: int | None, expected: str, monkeypatch: pytest.MonkeyPatch, ) -> None: """ "to_repr()" uses Rich to get a nice repr if it is installed and if "use_rich" is True. """ try: import rich except ImportError: pytest.skip(reason="rich not installed") monkeypatch.setattr(tracebacks, "rich", rich) assert expected == tracebacks.to_repr( data, max_string=max_len, use_rich=use_rich ) def test_to_repr_error() -> None: """ "to_repr()" does not fail if __repr__() raises an exception. """ class Baam: def __repr__(self) -> str: raise ValueError("BAAM!") with pytest.raises(ValueError, match="BAAM!"): repr(Baam()) assert "" == tracebacks.to_repr(Baam()) def test_simple_exception(): """ Tracebacks are parsed for simple, single exceptions. """ try: lineno = get_next_lineno() 1 / 0 except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ZeroDivisionError", exc_value="division by zero", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_simple_exception", locals=None, ), ], ), ] == trace.stacks def test_raise_hide_cause(): """ If "raise ... from None" is used, the trace looks like from a simple exception. """ try: try: 1 / 0 except ArithmeticError: lineno = get_next_lineno() raise ValueError("onoes") from None except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ValueError", exc_value="onoes", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_raise_hide_cause", locals=None, ), ], ), ] == trace.stacks def test_raise_with_cause(): """ If "raise ... from orig" is used, the orig trace is included and marked as cause. """ try: try: lineno_1 = get_next_lineno() 1 / 0 except ArithmeticError as orig_exc: lineno_2 = get_next_lineno() raise ValueError("onoes") from orig_exc except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ValueError", exc_value="onoes", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno_2, name="test_raise_with_cause", locals=None, ), ], ), tracebacks.Stack( exc_type="ZeroDivisionError", exc_value="division by zero", syntax_error=None, is_cause=True, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno_1, name="test_raise_with_cause", locals=None, ), ], ), ] == trace.stacks def test_raise_with_cause_no_tb(): """ If an exception's cause has no traceback, that cause is ignored. """ try: lineno = get_next_lineno() raise ValueError("onoes") from RuntimeError("I am fake") except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ValueError", exc_value="onoes", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_raise_with_cause_no_tb", locals=None, ), ], ), ] == trace.stacks def test_raise_nested(): """ If an exc is raised during handling another one, the orig trace is included. """ try: try: lineno_1 = get_next_lineno() 1 / 0 except ArithmeticError: lineno_2 = get_next_lineno() raise ValueError("onoes") # noqa: B904 except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ValueError", exc_value="onoes", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno_2, name="test_raise_nested", locals=None, ), ], ), tracebacks.Stack( exc_type="ZeroDivisionError", exc_value="division by zero", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno_1, name="test_raise_nested", locals=None, ), ], ), ] == trace.stacks def test_raise_no_msg(): """ If exception classes (not instances) are raised, "exc_value" is an empty string. """ try: lineno = get_next_lineno() raise RuntimeError except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="RuntimeError", exc_value="", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_raise_no_msg", locals=None, ), ], ), ] == trace.stacks def test_syntax_error(): """ For SyntaxError, extra info about that error is added to the trace. """ try: # raises SyntaxError: invalid syntax lineno = get_next_lineno() eval("2 +* 2") except SyntaxError as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="SyntaxError", exc_value="invalid syntax (, line 1)", syntax_error=tracebacks.SyntaxError_( offset=4, filename="", line="2 +* 2", lineno=1, msg="invalid syntax", ), is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_syntax_error", ), ], ), ] == trace.stacks def test_filename_with_bracket(): """ Filenames with brackets (e.g., "") are handled properly. """ try: lineno = get_next_lineno() exec(compile("1/0", filename="", mode="exec")) except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ZeroDivisionError", exc_value="division by zero", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_filename_with_bracket", locals=None, ), tracebacks.Frame( filename="", lineno=1, name="", locals=None, ), ], ), ] == trace.stacks def test_filename_not_a_file(): """ "Invalid" filenames are appended to CWD as if they were actual files. """ try: lineno = get_next_lineno() exec(compile("1/0", filename="string", mode="exec")) except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) assert [ tracebacks.Stack( exc_type="ZeroDivisionError", exc_value="division by zero", syntax_error=None, is_cause=False, frames=[ tracebacks.Frame( filename=__file__, lineno=lineno, name="test_filename_not_a_file", locals=None, ), tracebacks.Frame( filename=str(Path.cwd().joinpath("string")), lineno=1, name="", locals=None, ), ], ), ] == trace.stacks def test_show_locals(): """ Local variables in each frame can optionally be captured. """ def bar(a): print(1 / a) def foo(a): bar(a) try: foo(0) except Exception as e: trace = tracebacks.extract( type(e), e, e.__traceback__, show_locals=True ) stack_locals = [f.locals for f in trace.stacks[0].frames] # The first frames contain functions with "random" memory addresses, # so we only check the variable names: assert stack_locals[0].keys() == {"foo", "e", "bar"} assert stack_locals[1].keys() == {"a", "bar"} assert stack_locals[2] == {"a": "0"} def test_recursive(): """ Recursion errors give a lot of frames but don't break stuff. """ def foo(n): return bar(n) def bar(n): return foo(n) try: lineno = get_next_lineno() foo(1) except Exception as e: trace = tracebacks.extract(type(e), e, e.__traceback__) frames = trace.stacks[0].frames trace.stacks[0].frames = [] assert [ tracebacks.Stack( exc_type="RecursionError", exc_value="maximum recursion depth exceeded", syntax_error=None, is_cause=False, frames=[], ), ] == trace.stacks assert ( len(frames) > sys.getrecursionlimit() - 50 ) # Buffer for frames from pytest assert ( tracebacks.Frame( filename=__file__, lineno=lineno, name="test_recursive", ) == frames[0] ) # If we run the tests under Python 3.12 with sysmon enabled, it inserts # frames at the end. if sys.version_info >= (3, 12): frames = [f for f in frames if "coverage" not in f.filename] # Depending on whether we invoke pytest directly or run tox, either "foo()" # or "bar()" is at the end of the stack. assert frames[-1] in [ tracebacks.Frame( filename=__file__, lineno=lineno - 7, name="foo", ), tracebacks.Frame( filename=__file__, lineno=lineno - 4, name="bar", ), ] def test_json_traceback(): """ Tracebacks are formatted to JSON with all information. """ try: lineno = get_next_lineno() 1 / 0 except Exception as e: format_json = tracebacks.ExceptionDictTransformer(show_locals=False) result = format_json((type(e), e, e.__traceback__)) assert [ { "exc_type": "ZeroDivisionError", "exc_value": "division by zero", "frames": [ { "filename": __file__, "lineno": lineno, "name": "test_json_traceback", } ], "is_cause": False, "syntax_error": None, }, ] == result def test_json_traceback_locals_max_string(): """ Local variables in each frame are trimmed to locals_max_string. """ try: _var = "spamspamspam" lineno = get_next_lineno() 1 / 0 except Exception as e: result = tracebacks.ExceptionDictTransformer(locals_max_string=4)( (type(e), e, e.__traceback__) ) assert [ { "exc_type": "ZeroDivisionError", "exc_value": "division by zero", "frames": [ { "filename": __file__, "lineno": lineno, "locals": { "_var": "'spam'+8", "e": "'Zero'+33", "lineno": str(lineno), }, "name": "test_json_traceback_locals_max_string", } ], "is_cause": False, "syntax_error": None, }, ] == result @pytest.mark.parametrize( ("max_frames", "expected_frames", "skipped_idx", "skipped_count"), [ (2, 3, 1, 2), (3, 3, 1, 2), (4, 4, -1, 0), (5, 4, -1, 0), ], ) def test_json_traceback_max_frames( max_frames: int, expected_frames: int, skipped_idx: int, skipped_count: int ): """ Only max_frames frames are included in the traceback and the skipped frames are reported. """ def spam(): return 1 / 0 def eggs(): spam() def bacon(): eggs() try: bacon() except Exception as e: format_json = tracebacks.ExceptionDictTransformer( show_locals=False, max_frames=max_frames ) result = format_json((type(e), e, e.__traceback__)) trace = result[0] assert len(trace["frames"]) == expected_frames, trace["frames"] if skipped_count: assert trace["frames"][skipped_idx] == { "filename": "", "lineno": -1, "name": f"Skipped frames: {skipped_count}", } else: assert not any(f["lineno"] == -1 for f in trace["frames"]) @pytest.mark.parametrize( ("suppress", "file_no_locals"), [ pytest.param((__file__,), __file__, id="file"), pytest.param((json,), json.__file__, id="json"), ], ) def test_json_tracebacks_suppress( suppress: tuple[str | ModuleType, ...], file_no_locals: str, capsys: pytest.CaptureFixture, ) -> None: """ Console and JSON output look as expected This means also warnings are logged correctly. """ try: try: json.loads(42) # type: ignore[arg-type] except Exception as e: raise ValueError("error shown to users") from e except ValueError as e: format_json = tracebacks.ExceptionDictTransformer( show_locals=True, suppress=suppress ) result = format_json((type(e), e, e.__traceback__)) for stack in result: for frame in stack["frames"]: no_locals = frame["filename"] == file_no_locals if no_locals: assert "locals" not in frame else: assert "locals" in frame @pytest.mark.parametrize( ("hide_sunder", "hide_dunder", "expected"), [ (False, False, {"_spam", "__eggs"}), (True, False, set()), # Also hides "__eggs", b/c it starts with "_"! (False, True, {"_spam"}), (True, True, set()), ], ) def test_json_tracebacks_skip_sunder_dunder( hide_sunder: bool, hide_dunder: bool, expected: set[str] ) -> None: """ Local variables starting with "_" or "__" can be hidden from the locals dict. """ def func() -> None: _spam = True __eggs = 3 1 / 0 try: func() except ZeroDivisionError as e: format_json = tracebacks.ExceptionDictTransformer( show_locals=True, locals_hide_sunder=hide_sunder, locals_hide_dunder=hide_dunder, ) result = format_json((type(e), e, e.__traceback__)) assert expected == set(result[0]["frames"][1]["locals"]) @pytest.mark.parametrize( "kwargs", [ {"locals_max_length": -1}, {"locals_max_string": -1}, {"max_frames": -1}, {"max_frames": 0}, {"max_frames": 1}, {"suppress": (json,)}, ], ) def test_json_traceback_value_error( kwargs: dict[str, Any], monkeypatch: pytest.MonkeyPatch ) -> None: """ Wrong arguments to ExceptionDictTransformer raise a ValueError that contains the name of the argument.. """ if "suppress" in kwargs: monkeypatch.setattr(kwargs["suppress"][0], "__file__", None) with pytest.raises(ValueError, match=next(iter(kwargs.keys()))): tracebacks.ExceptionDictTransformer(**kwargs) structlog-24.4.0/tests/test_twisted.py0000644000000000000000000002402714645734712015035 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import json import pytest from pretend import call_recorder from structlog import ReturnLogger from structlog._config import _CONFIG from structlog.processors import KeyValueRenderer try: from twisted.python.failure import Failure from twisted.python.log import ILogObserver from structlog.twisted import ( BoundLogger, EventAdapter, JSONLogObserverWrapper, JSONRenderer, LoggerFactory, PlainFileLogObserver, ReprWrapper, _extractStuffAndWhy, plainJSONStdOutLogger, ) except ImportError: pytest.skip(allow_module_level=True) def test_LoggerFactory(): """ Logger factory ultimately returns twisted.python.log for output. """ from twisted.python import log assert log is LoggerFactory()() def _render_repr(_, __, event_dict): return repr(event_dict) def build_bl(logger=None, processors=None, context=None): """ Convenience function to build BoundLoggerses with sane defaults. """ return BoundLogger( logger or ReturnLogger(), processors or [KeyValueRenderer()], context if context is not None else _CONFIG.default_context_class(), ) class TestBoundLogger: def test_msg(self): """ log.msg renders correctly. """ bl = build_bl() assert "foo=42 event='event'" == bl.msg("event", foo=42) def test_errVanilla(self): """ log.err renders correctly if no failure is attached. """ bl = build_bl() assert "foo=42 event='event'" == bl.err("event", foo=42) def test_errWithFailure(self): """ Failures are correctly injected into the log entries. """ bl = build_bl( processors=[EventAdapter(dictRenderer=KeyValueRenderer())] ) try: raise ValueError except ValueError: # Use str() for comparison to avoid tricky # deep-compares of Failures. assert str( ( (), { "_stuff": Failure(ValueError()), "_why": "foo=42 event='event'", }, ) ) == str(bl.err("event", foo=42)) class TestExtractStuffAndWhy: def test_extractFailsOnTwoFailures(self): """ Raise ValueError if both _stuff and event contain exceptions. """ with pytest.raises( ValueError, match="Both _stuff and event contain an Exception/Failure.", ): _extractStuffAndWhy( { "_stuff": Failure(ValueError()), "event": Failure(TypeError()), } ) def test_failsOnConflictingEventAnd_why(self): """ Raise ValueError if both _why and event are in the event_dict. """ with pytest.raises( ValueError, match="Both `_why` and `event` supplied." ): _extractStuffAndWhy({"_why": "foo", "event": "bar"}) def test_handlesFailures(self): """ Extracts failures and events. """ f = Failure(ValueError()) assert ({"value": f}, "foo", {}) == _extractStuffAndWhy( {"_why": "foo", "_stuff": {"value": f}} ) assert ({"value": f}, None, {}) == _extractStuffAndWhy( {"_stuff": {"value": f}} ) def test_handlesMissingFailure(self): """ Missing failures extract a None. """ assert (None, "foo", {}) == _extractStuffAndWhy({"event": "foo"}) class TestEventAdapter: """ Some tests here are redundant because they predate _extractStuffAndWhy. """ def test_EventAdapterFormatsLog(self): """ EventAdapter formats log entries correctly. """ la = EventAdapter(_render_repr) assert "{'foo': 'bar'}" == la(None, "msg", {"foo": "bar"}) def test_transforms_whyIntoEvent(self): """ log.err(_stuff=exc, _why='foo') makes the output 'event="foo"' """ la = EventAdapter(_render_repr) error = ValueError("test") rv = la(None, "err", {"_stuff": error, "_why": "foo", "event": None}) assert () == rv[0] assert isinstance(rv[1]["_stuff"], Failure) assert error == rv[1]["_stuff"].value assert "{'event': 'foo'}" == rv[1]["_why"] def test_worksUsualCase(self): """ log.err(exc, _why='foo') makes the output 'event="foo"' """ la = EventAdapter(_render_repr) error = ValueError("test") rv = la(None, "err", {"event": error, "_why": "foo"}) assert () == rv[0] assert isinstance(rv[1]["_stuff"], Failure) assert error == rv[1]["_stuff"].value assert "{'event': 'foo'}" == rv[1]["_why"] def test_allKeywords(self): """ log.err(_stuff=exc, _why='event') """ la = EventAdapter(_render_repr) error = ValueError("test") rv = la(None, "err", {"_stuff": error, "_why": "foo"}) assert () == rv[0] assert isinstance(rv[1]["_stuff"], Failure) assert error == rv[1]["_stuff"].value assert "{'event': 'foo'}" == rv[1]["_why"] def test_noFailure(self): """ log.err('event') """ la = EventAdapter(_render_repr) assert ((), {"_stuff": None, "_why": "{'event': 'someEvent'}"}) == la( None, "err", {"event": "someEvent"} ) def test_noFailureWithKeyword(self): """ log.err(_why='event') """ la = EventAdapter(_render_repr) assert ((), {"_stuff": None, "_why": "{'event': 'someEvent'}"}) == la( None, "err", {"_why": "someEvent"} ) def test_catchesConflictingEventAnd_why(self): """ Passing both _why and event raises a ValueError. """ la = EventAdapter(_render_repr) with pytest.raises( ValueError, match="Both `_why` and `event` supplied." ): la(None, "err", {"event": "someEvent", "_why": "someReason"}) @pytest.fixture() def jr(): """ A plain Twisted JSONRenderer. """ return JSONRenderer() class TestJSONRenderer: def test_dumpsKWsAreHandedThrough(self, jr): """ JSONRenderer allows for setting arguments that are passed to json.dumps(). Make sure they are passed. """ d = {"x": "foo"} d.update(a="bar") jr_sorted = JSONRenderer(sort_keys=True) assert jr_sorted(None, "err", d) != jr(None, "err", d) def test_handlesMissingFailure(self, jr): """ Calling err without an actual failure works and returns the event as a string wrapped in ReprWrapper. """ assert ( ReprWrapper('{"event": "foo"}') == jr(None, "err", {"event": "foo"})[0][0] ) assert ( ReprWrapper('{"event": "foo"}') == jr(None, "err", {"_why": "foo"})[0][0] ) def test_msgWorksToo(self, jr): """ msg renders the event as a string and wraps it using ReprWrapper. """ assert ( ReprWrapper('{"event": "foo"}') == jr(None, "msg", {"_why": "foo"})[0][0] ) def test_handlesFailure(self, jr): """ JSONRenderer renders failures correctly. """ rv = jr(None, "err", {"event": Failure(ValueError())})[0][0].string assert "Failure: builtins.ValueError" in rv assert '"event": "error"' in rv def test_setsStructLogField(self, jr): """ Formatted entries are marked so they can be identified without guessing for example in JSONLogObserverWrapper. """ assert {"_structlog": True} == jr(None, "msg", {"_why": "foo"})[1] class TestReprWrapper: def test_repr(self): """ The repr of the wrapped string is the vanilla string without quotes. """ assert "foo" == repr(ReprWrapper("foo")) class TestPlainFileLogObserver: def test_isLogObserver(self, sio): """ PlainFileLogObserver is an ILogObserver. """ assert ILogObserver.providedBy(PlainFileLogObserver(sio)) def test_writesOnlyMessageWithLF(self, sio): """ PlainFileLogObserver writes only the message and a line feed. """ PlainFileLogObserver(sio)( {"system": "some system", "message": ("hello",)} ) assert "hello\n" == sio.getvalue() class TestJSONObserverWrapper: def test_IsAnObserver(self): """ JSONLogObserverWrapper is an ILogObserver. """ assert ILogObserver.implementedBy(JSONLogObserverWrapper) def test_callsWrappedObserver(self): """ The wrapper always runs the wrapped observer in the end. """ o = call_recorder(lambda *a, **kw: None) JSONLogObserverWrapper(o)({"message": ("hello",)}) assert 1 == len(o.calls) def test_jsonifiesPlainLogEntries(self): """ Entries that aren't formatted by JSONRenderer are rendered as JSON now. """ o = call_recorder(lambda *a, **kw: None) JSONLogObserverWrapper(o)({"message": ("hello",), "system": "-"}) msg = json.loads(o.calls[0].args[0]["message"][0]) assert msg == {"event": "hello", "system": "-"} def test_leavesStructLogAlone(self): """ Entries that are formatted by JSONRenderer are left alone. """ d = {"message": ("hello",), "_structlog": True} def verify(eventDict): assert d == eventDict JSONLogObserverWrapper(verify)(d) class TestPlainJSONStdOutLogger: def test_isLogObserver(self): """ plainJSONStdOutLogger is an ILogObserver. """ assert ILogObserver.providedBy(plainJSONStdOutLogger()) structlog-24.4.0/tests/test_utils.py0000644000000000000000000000417214645734712014511 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. import multiprocessing import sys import pytest from structlog._utils import get_processname class TestGetProcessname: def test_default(self): """ The returned process name matches the name of the current process from the `multiprocessing` module. """ assert get_processname() == multiprocessing.current_process().name def test_changed(self, monkeypatch: pytest.MonkeyPatch): """ The returned process name matches the name of the current process from the `multiprocessing` module if it is not the default. """ tmp_name = "fakename" monkeypatch.setattr( target=multiprocessing.current_process(), name="name", value=tmp_name, ) assert get_processname() == tmp_name def test_no_multiprocessing(self, monkeypatch: pytest.MonkeyPatch) -> None: """ The returned process name is the default process name if the `multiprocessing` module is not available. """ tmp_name = "fakename" monkeypatch.setattr( target=multiprocessing.current_process(), name="name", value=tmp_name, ) monkeypatch.setattr( target=sys, name="modules", value={}, ) assert get_processname() == "n/a" def test_exception(self, monkeypatch: pytest.MonkeyPatch) -> None: """ The returned process name is the default process name when an exception is thrown when an attempt is made to retrieve the current process name from the `multiprocessing` module. """ def _current_process() -> None: raise RuntimeError("test") monkeypatch.setattr( target=multiprocessing, name="current_process", value=_current_process, ) assert get_processname() == "n/a" structlog-24.4.0/tests/utils.py0000644000000000000000000000072114645734712013446 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Shared test utilities. """ from structlog._log_levels import NAME_TO_LEVEL stdlib_log_methods = [m for m in NAME_TO_LEVEL if m != "notset"] class CustomError(Exception): """ Custom exception for testing purposes. """ structlog-24.4.0/tests/processors/__init__.py0000644000000000000000000000000014645734712016235 0ustar00structlog-24.4.0/tests/processors/test_processors.py0000644000000000000000000004625714645734712017767 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. from __future__ import annotations import functools import inspect import itertools import json import logging import os import pickle import sys import threading from io import StringIO import pytest import structlog from structlog import BoundLogger from structlog._utils import get_processname from structlog.processors import ( CallsiteParameter, CallsiteParameterAdder, EventRenamer, ExceptionPrettyPrinter, JSONRenderer, StackInfoRenderer, UnicodeDecoder, UnicodeEncoder, _figure_out_exc_info, format_exc_info, ) from structlog.stdlib import ProcessorFormatter from structlog.typing import EventDict from ..additional_frame import additional_frame try: import simplejson except ImportError: simplejson = None class TestUnicodeEncoder: def test_encodes(self): """ Unicode strings get encoded (as UTF-8 by default). """ e = UnicodeEncoder() assert {"foo": b"b\xc3\xa4r"} == e(None, None, {"foo": "b\xe4r"}) def test_passes_arguments(self): """ Encoding options are passed into the encoding call. """ e = UnicodeEncoder("latin1", "xmlcharrefreplace") assert {"foo": b"–"} == e(None, None, {"foo": "\u2013"}) def test_bytes_nop(self): """ If the string is already bytes, don't do anything. """ e = UnicodeEncoder() assert {"foo": b"b\xc3\xa4r"} == e(None, None, {"foo": b"b\xc3\xa4r"}) class TestUnicodeDecoder: def test_decodes(self): """ Byte strings get decoded (as UTF-8 by default). """ ud = UnicodeDecoder() assert {"foo": "b\xe4r"} == ud(None, None, {"foo": b"b\xc3\xa4r"}) def test_passes_arguments(self): """ Encoding options are passed into the encoding call. """ ud = UnicodeDecoder("utf-8", "ignore") assert {"foo": ""} == ud(None, None, {"foo": b"\xa1\xa4"}) def test_bytes_nop(self): """ If the value is already unicode, don't do anything. """ ud = UnicodeDecoder() assert {"foo": "b\u2013r"} == ud(None, None, {"foo": "b\u2013r"}) class TestExceptionPrettyPrinter: def test_stdout_by_default(self): """ If no file is supplied, use stdout. """ epp = ExceptionPrettyPrinter() assert sys.stdout is epp._file def test_prints_exception(self, sio): """ If there's an `exception` key in the event_dict, just print it out. This happens if `format_exc_info` was run before us in the chain. """ epp = ExceptionPrettyPrinter(file=sio) try: raise ValueError except ValueError: ed = format_exc_info(None, None, {"exc_info": True}) epp(None, None, ed) out = sio.getvalue() assert "test_prints_exception" in out assert "raise ValueError" in out def test_removes_exception_after_printing(self, sio): """ After pretty printing `exception` is removed from the event_dict. """ epp = ExceptionPrettyPrinter(sio) try: raise ValueError except ValueError: ed = format_exc_info(None, None, {"exc_info": True}) assert "exception" in ed new_ed = epp(None, None, ed) assert "exception" not in new_ed def test_handles_exc_info(self, sio): """ If `exc_info` is passed in, it behaves like `format_exc_info`. """ epp = ExceptionPrettyPrinter(sio) try: raise ValueError except ValueError: epp(None, None, {"exc_info": True}) out = sio.getvalue() assert "test_handles_exc_info" in out assert "raise ValueError" in out def test_removes_exc_info_after_printing(self, sio): """ After pretty printing `exception` is removed from the event_dict. """ epp = ExceptionPrettyPrinter(sio) try: raise ValueError except ValueError: ed = epp(None, None, {"exc_info": True}) assert "exc_info" not in ed def test_nop_if_no_exception(self, sio): """ If there is no exception, don't print anything. """ epp = ExceptionPrettyPrinter(sio) epp(None, None, {}) assert "" == sio.getvalue() def test_own_exc_info(self, sio): """ If exc_info is a tuple, use it. """ epp = ExceptionPrettyPrinter(sio) try: raise ValueError("XXX") except ValueError: ei = sys.exc_info() epp(None, None, {"exc_info": ei}) assert "XXX" in sio.getvalue() def test_exception_on_py3(self, sio): """ On Python 3, it's also legal to pass an Exception. """ epp = ExceptionPrettyPrinter(sio) try: raise ValueError("XXX") except ValueError as e: epp(None, None, {"exc_info": e}) assert "XXX" in sio.getvalue() @pytest.fixture() def sir(): return StackInfoRenderer() class TestStackInfoRenderer: def test_removes_stack_info(self, sir): """ The `stack_info` key is removed from `event_dict`. """ ed = sir(None, None, {"stack_info": True}) assert "stack_info" not in ed def test_adds_stack_if_asked(self, sir): """ If `stack_info` is true, `stack` is added. """ ed = sir(None, None, {"stack_info": True}) assert "stack" in ed def test_renders_correct_stack(self, sir): """ The rendered stack is correct. """ ed = sir(None, None, {"stack_info": True}) assert 'ed = sir(None, None, {"stack_info": True})' in ed["stack"] def test_additional_ignores(self): """ Filtering of names works. """ sir = StackInfoRenderer(["tests.additional_frame"]) ed = additional_frame( functools.partial(sir, None, None, {"stack_info": True}) ) assert "additional_frame.py" not in ed["stack"] class TestFigureOutExcInfo: @pytest.mark.parametrize("true_value", [True, 1, 1.1]) def test_obtains_exc_info_on_True(self, true_value): """ If the passed argument evaluates to True obtain exc_info ourselves. """ try: 0 / 0 except Exception: assert sys.exc_info() == _figure_out_exc_info(true_value) else: pytest.fail("Exception not raised.") def test_py3_exception_no_traceback(self): """ Exceptions without tracebacks are simply returned with None for traceback. """ e = ValueError() assert (e.__class__, e, None) == _figure_out_exc_info(e) class TestCallsiteParameterAdder: parameter_strings = { "pathname", "filename", "module", "func_name", "lineno", "thread", "thread_name", "process", "process_name", } _all_parameters = set(CallsiteParameter) def test_all_parameters(self) -> None: """ All callsite parameters are included in ``self.parameter_strings`` and the dictionary returned by ``self.get_callsite_parameters`` contains keys for all callsite parameters. """ assert self.parameter_strings == { member.value for member in self._all_parameters } assert self.parameter_strings == self.get_callsite_parameters().keys() @pytest.mark.asyncio() @pytest.mark.parametrize( ("wrapper_class", "method_name"), [ (structlog.stdlib.BoundLogger, "ainfo"), (structlog.stdlib.AsyncBoundLogger, "info"), ], ) async def test_async(self, wrapper_class, method_name) -> None: """ Callsite information for async invocations are correct. """ string_io = StringIO() class StringIOLogger(structlog.PrintLogger): def __init__(self): super().__init__(file=string_io) processor = self.make_processor(None, ["concurrent", "threading"]) structlog.configure( processors=[processor, JSONRenderer()], logger_factory=StringIOLogger, wrapper_class=wrapper_class, cache_logger_on_first_use=True, ) logger = structlog.stdlib.get_logger() callsite_params = self.get_callsite_parameters() await getattr(logger, method_name)("baz") logger_params = json.loads(string_io.getvalue()) # These are different when running under async for key in ["thread", "thread_name"]: callsite_params.pop(key) logger_params.pop(key) assert {"event": "baz", **callsite_params} == logger_params def test_additional_ignores(self, monkeypatch: pytest.MonkeyPatch) -> None: """ Stack frames from modules with names that start with values in `additional_ignores` are ignored when determining the callsite. """ test_message = "test message" additional_ignores = ["tests.additional_frame"] processor = self.make_processor(None, additional_ignores) event_dict: EventDict = {"event": test_message} # Warning: the next two lines must appear exactly like this to make # line numbers match. callsite_params = self.get_callsite_parameters(1) actual = processor(None, None, event_dict) expected = {"event": test_message, **callsite_params} assert expected == actual @pytest.mark.parametrize( ("origin", "parameter_strings"), itertools.product( ["logging", "structlog"], [ None, *[{parameter} for parameter in parameter_strings], set(), parameter_strings, {"pathname", "filename"}, {"module", "func_name"}, ], ), ) def test_processor( self, origin: str, parameter_strings: set[str] | None, ): """ The correct callsite parameters are added to event dictionaries. """ test_message = "test message" processor = self.make_processor(parameter_strings) if origin == "structlog": event_dict: EventDict = {"event": test_message} callsite_params = self.get_callsite_parameters() actual = processor(None, None, event_dict) elif origin == "logging": callsite_params = self.get_callsite_parameters() record = logging.LogRecord( "name", logging.INFO, callsite_params["pathname"], callsite_params["lineno"], test_message, None, None, callsite_params["func_name"], ) event_dict: EventDict = { "event": test_message, "_record": record, "_from_structlog": False, } actual = processor(None, None, event_dict) else: pytest.fail(f"invalid origin {origin}") actual = { key: value for key, value in actual.items() if not key.startswith("_") } callsite_params = self.filter_parameter_dict( callsite_params, parameter_strings ) expected = {"event": test_message, **callsite_params} assert expected == actual @pytest.mark.parametrize( ("setup", "origin", "parameter_strings"), itertools.product( ["common-without-pre", "common-with-pre", "shared", "everywhere"], ["logging", "structlog"], [ None, *[{parameter} for parameter in parameter_strings], set(), parameter_strings, {"pathname", "filename"}, {"module", "func_name"}, ], ), ) def test_e2e( self, setup: str, origin: str, parameter_strings: set[str] | None, ) -> None: """ Logging output contains the correct callsite parameters. """ logger = logging.Logger(sys._getframe().f_code.co_name) string_io = StringIO() handler = logging.StreamHandler(string_io) processors = [self.make_processor(parameter_strings)] if setup == "common-without-pre": common_processors = processors formatter = ProcessorFormatter( processors=[*processors, JSONRenderer()] ) elif setup == "common-with-pre": common_processors = processors formatter = ProcessorFormatter( foreign_pre_chain=processors, processors=[JSONRenderer()], ) elif setup == "shared": common_processors = [] formatter = ProcessorFormatter( processors=[*processors, JSONRenderer()], ) elif setup == "everywhere": common_processors = processors formatter = ProcessorFormatter( foreign_pre_chain=processors, processors=[*processors, JSONRenderer()], ) else: pytest.fail(f"invalid setup {setup}") handler.setFormatter(formatter) handler.setLevel(0) logger.addHandler(handler) logger.setLevel(0) test_message = "test message" if origin == "logging": callsite_params = self.get_callsite_parameters() logger.info(test_message) elif origin == "structlog": ctx = {} bound_logger = BoundLogger( logger, [*common_processors, ProcessorFormatter.wrap_for_formatter], ctx, ) callsite_params = self.get_callsite_parameters() bound_logger.info(test_message) else: pytest.fail(f"invalid origin {origin}") callsite_params = self.filter_parameter_dict( callsite_params, parameter_strings ) actual = { key: value for key, value in json.loads(string_io.getvalue()).items() if not key.startswith("_") } expected = {"event": test_message, **callsite_params} assert expected == actual def test_pickeable_callsite_parameter_adder(self) -> None: """ An instance of ``CallsiteParameterAdder`` can be pickled. This functionality may be used to propagate structlog configurations to subprocesses. """ pickle.dumps(CallsiteParameterAdder()) @classmethod def make_processor( cls, parameter_strings: set[str] | None, additional_ignores: list[str] | None = None, ) -> CallsiteParameterAdder: """ Creates a ``CallsiteParameterAdder`` with parameters matching the supplied *parameter_strings* values and with the supplied *additional_ignores* values. Args: parameter_strings: Strings for which corresponding ``CallsiteParameters`` should be included in the resulting ``CallsiteParameterAdded``. additional_ignores: Used as *additional_ignores* for the resulting ``CallsiteParameterAdded``. """ if parameter_strings is None: return CallsiteParameterAdder( additional_ignores=additional_ignores ) parameters = cls.filter_parameters(parameter_strings) return CallsiteParameterAdder( parameters=parameters, additional_ignores=additional_ignores, ) @classmethod def filter_parameters( cls, parameter_strings: set[str] | None ) -> set[CallsiteParameter]: """ Returns a set containing all ``CallsiteParameter`` members with values that are in ``parameter_strings``. Args: parameter_strings: The parameters strings for which corresponding ``CallsiteParameter`` members should be returned. If this value is `None` then all ``CallsiteParameter`` will be returned. """ if parameter_strings is None: return cls._all_parameters return { parameter for parameter in cls._all_parameters if parameter.value in parameter_strings } @classmethod def filter_parameter_dict( cls, input: dict[str, object], parameter_strings: set[str] | None ) -> dict[str, object]: """ Returns a dictionary that is equivalent to *input* but with all keys not in *parameter_strings* removed. Args: parameter_strings: The keys to keep in the dictionary, if this value is ``None`` then all keys matching ``cls.parameter_strings`` are kept. """ if parameter_strings is None: parameter_strings = cls.parameter_strings return { key: value for key, value in input.items() if key in parameter_strings } @classmethod def get_callsite_parameters(cls, offset: int = 1) -> dict[str, object]: """ This function creates dictionary of callsite parameters for the line that is ``offset`` lines after the invocation of this function. Args: offset: The amount of lines after the invocation of this function that callsite parameters should be generated for. """ frame_info = inspect.stack()[1] frame_traceback = inspect.getframeinfo(frame_info[0]) return { "pathname": frame_traceback.filename, "filename": os.path.basename(frame_traceback.filename), "module": os.path.splitext( os.path.basename(frame_traceback.filename) )[0], "func_name": frame_info.function, "lineno": frame_info.lineno + offset, "thread": threading.get_ident(), "thread_name": threading.current_thread().name, "process": os.getpid(), "process_name": get_processname(), } class TestRenameKey: def test_rename_once(self): """ Renaming event to something else works. """ assert {"msg": "hi", "foo": "bar"} == EventRenamer("msg")( None, None, {"event": "hi", "foo": "bar"} ) def test_rename_twice(self): """ Renaming both from and to `event` works. """ assert { "msg": "hi", "event": "fabulous", "foo": "bar", } == EventRenamer("msg", "_event")( None, None, {"event": "hi", "foo": "bar", "_event": "fabulous"} ) def test_replace_by_key_is_optional(self): """ The key that is renamed to `event` doesn't have to exist. """ assert {"msg": "hi", "foo": "bar"} == EventRenamer("msg", "missing")( None, None, {"event": "hi", "foo": "bar"} ) structlog-24.4.0/tests/processors/test_renderers.py0000644000000000000000000004102614645734712017543 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. from __future__ import annotations import datetime import json import pickle import pytest from freezegun import freeze_time from structlog.processors import ( ExceptionRenderer, JSONRenderer, KeyValueRenderer, LogfmtRenderer, MaybeTimeStamper, TimeStamper, _json_fallback_handler, format_exc_info, ) from structlog.threadlocal import wrap_dict from ..utils import CustomError try: import simplejson except ImportError: simplejson = None class TestKeyValueRenderer: def test_sort_keys(self, event_dict): """ Keys are sorted if sort_keys is set. """ rv = KeyValueRenderer(sort_keys=True)(None, None, event_dict) assert r"a= b=[3, 4] x=7 y='test' z=(1, 2)" == rv def test_order_complete(self, event_dict): """ Orders keys according to key_order. """ rv = KeyValueRenderer(key_order=["y", "b", "a", "z", "x"])( None, None, event_dict ) assert r"y='test' b=[3, 4] a= z=(1, 2) x=7" == rv def test_order_missing(self, event_dict): """ Missing keys get rendered as None. """ rv = KeyValueRenderer(key_order=["c", "y", "b", "a", "z", "x"])( None, None, event_dict ) assert r"c=None y='test' b=[3, 4] a= z=(1, 2) x=7" == rv def test_order_missing_dropped(self, event_dict): """ Missing keys get dropped """ rv = KeyValueRenderer( key_order=["c", "y", "b", "a", "z", "x"], drop_missing=True )(None, None, event_dict) assert r"y='test' b=[3, 4] a= z=(1, 2) x=7" == rv def test_order_extra(self, event_dict): """ Extra keys get sorted if sort_keys=True. """ event_dict["B"] = "B" event_dict["A"] = "A" rv = KeyValueRenderer( key_order=["c", "y", "b", "a", "z", "x"], sort_keys=True )(None, None, event_dict) assert ( r"c=None y='test' b=[3, 4] a= z=(1, 2) x=7 A='A' B='B'" ) == rv def test_order_sorted_missing_dropped(self, event_dict): """ Keys get sorted if sort_keys=True and extras get dropped. """ event_dict["B"] = "B" event_dict["A"] = "A" rv = KeyValueRenderer( key_order=["c", "y", "b", "a", "z", "x"], sort_keys=True, drop_missing=True, )(None, None, event_dict) assert r"y='test' b=[3, 4] a= z=(1, 2) x=7 A='A' B='B'" == rv def test_random_order(self, event_dict): """ No special ordering doesn't blow up. """ rv = KeyValueRenderer()(None, None, event_dict) assert isinstance(rv, str) @pytest.mark.parametrize("rns", [True, False]) def test_repr_native_str(self, rns): """ repr_native_str=False doesn't repr on native strings. """ rv = KeyValueRenderer(repr_native_str=rns)( None, None, {"event": "哈", "key": 42, "key2": "哈"} ) cnt = rv.count("哈") assert 2 == cnt class TestLogfmtRenderer: def test_sort_keys(self, event_dict): """ Keys are sorted if sort_keys is set. """ rv = LogfmtRenderer(sort_keys=True)(None, None, event_dict) assert r'a= b="[3, 4]" x=7 y=test z="(1, 2)"' == rv def test_order_complete(self, event_dict): """ Orders keys according to key_order. """ rv = LogfmtRenderer(key_order=["y", "b", "a", "z", "x"])( None, None, event_dict ) assert r'y=test b="[3, 4]" a= z="(1, 2)" x=7' == rv def test_order_missing(self, event_dict): """ Missing keys get rendered as None. """ rv = LogfmtRenderer(key_order=["c", "y", "b", "a", "z", "x"])( None, None, event_dict ) assert r'c= y=test b="[3, 4]" a= z="(1, 2)" x=7' == rv def test_order_missing_dropped(self, event_dict): """ Missing keys get dropped """ rv = LogfmtRenderer( key_order=["c", "y", "b", "a", "z", "x"], drop_missing=True )(None, None, event_dict) assert r'y=test b="[3, 4]" a= z="(1, 2)" x=7' == rv def test_order_extra(self, event_dict): """ Extra keys get sorted if sort_keys=True. """ event_dict["B"] = "B" event_dict["A"] = "A" rv = LogfmtRenderer( key_order=["c", "y", "b", "a", "z", "x"], sort_keys=True )(None, None, event_dict) assert ( r'c= y=test b="[3, 4]" a= z="(1, 2)" x=7 A=A B=B' ) == rv def test_order_sorted_missing_dropped(self, event_dict): """ Keys get sorted if sort_keys=True and extras get dropped. """ event_dict["B"] = "B" event_dict["A"] = "A" rv = LogfmtRenderer( key_order=["c", "y", "b", "a", "z", "x"], sort_keys=True, drop_missing=True, )(None, None, event_dict) assert r'y=test b="[3, 4]" a= z="(1, 2)" x=7 A=A B=B' == rv def test_random_order(self, event_dict): """ No special ordering doesn't blow up. """ rv = LogfmtRenderer()(None, None, event_dict) assert isinstance(rv, str) def test_empty_event_dict(self): """ Empty event dict renders as empty string. """ rv = LogfmtRenderer()(None, None, {}) assert "" == rv def test_bool_as_flag(self): """ If activated, render ``{"a": True}`` as ``a`` instead of ``a=true``. """ event_dict = {"a": True, "b": False} rv_abbrev = LogfmtRenderer(bool_as_flag=True)(None, None, event_dict) assert r"a b=false" == rv_abbrev rv_no_abbrev = LogfmtRenderer(bool_as_flag=False)( None, None, event_dict ) assert r"a=true b=false" == rv_no_abbrev def test_reference_format(self): """ Test rendering according to example at https://pkg.go.dev/github.com/kr/logfmt """ event_dict = { "foo": "bar", "a": 14, "baz": "hello kitty", "cool%story": "bro", "f": True, "%^asdf": True, } rv = LogfmtRenderer()(None, None, event_dict) assert 'foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf' == rv def test_equal_sign_or_space_in_value(self): """ Values with equal signs are always quoted. """ event_dict = { "without": "somevalue", "withequal": "some=value", "withspace": "some value", } rv = LogfmtRenderer()(None, None, event_dict) assert ( r'without=somevalue withequal="some=value" withspace="some value"' == rv ) def test_invalid_key(self): """ Keys cannot contain space characters. """ event_dict = {"invalid key": "somevalue"} with pytest.raises(ValueError, match='Invalid key: "invalid key"'): LogfmtRenderer()(None, None, event_dict) def test_newline_in_value(self): """ Newlines in values are escaped. """ event_dict = {"with_newline": "some\nvalue"} rv = LogfmtRenderer()(None, None, event_dict) assert r"with_newline=some\nvalue" == rv @pytest.mark.parametrize( ("raw", "escaped"), [ # Slash by itself does not need to be escaped. (r"a\slash", r"a\slash"), # A quote requires quoting, and escaping the quote. ('a"quote', r'"a\"quote"'), # If anything triggers quoting of the string, then the slash must # be escaped. ( r'a\slash with space or a"quote', r'"a\\slash with space or a\"quote"', ), ( r"I want to render this \"string\" with logfmtrenderer", r'"I want to render this \\\"string\\\" with logfmtrenderer"', ), ], ) def test_escaping(self, raw, escaped): """ Backslashes and quotes are escaped. """ rv = LogfmtRenderer()(None, None, {"key": raw}) assert f"key={escaped}" == rv class TestJSONRenderer: def test_renders_json(self, event_dict): """ Renders a predictable JSON string. """ rv = JSONRenderer(sort_keys=True)(None, None, event_dict) assert ( r'{"a": "", "b": [3, 4], "x": 7, ' r'"y": "test", "z": ' r"[1, 2]}" ) == rv def test_FallbackEncoder_handles_ThreadLocalDictWrapped_dicts(self): """ Our fallback handling handles properly ThreadLocalDictWrapper values. """ with pytest.deprecated_call(): d = wrap_dict(dict) s = json.dumps(d({"a": 42}), default=_json_fallback_handler) assert '{"a": 42}' == s def test_FallbackEncoder_falls_back(self): """ The fallback handler uses repr if it doesn't know the type. """ s = json.dumps( {"date": datetime.date(1980, 3, 25)}, default=_json_fallback_handler, ) assert '{"date": "datetime.date(1980, 3, 25)"}' == s def test_serializer(self): """ A custom serializer is used if specified. """ jr = JSONRenderer(serializer=lambda obj, **kw: {"a": 42}) obj = object() assert {"a": 42} == jr(None, None, obj) def test_custom_fallback(self): """ A custom fallback handler can be used. """ jr = JSONRenderer(default=lambda x: repr(x)[::-1]) d = {"date": datetime.date(1980, 3, 25)} assert '{"date": ")52 ,3 ,0891(etad.emitetad"}' == jr(None, None, d) @pytest.mark.skipif(simplejson is None, reason="simplejson is missing.") def test_simplejson(self, event_dict): """ Integration test with simplejson. """ jr = JSONRenderer(serializer=simplejson.dumps) assert { "a": "", "b": [3, 4], "x": 7, "y": "test", "z": [1, 2], } == json.loads(jr(None, None, event_dict)) class TestTimeStamper: def test_disallows_non_utc_unix_timestamps(self): """ A asking for a UNIX timestamp with a timezone that's not UTC raises a ValueError. """ with pytest.raises( ValueError, match="UNIX timestamps are always UTC." ): TimeStamper(utc=False) def test_inserts_utc_unix_timestamp_by_default(self): """ Per default a float UNIX timestamp is used. """ ts = TimeStamper() d = ts(None, None, {}) # freezegun doesn't work with time.time. :( assert isinstance(d["timestamp"], float) @freeze_time("1980-03-25 16:00:00") def test_local(self): """ Timestamp in local timezone work. We can't add a timezone to the string without additional libraries. """ ts = TimeStamper(fmt="iso", utc=False) d = ts(None, None, {}) assert "1980-03-25T16:00:00" == d["timestamp"] @freeze_time("1980-03-25 16:00:00") def test_formats(self): """ The fmt string is respected. """ ts = TimeStamper(fmt="%Y") d = ts(None, None, {}) assert "1980" == d["timestamp"] @freeze_time("1980-03-25 16:00:00") def test_adds_Z_to_iso(self): """ stdlib's isoformat is buggy, so we fix it. """ ts = TimeStamper(fmt="iso", utc=True) d = ts(None, None, {}) assert "1980-03-25T16:00:00Z" == d["timestamp"] @freeze_time("1980-03-25 16:00:00") def test_key_can_be_specified(self): """ Timestamp is stored with the specified key. """ ts = TimeStamper(fmt="%m", key="month") d = ts(None, None, {}) assert "03" == d["month"] @freeze_time("1980-03-25 16:00:00") @pytest.mark.parametrize("fmt", [None, "%Y"]) @pytest.mark.parametrize("utc", [True, False]) @pytest.mark.parametrize("key", [None, "other-key"]) @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1)) def test_pickle(self, fmt, utc, key, proto): """ TimeStamper is serializable. """ # UNIX timestamps must be UTC. if fmt is None and not utc: pytest.skip() ts = TimeStamper() assert ts(None, None, {}) == pickle.loads(pickle.dumps(ts, proto))( None, None, {} ) def test_apply_freezegun_after_instantiation(self): """ Freezing time after instantiation of TimeStamper works. """ ts = TimeStamper(fmt="iso", utc=False) with freeze_time("1980-03-25 16:00:00", tz_offset=1): d = ts(None, None, {}) assert "1980-03-25T17:00:00" == d["timestamp"] class TestMaybeTimeStamper: def test_overwrite(self): """ If there is a timestamp, leave it. """ mts = MaybeTimeStamper() assert {"timestamp": 42} == mts(None, None, {"timestamp": 42}) def test_none(self): """ If there is no timestamp, add one. """ mts = MaybeTimeStamper() assert "timestamp" in mts(None, None, {}) class TestFormatExcInfo: def test_custom_formatter(self): """ The exception formatter can be changed. """ formatter = ExceptionRenderer(lambda _: "There is no exception!") try: raise CustomError("test") except CustomError as e: exc = e assert formatter(None, None, {"exc_info": exc}) == { "exception": "There is no exception!" } @pytest.mark.parametrize("ei", [False, None, ""]) def test_nop(self, ei): """ If exc_info is falsey, only remove the key. """ assert {} == ExceptionRenderer()(None, None, {"exc_info": ei}) def test_nop_missing(self): """ If event dict doesn't contain exc_info, do nothing. """ assert {} == ExceptionRenderer()(None, None, {}) def test_formats_tuple(self): """ If exc_info is a tuple, it is used. """ formatter = ExceptionRenderer(lambda exc_info: exc_info) d = formatter(None, None, {"exc_info": (None, None, 42)}) assert {"exception": (None, None, 42)} == d def test_gets_exc_info_on_bool(self): """ If exc_info is True, it is obtained using sys.exc_info(). """ # monkeypatching sys.exc_info makes currently pytest return 1 on # success. try: raise ValueError("test") except ValueError: d = ExceptionRenderer()(None, None, {"exc_info": True}) assert "exc_info" not in d assert 'raise ValueError("test")' in d["exception"] assert "ValueError: test" in d["exception"] def test_exception(self): """ Passing exceptions as exc_info is valid. """ formatter = ExceptionRenderer(lambda exc_info: exc_info) try: raise ValueError("test") except ValueError as e: exc = e else: pytest.fail("Exception not raised.") assert { "exception": (ValueError, exc, exc.__traceback__) } == formatter(None, None, {"exc_info": exc}) def test_exception_without_traceback(self): """ If an Exception is missing a traceback, render it anyway. """ rv = ExceptionRenderer()( None, None, {"exc_info": Exception("no traceback!")} ) assert {"exception": "Exception: no traceback!"} == rv def test_format_exception(self): """ "format_exception" is the "ExceptionRenderer" with default settings. """ try: raise ValueError("test") except ValueError as e: a = format_exc_info(None, None, {"exc_info": e}) b = ExceptionRenderer()(None, None, {"exc_info": e}) assert a == b @pytest.mark.parametrize("ei", [True, (None, None, None)]) def test_no_exception(self, ei): """ A missing exception does not blow up. """ assert {"exception": "MISSING"} == format_exc_info( None, None, {"exc_info": ei} ) structlog-24.4.0/tests/typing/api.py0000644000000000000000000002535614645734712014404 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ Make sure our configuration examples actually pass the type checker. """ from __future__ import annotations import logging import logging.config from typing import Any, Callable import structlog from structlog.processors import CallsiteParameter from structlog.typing import FilteringBoundLogger bl = structlog.get_logger() bl.msg("hello", whom="world", x=42, y={}) bls: structlog.stdlib.BoundLogger = structlog.get_logger() bls.info("hello", whom="world", x=42, y={}) def bytes_dumps( __obj: Any, default: Callable[[Any], Any] | None = None, option: int | None = None, ) -> bytes: """ Test with orjson's signature taken from . """ return b"{}" structlog.configure( processors=[structlog.processors.JSONRenderer(serializer=bytes_dumps)] ) structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.stdlib.render_to_log_kwargs, ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) structlog.configure( processors=[ structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=structlog.stdlib.LoggerFactory(), ) formatter = structlog.stdlib.ProcessorFormatter( processor=structlog.dev.ConsoleRenderer(), ) formatter = structlog.stdlib.ProcessorFormatter( processors=[ structlog.processors.CallsiteParameterAdder(), structlog.processors.CallsiteParameterAdder( set(CallsiteParameter), ["threading"] ), structlog.processors.CallsiteParameterAdder( set(CallsiteParameter), additional_ignores=["threading"] ), structlog.processors.CallsiteParameterAdder( parameters=set(CallsiteParameter), additional_ignores=["threading"] ), structlog.processors.CallsiteParameterAdder( [ CallsiteParameter.FILENAME, CallsiteParameter.FUNC_NAME, CallsiteParameter.LINENO, ] ), structlog.processors.CallsiteParameterAdder( parameters=[ CallsiteParameter.FILENAME, CallsiteParameter.FUNC_NAME, CallsiteParameter.LINENO, ] ), structlog.processors.CallsiteParameterAdder( parameters=[ CallsiteParameter.FILENAME, CallsiteParameter.FUNC_NAME, CallsiteParameter.LINENO, ] ), ], ) handler = logging.StreamHandler() handler.setFormatter(formatter) root_logger = logging.getLogger() root_logger.addHandler(handler) root_logger.setLevel(logging.INFO) timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") shared_processors: list[structlog.typing.Processor] = [ structlog.stdlib.add_log_level, timestamper, ] structlog.configure( processors=[ *shared_processors, structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, ) formatter = structlog.stdlib.ProcessorFormatter( processor=structlog.dev.ConsoleRenderer(), foreign_pre_chain=[ structlog.stdlib.ExtraAdder(), structlog.stdlib.ExtraAdder(allow=None), structlog.stdlib.ExtraAdder(None), structlog.stdlib.ExtraAdder(allow=["k1", "k2"]), structlog.stdlib.ExtraAdder({"k1", "k2"}), *shared_processors, ], ) timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") pre_chain = [ # Add the log level and a timestamp to the event_dict if the log entry # is not from structlog. structlog.stdlib.add_log_level, timestamper, ] logging.config.dictConfig( { "version": 1, "disable_existing_loggers": False, "formatters": { "plain": { "()": structlog.stdlib.ProcessorFormatter, "processor": structlog.dev.ConsoleRenderer(colors=False), "foreign_pre_chain": pre_chain, }, "colored": { "()": structlog.stdlib.ProcessorFormatter, "processor": structlog.dev.ConsoleRenderer(colors=True), "foreign_pre_chain": pre_chain, }, }, "handlers": { "default": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "colored", }, "file": { "level": "DEBUG", "class": "logging.handlers.WatchedFileHandler", "filename": "test.log", "formatter": "plain", }, }, "loggers": { "": { "handlers": ["default", "file"], "level": "DEBUG", "propagate": True, }, }, } ) structlog.configure( processors=[ structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), timestamper, structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.TimeStamper(fmt="iso"), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.processors.JSONRenderer(), ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.TimeStamper(fmt="iso"), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.processors.LogfmtRenderer(), ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.TimeStamper(fmt="iso"), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.processors.JSONRenderer(), ], context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.AsyncBoundLogger, cache_logger_on_first_use=True, ) # Regression test for # https://github.com/wemake-services/wemake-django-template/ structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.processors.TimeStamper(fmt="iso"), structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.processors.ExceptionPrettyPrinter(), structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], context_class=structlog.threadlocal.wrap_dict(dict), logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, ) with structlog.threadlocal.bound_threadlocal(x=42): pass def typecheck_filtering_return() -> None: fblogger: FilteringBoundLogger = structlog.get_logger(__name__) fblog = fblogger.bind(key1="value1", key2="value2", key3="value3") fblog.info("values bound") fblog = fblog.unbind("key1") fblog.debug("value unbound") fblog = fblog.try_unbind("bad_key") fblog.warn("no value unbound because key not defined") fblog = fblog.new(new="value") fblog.info("this is a whole new logger") fblog.log(logging.CRITICAL, "this is synchronously CRITICAL") async def typecheck_filtering_return_async() -> None: fblogger: FilteringBoundLogger = structlog.get_logger(__name__) await fblogger.adebug("async debug") await fblogger.ainfo("async info") await fblogger.awarning("async warning") await fblogger.awarn("async warn") await fblogger.aerror("async error") await fblogger.afatal("fatal error") await fblogger.aexception("async exception") await fblogger.acritical("async critical") await fblogger.amsg("async msg") await fblogger.alog(logging.CRITICAL, "async log") async def typecheck_stdlib_async() -> None: logger: structlog.stdlib.BoundLogger = structlog.get_logger(__name__) await logger.adebug("async debug") await logger.ainfo("async info") await logger.awarning("async warning") await logger.aerror("async error") await logger.afatal("fatal error") await logger.aexception("async exception") await logger.acritical("async critical") await logger.alog(logging.CRITICAL, "async log") # Structured tracebacks and ExceptionRenderer with ExceptionDictTransformer struct_tb: structlog.tracebacks.Trace = structlog.tracebacks.extract( ValueError, ValueError("onoes"), None ) try: raise ValueError("onoes") except ValueError as e: struct_tb = structlog.tracebacks.extract(type(e), e, e.__traceback__) structlog.configure( processors=[ structlog.processors.ExceptionRenderer( structlog.tracebacks.ExceptionDictTransformer() ), structlog.processors.JSONRenderer(), ] ) fbl: FilteringBoundLogger = structlog.get_logger() fbl.info("Hello %s! The answer is %d.", "World", 42, x=1) structlog-24.4.0/.gitignore0000644000000000000000000000025314645734712012562 0ustar00*.pyc *.pyo .DS_Store .cache .coverage* .direnv .envrc .mypy_cache .pytest_cache .tox .vscode benchmarks build dist docs/_build htmlcov tmp structlog.docset structlog.tgz structlog-24.4.0/LICENSE-APACHE0000644000000000000000000002367614645734712012534 0ustar00 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS structlog-24.4.0/LICENSE-MIT0000644000000000000000000000213114645734712012223 0ustar00The MIT License (MIT) Copyright (c) 2013 Hynek Schlawack and the structlog contributors 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. structlog-24.4.0/NOTICE0000644000000000000000000000011014645734712011466 0ustar00structlog Copyright 2013 Hynek Schlawack and the structlog contributors structlog-24.4.0/pyproject.toml0000644000000000000000000001601714645734712013513 0ustar00# SPDX-License-Identifier: MIT OR Apache-2.0 [build-system] requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme>=22.8.0"] build-backend = "hatchling.build" [project] dynamic = ["readme", "version"] name = "structlog" description = "Structured Logging for Python" authors = [{ name = "Hynek Schlawack", email = "hs@ox.cx" }] requires-python = ">=3.8" license = "MIT OR Apache-2.0" keywords = ["logging", "structured", "structure", "log"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: System :: Logging", "Typing :: Typed", ] dependencies = [] [project.urls] Documentation = "https://www.structlog.org/" Changelog = "https://github.com/hynek/structlog/blob/main/CHANGELOG.md" GitHub = "https://github.com/hynek/structlog" Funding = "https://github.com/sponsors/hynek" Tidelift = "https://tidelift.com?utm_source=lifter&utm_medium=referral&utm_campaign=hynek" Mastodon = "https://mastodon.social/@hynek" Twitter = "https://twitter.com/hynek" [project.optional-dependencies] tests = [ "freezegun>=0.2.8", "pretend", "pytest-asyncio>=0.17", "pytest>=6.0", "simplejson", ] # Need Twisted & Rich for stubs. # Otherwise mypy fails in tox. typing = ["mypy>=1.4", "rich", "twisted"] docs = [ "cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted", ] dev = ["structlog[tests,typing]"] [tool.hatch.version] source = "vcs" raw-options = { local_scheme = "no-local-version" } [tool.pytest.ini_options] addopts = ["-ra", "--strict-markers", "--strict-config"] testpaths = "tests" xfail_strict = true filterwarnings = [ "once::Warning", 'ignore:datetime.datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:dateutil.tz', ] asyncio_mode = "auto" [tool.coverage.run] parallel = true branch = true source = ["structlog"] [tool.coverage.paths] source = ["src", ".tox/py*/**/site-packages"] [tool.coverage.report] show_missing = true skip_covered = true omit = ["src/structlog/_greenlets.py"] exclude_lines = [ # a more strict default pragma "\\# pragma: no cover\\b", # allow defensive code "^\\s*raise AssertionError\\b", "^\\s*raise NotImplementedError\\b", "^\\s*return NotImplemented\\b", "^\\s*raise$", # typing-related code "^if (False|TYPE_CHECKING):", ": \\.\\.\\.(\\s*#.*)?$", "^ +\\.\\.\\.$", "-> ['\"]?NoReturn['\"]?:", ] [tool.interrogate] omit-covered-files = true verbose = 2 fail-under = 100 whitelist-regex = ["test_.*"] [tool.black] line-length = 79 [tool.ruff] src = ["src", "tests"] [tool.ruff.lint] select = ["ALL"] ignore = [ "A", # shadowing is fine "ANN", # Mypy is better at this "ARG", # unused arguments are common w/ interfaces "C901", # sometimes you trade complexity for performance "COM", # Black takes care of our commas "D", # We prefer our own docstring style. "E501", # leave line-length enforcement to Black "EM101", # simple strings are fine "FBT", # bools are our friends "FIX", # Yes, we want XXX as a marker. "INP001", # sometimes we want Python files outside of packages "N802", # some names are non-pep8 due to stdlib logging / Twisted "N803", # ditto "N806", # ditto "PLR0913", # leave complexity to me "PLR2004", # numbers are sometimes fine "PLW2901", # overwriting a loop var can be useful "RUF001", # leave my smart characters alone "RUF001", # leave my smart characters alone "SLF001", # private members are accessed by friendly functions "T201", # prints are fine "TCH", # TYPE_CHECKING blocks break autodocs "TD", # we don't follow other people's todo style "TID252", # Relative imports are fine "TRY003", # simple strings are fine "TRY004", # too many false negatives "TRY300", # else blocks are nice, but code-locality is nicer "PTH", # pathlib can be slow, so no point to rewrite ] [tool.ruff.lint.per-file-ignores] "tests/*" = [ "B018", # "useless" expressions can be useful in tests "BLE", # tests have different rules around exceptions "EM", # tests have different rules around exceptions "LOG001", # need to instantiate log messages in tests "PLC1901", # empty strings are falsey, but are less specific in tests "PT005", # we always add underscores and explicit name "RUF012", # no type hints in tests "S", # it's test; chill out security "SIM300", # Yoda rocks in tests "TRY", # tests have different rules around exceptions ] [tool.ruff.lint.isort] lines-between-types = 1 lines-after-imports = 2 [tool.mypy] strict = true pretty = true show_error_codes = true enable_error_code = ["ignore-without-code"] ignore_missing_imports = true warn_return_any = false [[tool.mypy.overrides]] module = "tests.*" ignore_errors = true [[tool.mypy.overrides]] module = "tests.typing.*" ignore_errors = false [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/markdown" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] text = '''

structlog mascot

''' [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.md" start-after = "\n" end-before = "\n" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.md" start-after = "\n" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] text = """ ## Release Information """ [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "CHANGELOG.md" start-after = "" pattern = "\n(###.+?\n)## " [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] text = """ --- [Full Changelog →](https://www.structlog.org/en/stable/changelog.html) """ [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.md" start-at = "## Credits" end-before = "" # Point sponsor image URLs to versions. [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] pattern = 'docs\/_static\/sponsors' replacement = 'https://www.structlog.org/en/$HFPR_VERSION/_static/sponsors' [[tool.sponcon.sponsors]] title = "Variomedia AG" url = "https://www.variomedia.de/" img = "Variomedia.svg" [[tool.sponcon.sponsors]] title = "Tidelift" url = "https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek" img = "Tidelift.svg" [[tool.sponcon.sponsors]] title = "Klaviyo" url = "https://klaviyo.com/" img = "Klaviyo.svg" [[tool.sponcon.sponsors]] title = "FilePreviews" url = "https://filepreviews.io/" img = "FilePreviews.svg" structlog-24.4.0/PKG-INFO0000644000000000000000000001614714645734712011700 0ustar00Metadata-Version: 2.3 Name: structlog Version: 24.4.0 Summary: Structured Logging for Python Project-URL: Documentation, https://www.structlog.org/ Project-URL: Changelog, https://github.com/hynek/structlog/blob/main/CHANGELOG.md Project-URL: GitHub, https://github.com/hynek/structlog Project-URL: Funding, https://github.com/sponsors/hynek Project-URL: Tidelift, https://tidelift.com?utm_source=lifter&utm_medium=referral&utm_campaign=hynek Project-URL: Mastodon, https://mastodon.social/@hynek Project-URL: Twitter, https://twitter.com/hynek Author-email: Hynek Schlawack License-Expression: MIT OR Apache-2.0 License-File: LICENSE-APACHE License-File: LICENSE-MIT License-File: NOTICE Keywords: log,logging,structure,structured Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: Apache Software License Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: System :: Logging Classifier: Typing :: Typed Requires-Python: >=3.8 Provides-Extra: dev Requires-Dist: freezegun>=0.2.8; extra == 'dev' Requires-Dist: mypy>=1.4; extra == 'dev' Requires-Dist: pretend; extra == 'dev' Requires-Dist: pytest-asyncio>=0.17; extra == 'dev' Requires-Dist: pytest>=6.0; extra == 'dev' Requires-Dist: rich; extra == 'dev' Requires-Dist: simplejson; extra == 'dev' Requires-Dist: twisted; extra == 'dev' Provides-Extra: docs Requires-Dist: cogapp; extra == 'docs' Requires-Dist: furo; extra == 'docs' Requires-Dist: myst-parser; extra == 'docs' Requires-Dist: sphinx; extra == 'docs' Requires-Dist: sphinx-notfound-page; extra == 'docs' Requires-Dist: sphinxcontrib-mermaid; extra == 'docs' Requires-Dist: sphinxext-opengraph; extra == 'docs' Requires-Dist: twisted; extra == 'docs' Provides-Extra: tests Requires-Dist: freezegun>=0.2.8; extra == 'tests' Requires-Dist: pretend; extra == 'tests' Requires-Dist: pytest-asyncio>=0.17; extra == 'tests' Requires-Dist: pytest>=6.0; extra == 'tests' Requires-Dist: simplejson; extra == 'tests' Provides-Extra: typing Requires-Dist: mypy>=1.4; extra == 'typing' Requires-Dist: rich; extra == 'typing' Requires-Dist: twisted; extra == 'typing' Description-Content-Type: text/markdown

structlog mascot

*structlog* is *the* production-ready logging solution for Python: - **Simple**: Everything is about **functions** that take and return **dictionaries** – all hidden behind **familiar APIs**. - **Powerful**: Functions and dictionaries aren’t just simple but also powerful. *structlog* leaves *you* in control. - **Fast**: *structlog* is not hamstrung by designs of yore. Its flexibility comes not at the price of performance. Thanks to its flexible design, *you* choose whether you want *structlog* to take care of the **output** of your log entries or whether you prefer to **forward** them to an existing logging system like the standard library's `logging` module. The output format is just as flexible and *structlog* comes with support for JSON, [*logfmt*](https://brandur.org/logfmt), as well as pretty console output out-of-the-box: [![image](https://github.com/hynek/structlog/blob/main/docs/_static/console_renderer.png?raw=true)](https://github.com/hynek/structlog/blob/main/docs/_static/console_renderer.png?raw=true) ## Sponsors *structlog* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). Especially those generously supporting us at the *The Organization* tier and higher:

Please consider joining them to help make structlog’s maintenance more sustainable!

--- *structlog* has been successfully used in production at every scale since **2013**, while embracing cutting-edge technologies like *asyncio*, context variables, or type hints as they emerged. Its paradigms proved influential enough to [help design](https://twitter.com/sirupsen/status/638330548361019392) structured logging [packages across ecosystems](https://github.com/sirupsen/logrus). ## Project Links - [**Get Help**](https://stackoverflow.com/questions/tagged/structlog) (use the *structlog* tag on Stack Overflow) - [**PyPI**](https://pypi.org/project/structlog/) - [**GitHub**](https://github.com/hynek/structlog) - [**Documentation**](https://www.structlog.org/) - [**Changelog**](https://github.com/hynek/structlog/tree/main/CHANGELOG.md) - [**Third-party Extensions**](https://github.com/hynek/structlog/wiki/Third-party-Extensions) ## *structlog* for Enterprise Available as part of the Tidelift Subscription. The maintainers of *structlog* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek) ## Release Information ### Changed No code changes since 24.3.0 --- [Full Changelog →](https://www.structlog.org/en/stable/changelog.html) ## Credits *structlog* is written and maintained by [Hynek Schlawack](https://hynek.me/). The idea of bound loggers is inspired by previous work by [Jean-Paul Calderone](https://github.com/exarkun) and [David Reid](https://github.com/dreid). The development is kindly supported by my employer [Variomedia AG](https://www.variomedia.de/), *structlog*’s [Tidelift subscribers](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek), and all my amazing [GitHub Sponsors](https://github.com/sponsors/hynek). The logs-loving beaver logo has been contributed by [Lynn Root](https://www.roguelynn.com).