pax_global_header00006660000000000000000000000064132140322300014500gustar00rootroot0000000000000052 comment=a6a0bc420deb9597e780dc43cf6f80276c68d0dd aws-xray-sdk-python-0.95/000077500000000000000000000000001321403223000153065ustar00rootroot00000000000000aws-xray-sdk-python-0.95/.gitignore000066400000000000000000000002101321403223000172670ustar00rootroot00000000000000.DS_Store *.pyc .Python .cache build bin include lib dist *.egg *.egg-info .tox .python-version pip-selfcheck.json .coverage* htmlcovaws-xray-sdk-python-0.95/CHANGELOG.rst000066400000000000000000000027631321403223000173370ustar00rootroot00000000000000========= CHANGELOG ========= 0.95 ==== * **Breaking**: AWS API parameter whitelist json file is moved to path `aws_xray_sdk/ext/resources/aws_para_whitelist.json` in `PR6 `_. * Added aiobotocore/aioboto3 support and async function capture. `PR6 `_ * Added logic to removing segment/subsegment name invalid characters. `PR9 `_ * Temporarily disabled tests run on Django2.0. `PR10 `_ * Code cleanup. `PR11 `_ 0.94 ==== * Added aiohttp support. `PR3 `_ 0.93 ==== * The X-Ray SDK for Python is now an open source project. You can follow the project and submit issues and pull requests on GitHub: https://github.com/aws/aws-xray-sdk-python 0.92.2 ====== * bugfix: Fixed an issue that caused the X-Ray recorder to omit the origin when recording segments with a service plugin. This caused the service's type to not appear on the service map in the X-Ray console. 0.92.1 ====== * bugfix: Fixed an issue that caused all calls to Amazon DynamoDB tables to be grouped under a single node in the service map. With this update, each table gets a separate node. 0.92 ==== * feature: Add Flask support * feature: Add dynamic naming on segment name 0.91.1 ====== * bugfix: The SDK has been released as a universal wheel aws-xray-sdk-python-0.95/LICENSE000066400000000000000000000261351321403223000163220ustar00rootroot00000000000000 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. aws-xray-sdk-python-0.95/MANIFEST.in000066400000000000000000000001651321403223000170460ustar00rootroot00000000000000include aws_xray_sdk/ext/resources/*.json include aws_xray_sdk/core/sampling/*.json include README.md include LICENSEaws-xray-sdk-python-0.95/README.md000066400000000000000000000113331321403223000165660ustar00rootroot00000000000000# AWS X-Ray SDK for Python (beta) ![Screenshot of the AWS X-Ray console](/images/example_servicemap.png?raw=true) ## Installing The AWS X-Ray SDK for Python is compatible with Python 2.7, 3.4, 3.5, and 3.6. Install the SDK using the following command (the SDK's non-testing dependencies will be installed). ``` pip install aws-xray-sdk ``` To install the SDK's testing dependencies, use the following command. ``` pip install tox ``` ## Getting Help Use the following community resources for getting help with the SDK. We use the GitHub issues for tracking bugs and feature requests. * Ask a question in the [AWS X-Ray Forum](https://forums.aws.amazon.com/forum.jspa?forumID=241&start=0). * Open a support ticket with [AWS Support](http://docs.aws.amazon.com/awssupport/latest/user/getting-started.html). * If you think you may have found a bug, open an [issue](https://github.com/aws/aws-xray-sdk-python/issues/new). ## Opening Issues If you encounter a bug with the AWS X-Ray SDK for Python, we want to hear about it. Before opening a new issue, search the [existing issues](https://github.com/aws/aws-xray-sdk-python/issues) to see if others are also experiencing the issue. Include the version of the AWS X-Ray SDK for Python, Python language, and botocore/boto3 if applicable. In addition, include the repro case when appropriate. The GitHub issues are intended for bug reports and feature requests. For help and questions about using the AWS SDK for Python, use the resources listed in the [Getting Help](https://github.com/aws/aws-xray-sdk-python#getting-help) section. Keeping the list of open issues lean helps us respond in a timely manner. ## Documentation The [developer guide](https://docs.aws.amazon.com/xray/latest/devguide) provides in-depth guidance about using the AWS X-Ray service. The [API Reference](http://docs.aws.amazon.com/xray-sdk-for-python/latest/reference/) provides guidance for using the SDK and module-level documentation. ## Quick Start **Configuration** ```python from aws_xray_sdk.core import xray_recorder xray_recorder.configure( sampling=False, context_missing='LOG_ERROR', plugins=('EC2Plugin', 'ECSPlugin', 'ElasticBeanstalkPlugin'), daemon_address='127.0.0.1:3000', dynamic_naming='*mysite.com*' ) ``` **Start a custom segment/subsegment** ```python from aws_xray_sdk.core import xray_recorder # Start a segment segment = xray_recorder.begin_segment('segment_name') # Start a subsegment subsegment = xray_recorder.begin_subsegment('subsegment_name') # Add metadata or annotation here if necessary segment.put_metadata('key', dict, 'namespace') subsegment.put_annotation('key', 'value') xray_recorder.end_subsegment() # Close the segment xray_recorder.end_segment() ``` **Capture** ```python from aws_xray_sdk.core import xray_recorder @xray_recorder.capture('subsegment_name') def myfunc(): # Do something here myfunc() ``` ```python from aws_xray_sdk.core import xray_recorder @xray_recorder.capture_async('subsegment_name') async def myfunc(): # Do something here async def main(): await myfunc() ``` **Trace AWS Lambda functions** ```python from aws_xray_sdk.core import xray_recorder def lambda_handler(event, context): # ... some code subsegment = xray_recorder.begin_subsegment('subsegment_name') # Code to record # Add metadata or annotation here, if necessary subsegment.put_metadata('key', dict, 'namespace') subsegment.put_annotation('key', 'value') xray_recorder.end_subsegment() # ... some other code ``` **Patch third-party libraries** ```python from aws_xray_sdk.core import patch libs_to_patch = ('boto3', 'mysql', 'requests') patch(libs_to_patch) ``` **Add Django middleware** In django settings.py, use the following. ```python INSTALLED_APPS = [ # ... other apps 'aws_xray_sdk.ext.django', ] MIDDLEWARE = [ 'aws_xray_sdk.ext.django.middleware.XRayMiddleware', # ... other middlewares ] ``` **Add Flask middleware** ```python from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.flask.middleware import XRayMiddleware app = Flask(__name__) xray_recorder.configure(service='fallback_name', dynamic_naming='*mysite.com*') XRayMiddleware(app, xray_recorder) ``` **Add aiohttp middleware** ```python from aiohttp import web from aws_xray_sdk.ext.aiohttp.middleware import middleware from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.async_context import AsyncContext xray_recorder.configure(service='fallback_name', context=AsyncContext()) app = web.Application(middlewares=[middleware]) app.router.add_get("/", handler) web.run_app(app) ``` ## License The AWS X-Ray SDK for Python is licensed under the Apache 2.0 License. See LICENSE and NOTICE.txt for more information. aws-xray-sdk-python-0.95/__init__.py000066400000000000000000000000001321403223000174050ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/000077500000000000000000000000001321403223000200045ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/__init__.py000066400000000000000000000000001321403223000221030ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/000077500000000000000000000000001321403223000207345ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/__init__.py000066400000000000000000000005371321403223000230520ustar00rootroot00000000000000from .recorder import AWSXRayRecorder from .patcher import patch_all, patch from .utils.compat import PY35 if not PY35: xray_recorder = AWSXRayRecorder() else: from .async_recorder import AsyncAWSXRayRecorder xray_recorder = AsyncAWSXRayRecorder() __all__ = [ 'patch', 'patch_all', 'xray_recorder', 'AWSXRayRecorder', ] aws-xray-sdk-python-0.95/aws_xray_sdk/core/async_context.py000066400000000000000000000055461321403223000242010ustar00rootroot00000000000000import asyncio from .context import Context as _Context class AsyncContext(_Context): """ Async Context for storing segments. Inherits nearly everything from the main Context class. Replaces threading.local with a task based local storage class, Also overrides clear_trace_entities """ def __init__(self, *args, loop=None, use_task_factory=True, **kwargs): super(AsyncContext, self).__init__(*args, **kwargs) self._loop = loop if loop is None: self._loop = asyncio.get_event_loop() if use_task_factory: self._loop.set_task_factory(task_factory) self._local = TaskLocalStorage(loop=loop) def clear_trace_entities(self): """ Clear all trace_entities stored in the task local context. """ if self._local is not None: self._local.clear() class TaskLocalStorage(object): """ Simple task local storage """ def __init__(self, loop=None): if loop is None: loop = asyncio.get_event_loop() self._loop = loop def __setattr__(self, name, value): if name in ('_loop',): # Set normal attributes object.__setattr__(self, name, value) else: # Set task local attributes task = asyncio.Task.current_task(loop=self._loop) if task is None: return None if not hasattr(task, 'context'): task.context = {} task.context[name] = value def __getattribute__(self, item): if item in ('_loop', 'clear'): # Return references to local objects return object.__getattribute__(self, item) task = asyncio.Task.current_task(loop=self._loop) if task is None: return None if hasattr(task, 'context') and item in task.context: return task.context[item] raise AttributeError('Task context does not have attribute {0}'.format(item)) def clear(self): # If were in a task, clear the context dictionary task = asyncio.Task.current_task(loop=self._loop) if task is not None and hasattr(task, 'context'): task.context.clear() def task_factory(loop, coro): """ Task factory function Fuction closely mirrors the logic inside of asyncio.BaseEventLoop.create_task. Then if there is a current task and the current task has a context then share that context with the new task """ task = asyncio.Task(coro, loop=loop) if task._source_traceback: # flake8: noqa del task._source_traceback[-1] # flake8: noqa # Share context with new task if possible current_task = asyncio.Task.current_task(loop=loop) if current_task is not None and hasattr(current_task, 'context'): setattr(task, 'context', current_task.context) return task aws-xray-sdk-python-0.95/aws_xray_sdk/core/async_recorder.py000066400000000000000000000040031321403223000243050ustar00rootroot00000000000000import time import traceback import wrapt from aws_xray_sdk.core.recorder import AWSXRayRecorder class AsyncAWSXRayRecorder(AWSXRayRecorder): def capture_async(self, name=None): """ A decorator that records enclosed function in a subsegment. It only works with asynchronous functions. params str name: The name of the subsegment. If not specified the function name will be used. """ @wrapt.decorator async def wrapper(wrapped, instance, args, kwargs): func_name = name if not func_name: func_name = wrapped.__name__ result = await self.record_subsegment_async( wrapped, instance, args, kwargs, name=func_name, namespace='local', meta_processor=None, ) return result return wrapper async def record_subsegment_async(self, wrapped, instance, args, kwargs, name, namespace, meta_processor): subsegment = self.begin_subsegment(name, namespace) exception = None stack = None return_value = None try: return_value = await wrapped(*args, **kwargs) return return_value except Exception as e: exception = e stack = traceback.extract_stack(limit=self._max_trace_back) raise finally: end_time = time.time() if callable(meta_processor): meta_processor( wrapped=wrapped, instance=instance, args=args, kwargs=kwargs, return_value=return_value, exception=exception, subsegment=subsegment, stack=stack, ) elif exception: if subsegment: subsegment.add_exception(exception, stack) self.end_subsegment(end_time) aws-xray-sdk-python-0.95/aws_xray_sdk/core/context.py000066400000000000000000000111221321403223000227670ustar00rootroot00000000000000import threading import logging import os from .exceptions.exceptions import SegmentNotFoundException log = logging.getLogger(__name__) MISSING_SEGMENT_MSG = 'cannot find the current segment/subsegment, please make sure you have a segment open' SUPPORTED_CONTEXT_MISSING = ('RUNTIME_ERROR', 'LOG_ERROR') CXT_MISSING_STRATEGY_KEY = 'AWS_XRAY_CONTEXT_MISSING' class Context(object): """ The context storage class to store trace entities(segments/subsegments). The default implementation uses threadlocal to store these entities. It also provides interfaces to manually inject trace entities which will replace the current stored entities and to clean up the storage. For any data access or data mutation, if there is no active segment present if will use user-defined behavior to handle such case. By default it throws an runtime error. This data structure is thread-safe. """ def __init__(self, context_missing='RUNTIME_ERROR'): self._local = threading.local() strategy = os.getenv(CXT_MISSING_STRATEGY_KEY, context_missing) self._context_missing = strategy def put_segment(self, segment): """ Store the segment created by ``xray_recorder`` to the context. It overrides the current segment if there is already one. """ setattr(self._local, 'entities', [segment]) def end_segment(self, end_time=None): """ End the current active segment. :param int end_time: epoch in seconds. If not specified the current system time will be used. """ entity = self.get_trace_entity() if not entity: log.warning("No segment to end") return if self._is_subsegment(entity): entity.parent_segment.close(end_time) else: entity.close(end_time) def put_subsegment(self, subsegment): """ Store the subsegment created by ``xray_recorder`` to the context. If you put a new subsegment while there is already an open subsegment, the new subsegment becomes the child of the existing subsegment. """ entity = self.get_trace_entity() if not entity: log.warning("Active segment or subsegment not found. Discarded %s." % subsegment.name) return entity.add_subsegment(subsegment) self._local.entities.append(subsegment) def end_subsegment(self, end_time=None): """ End the current active segment. Return False if there is no subsegment to end. :param int end_time: epoch in seconds. If not specified the current system time will be used. """ subsegment = self.get_trace_entity() if self._is_subsegment(subsegment): subsegment.close(end_time) self._local.entities.pop() return True else: log.warning("No subsegment to end.") return False def get_trace_entity(self): """ Return the current trace entity(segment/subsegment). If there is none, it behaves based on pre-defined ``context_missing`` strategy. """ if not getattr(self._local, 'entities', None): return self.handle_context_missing() return self._local.entities[-1] def set_trace_entity(self, trace_entity): """ Store the input trace_entity to local context. It will overwrite all existing ones if there is any. """ setattr(self._local, 'entities', [trace_entity]) def clear_trace_entities(self): """ clear all trace_entities stored in the local context. In case of using threadlocal to store trace entites, it will clean up all trace entities created by the current thread. """ self._local.__dict__.clear() def handle_context_missing(self): """ Called whenever there is no trace entity to access or mutate. """ if self.context_missing == 'RUNTIME_ERROR': log.error(MISSING_SEGMENT_MSG) raise SegmentNotFoundException(MISSING_SEGMENT_MSG) else: log.error(MISSING_SEGMENT_MSG) def _is_subsegment(self, entity): return hasattr(entity, 'type') and entity.type == 'subsegment' @property def context_missing(self): return self._context_missing @context_missing.setter def context_missing(self, value): if value not in SUPPORTED_CONTEXT_MISSING: log.warning('specified context_missing not supported, using default.') return self._context_missing = value aws-xray-sdk-python-0.95/aws_xray_sdk/core/emitters/000077500000000000000000000000001321403223000225705ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/emitters/__init__.py000066400000000000000000000000001321403223000246670ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/emitters/udp_emitter.py000066400000000000000000000042601321403223000254650ustar00rootroot00000000000000import os import socket import logging from ..exceptions.exceptions import InvalidDaemonAddressException log = logging.getLogger(__name__) PROTOCOL_HEADER = "{\"format\": \"json\", \"version\": 1}" PROTOCOL_DELIMITER = '\n' DAEMON_ADDRESS_KEY = "AWS_XRAY_DAEMON_ADDRESS" class UDPEmitter(object): """ The default emitter the X-Ray recorder uses to send segments/subsegments to the X-Ray daemon over UDP using a non-blocking socket. If there is an exception on the actual data transfer between the socket and the daemon, it logs the exception and continue. """ def __init__(self, daemon_address='127.0.0.1:2000'): self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setblocking(0) address = os.getenv(DAEMON_ADDRESS_KEY, daemon_address) self._ip, self._port = self._parse_address(address) def send_entity(self, entity): """ Serializes a segment/subsegment and sends it to the X-Ray daemon over UDP. By default it doesn't retry on failures. :param entity: a trace entity to send to the X-Ray daemon """ message = "%s%s%s" % (PROTOCOL_HEADER, PROTOCOL_DELIMITER, entity.serialize()) log.debug("sending: %s to %s:%s." % (message, self._ip, self._port)) self._send_data(message) def set_daemon_address(self, address): """ Takes a full address like 127.0.0.1:2000 and parses it into ip address and port. Throws an exception if the address has invalid format. """ if address: self._ip, self._port = self._parse_address(address) def _send_data(self, data): try: self._socket.sendto(data.encode('utf-8'), (self._ip, self._port)) except Exception: log.exception('failed to send data to X-Ray daemon.') def _parse_address(self, daemon_address): try: val = daemon_address.split(':') return val[0], int(val[1]) except Exception: raise InvalidDaemonAddressException('Invalid daemon address %s specified.' % daemon_address) aws-xray-sdk-python-0.95/aws_xray_sdk/core/exceptions/000077500000000000000000000000001321403223000231155ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/exceptions/__init__.py000066400000000000000000000000001321403223000252140ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/exceptions/exceptions.py000066400000000000000000000006751321403223000256600ustar00rootroot00000000000000class InvalidSamplingManifestError(Exception): pass class SegmentNotFoundException(Exception): pass class InvalidDaemonAddressException(Exception): pass class SegmentNameMissingException(Exception): pass class SubsegmentNameMissingException(Exception): pass class FacadeSegmentMutationException(Exception): pass class MissingPluginNames(Exception): pass class AlreadyEndedException(Exception): pass aws-xray-sdk-python-0.95/aws_xray_sdk/core/lambda_launcher.py000066400000000000000000000102761321403223000244150ustar00rootroot00000000000000import os import logging import threading from .models.facade_segment import FacadeSegment from .models.trace_header import TraceHeader from .context import Context log = logging.getLogger(__name__) LAMBDA_TRACE_HEADER_KEY = '_X_AMZN_TRACE_ID' LAMBDA_TASK_ROOT_KEY = 'LAMBDA_TASK_ROOT' TOUCH_FILE_DIR = '/tmp/.aws-xray/' TOUCH_FILE_PATH = '/tmp/.aws-xray/initialized' def check_in_lambda(): """ Return None if SDK is not loaded in AWS Lambda worker. Otherwise drop a touch file and return a lambda context. """ if not os.getenv(LAMBDA_TASK_ROOT_KEY): return None try: os.mkdir(TOUCH_FILE_DIR) except OSError: log.debug('directory %s already exists', TOUCH_FILE_DIR) try: f = open(TOUCH_FILE_PATH, 'w+') f.close() # utime force second parameter in python2.7 os.utime(TOUCH_FILE_PATH, None) except (IOError, OSError): log.warning("Unable to write to %s. Failed to signal SDK initialization." % TOUCH_FILE_PATH) return LambdaContext() class LambdaContext(Context): """ Lambda service will generate a segment for each function invocation which cannot be mutated. The context doesn't keep any manually created segment but instead every time ``get_trace_entity()`` gets called it refresh the facade segment based on environment variables set by Lambda worker. """ def __init__(self): self._local = threading.local() def put_segment(self, segment): """ No-op. """ log.warning('Cannot create segments inside Lambda function. Discarded.') def end_segment(self, end_time=None): """ No-op. """ log.warning('Cannot end segment inside Lambda function. Ignored.') def put_subsegment(self, subsegment): """ Refresh the facade segment every time this function is invoked to prevent a new subsegment from being attached to a leaked segment/subsegment. """ current_entity = self.get_trace_entity() if not self._is_subsegment(current_entity) and current_entity.initializing: log.warning("Subsegment %s discarded due to Lambda worker still initializing" % subsegment.name) current_entity.add_subsegment(subsegment) self._local.entities.append(subsegment) def get_trace_entity(self): self._refresh_context() if getattr(self._local, 'entities', None): return self._local.entities[-1] else: return self._local.segment def _refresh_context(self): """ Get current facade segment. To prevent resource leaking in Lambda worker, every time there is segment present, we compare its trace id to current environment variables. If it is different we create a new facade segment and clean up subsegments stored. """ header_str = os.getenv(LAMBDA_TRACE_HEADER_KEY) trace_header = TraceHeader.from_header_str(header_str) segment = getattr(self._local, 'segment', None) if segment: # Ensure customers don't have leaked subsegments across invocations if not trace_header.root or trace_header.root == segment.trace_id: return else: self._initialize_context(trace_header) else: self._initialize_context(trace_header) @property def context_missing(self): return None @context_missing.setter def context_missing(self, value): pass def handle_context_missing(self): """ No-op. """ pass def _initialize_context(self, trace_header): """ Create a facade segment based on environment variables set by AWS Lambda and initialize storage for subsegments. """ sampled = None if trace_header.sampled == 0: sampled = False elif trace_header.sampled == 1: sampled = True segment = FacadeSegment( name='facade', traceid=trace_header.root, entityid=trace_header.parent, sampled=sampled, ) setattr(self._local, 'segment', segment) setattr(self._local, 'entities', []) aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/000077500000000000000000000000001321403223000222175ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/__init__.py000066400000000000000000000000001321403223000243160ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/default_dynamic_naming.py000066400000000000000000000023021321403223000272470ustar00rootroot00000000000000from ..utils.search_pattern import wildcard_match class DefaultDynamicNaming(object): """ Decides what name to use on a segment generated from an incoming request. By default it takes the host name and compares it to a pre-defined pattern. If the host name matches that pattern, it returns the host name, otherwise it returns the fallback name. The host name usually comes from the incoming request's headers. """ def __init__(self, pattern, fallback): """ :param str pattern: the regex-like pattern to be compared against. Right now only ? and * are supported. An asterisk (*) represents any combination of characters. A question mark (?) represents any single character. :param str fallback: the fallback name to be used if the candidate name doesn't match the provided pattern. """ self._pattern = pattern self._fallback = fallback def get_name(self, host_name): """ Returns the segment name based on the input host name. """ if wildcard_match(self._pattern, host_name): return host_name else: return self._fallback aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/dummy_entities.py000066400000000000000000000051131321403223000256300ustar00rootroot00000000000000from .segment import Segment from .subsegment import Subsegment class DummySegment(Segment): """ A dummy segment is created when ``xray_recorder`` decide to not sample the segment based on sampling rules. Adding data to a dummy segment becomes a no-op except for subsegments. This is to reduce the memory footprint of the SDK. A dummy segment will not be sent to the X-Ray daemon. Manually create dummy segments is not recommended. """ def __init__(self, name='dummy'): super(DummySegment, self).__init__(name=name, traceid='dummy') self.sampled = False def set_aws(self, aws_meta): """ No-op """ pass def put_http_meta(self, key, value): """ No-op """ pass def put_annotation(self, key, value): """ No-op """ pass def put_metadata(self, key, value, namespace='default'): """ No-op """ pass def set_user(self, user): """ No-op """ pass def apply_status_code(self, status_code): """ No-op """ pass def add_exception(self, exception, stack, remote=False): """ No-op """ pass def serialize(self): """ No-op """ pass class DummySubsegment(Subsegment): """ A dummy subsegment will be created when ``xray_recorder`` tries to create a subsegment under a not sampled segment. Adding data to a dummy subsegment becomes no-op. Dummy subsegment will not be sent to the X-Ray daemon. """ def __init__(self, segment, name='dummy'): super(DummySubsegment, self).__init__(name, 'dummy', segment) self.sampled = False def set_aws(self, aws_meta): """ No-op """ pass def put_http_meta(self, key, value): """ No-op """ pass def put_annotation(self, key, value): """ No-op """ pass def put_metadata(self, key, value, namespace='default'): """ No-op """ pass def set_sql(self, sql): """ No-op """ pass def set_user(self, user): """ No-op """ pass def apply_status_code(self, status_code): """ No-op """ pass def add_exception(self, exception, stack, remote=False): """ No-op """ pass def serialize(self): """ No-op """ pass aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/entity.py000066400000000000000000000210121321403223000241010ustar00rootroot00000000000000import logging import os import binascii import time import string import jsonpickle from ..utils.compat import annotation_value_types, string_types from .throwable import Throwable from . import http from ..exceptions.exceptions import AlreadyEndedException log = logging.getLogger(__name__) # List of valid characters found at http://docs.aws.amazon.com/xray/latest/devguide/xray-api-segmentdocuments.html _valid_name_characters = string.ascii_letters + string.digits + '_.:/%&#=+\-@ ' class Entity(object): """ The parent class for segment/subsegment. It holds common properties and methods on segment and subsegment. """ def __init__(self, name): # required attributes self.id = self._generate_random_id() self.name = name self.name = ''.join([c for c in name if c in _valid_name_characters]) self.start_time = time.time() self.parent_id = None if self.name != name: log.warning("Removing Segment/Subsugment Name invalid characters.") # sampling self.sampled = True # state self.in_progress = True # meta fields self.http = {} self.annotations = {} self.metadata = {} self.aws = {} self.cause = {} # child subsegments # list is thread-safe self.subsegments = [] self.user = None def close(self, end_time=None): """ Close the trace entity by setting `end_time` and flip the in progress flag to False. :param int end_time: Epoch in seconds. If not specified current time will be used. """ self._check_ended() if end_time: self.end_time = end_time else: self.end_time = time.time() self.in_progress = False def add_subsegment(self, subsegment): """ Add input subsegment as a child subsegment. """ self._check_ended() subsegment.parent_id = self.id self.subsegments.append(subsegment) def remove_subsegment(self, subsegment): """ Remove input subsegment from child subsegments. """ self._check_ended() self.subsegments.remove(subsegment) def put_http_meta(self, key, value): """ Add http related metadata. :param str key: Currently supported keys are: * url * method * user_agent * client_ip * status * content_length :param value: status and content_length are int and for other supported keys string should be used. """ self._check_ended() if value is None: return if key == http.STATUS: if isinstance(value, string_types): value = int(value) self.apply_status_code(value) if key in http.request_keys: if 'request' not in self.http: self.http['request'] = {} self.http['request'][key] = value elif key in http.response_keys: if 'response' not in self.http: self.http['response'] = {} self.http['response'][key] = value else: log.warning("ignoring unsupported key %s in http meta.", key) def put_annotation(self, key, value): """ Annotate segment or subsegment with a key-value pair. Annotations will be indexed for later search query. :param str key: annotation key :param object value: annotation value. Any type other than string/number/bool will be dropped """ self._check_ended() if not isinstance(key, string_types): log.warning("ignoring non string type annotation key with type %s.", type(key)) return if not isinstance(value, annotation_value_types): log.warning("ignoring unsupported annotation value type %s.", type(value)) return self.annotations[key] = value def put_metadata(self, key, value, namespace='default'): """ Add metadata to segment or subsegment. Metadata is not indexed but can be later retrieved by BatchGetTraces API. :param str namespace: optional. Default namespace is `default`. It must be a string and prefix `AWS.` is reserved. :param str key: metadata key under specified namespace :param object value: any object that can be serialized into JSON string """ self._check_ended() if not isinstance(namespace, string_types): log.warning("ignoring non string type metadata namespace") return if namespace.startswith('AWS.'): log.warning("Prefix 'AWS.' is reserved, drop metadata with namespace %s", namespace) return if self.metadata.get(namespace, None): self.metadata[namespace][key] = value else: self.metadata[namespace] = {key: value} def set_aws(self, aws_meta): """ set aws section of the entity. This method is called by global recorder and botocore patcher to provide additonal information about AWS runtime. It is not recommended to manually set aws section. """ self._check_ended() self.aws = aws_meta def set_user(self, user): """ set user of an segment or subsegment. one segment or subsegment can only hold one user. User is indexed and can be later queried. """ self._check_ended() self.user = user def add_throttle_flag(self): self.throttle = True def add_fault_flag(self): self.fault = True def add_error_flag(self): self.error = True def apply_status_code(self, status_code): """ When a trace entity is generated under the http context, the status code will affect this entity's fault/error/throttle flags. Flip these flags based on status code. """ self._check_ended() if not status_code: return if status_code >= 500: self.add_fault_flag() elif status_code == 429: self.add_throttle_flag() self.add_error_flag() elif status_code >= 400: self.add_error_flag() def add_exception(self, exception, stack, remote=False): """ Add an exception to trace entities. :param Exception exception: the catched exception. :param list stack: the output from python built-in `traceback.extract_stack()`. :param bool remote: If False it means it's a client error instead of a downstream service. """ self._check_ended() self.add_fault_flag() if hasattr(exception, '_recorded'): setattr(self, 'cause', getattr(exception, '_cause_id')) return exceptions = [] exceptions.append(Throwable(exception, stack, remote)) self.cause['exceptions'] = exceptions self.cause['working_directory'] = os.getcwd() def serialize(self): """ Serialize to JSON document that can be accepted by the X-Ray backend service. It uses jsonpickle to perform serialization. """ try: return jsonpickle.encode(self, unpicklable=False) except Exception: log.exception("got an exception during serialization") def _delete_empty_properties(self, properties): """ Delete empty properties before serialization to avoid extra keys with empty values in the output json. """ if not self.parent_id: del properties['parent_id'] if not self.subsegments: del properties['subsegments'] if not self.aws: del properties['aws'] if not self.http: del properties['http'] if not self.cause: del properties['cause'] if not self.annotations: del properties['annotations'] if not self.metadata: del properties['metadata'] if not self.user: del properties['user'] del properties['sampled'] def _check_ended(self): if not self.in_progress: raise AlreadyEndedException("Already ended segment and subsegment cannot be modified.") def _generate_random_id(self): """ Generate a random 16-digit hex str. This is used for generating segment/subsegment id. """ return binascii.b2a_hex(os.urandom(8)).decode('utf-8') aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/facade_segment.py000066400000000000000000000073541321403223000255270ustar00rootroot00000000000000from .segment import Segment from ..exceptions.exceptions import FacadeSegmentMutationException MUTATION_UNSUPPORTED_MESSAGE = 'FacadeSegments cannot be mutated.' class FacadeSegment(Segment): """ This type of segment should only be used in an AWS Lambda environment. It holds the same id, traceid and sampling decision as the segment generated by Lambda service but its properties cannot be mutated except for its subsegments. If this segment is created before Lambda worker finishes initializatioin, all the child subsegments will be discarded. """ def __init__(self, name, entityid, traceid, sampled): self.initializing = self._is_initializing( entityid=entityid, traceid=traceid, sampled=sampled, ) super(FacadeSegment, self).__init__( name=name, entityid=entityid, traceid=traceid, sampled=sampled, ) def close(self, end_time=None): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def put_http_meta(self, key, value): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def put_annotation(self, key, value): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def put_metadata(self, key, value, namespace='default'): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def set_aws(self, aws_meta): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def set_user(self, user): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def add_throttle_flag(self): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def add_fault_flag(self): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def add_error_flag(self): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def add_exception(self, exception, stack, remote=False): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def apply_status_code(self, status_code): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def serialize(self): """ Unsupported operation. Will raise an exception. """ raise FacadeSegmentMutationException(MUTATION_UNSUPPORTED_MESSAGE) def ready_to_send(self): """ Facade segment should never be sent out. This always return False. """ return False def increment(self): """ Increment total subsegments counter by 1. """ self._subsegments_counter.increment() def decrement_ref_counter(self): """ No-op """ pass def _is_initializing(self, entityid, traceid, sampled): return not entityid or not traceid or sampled is None aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/http.py000066400000000000000000000005761321403223000235600ustar00rootroot00000000000000URL = "url" METHOD = "method" USER_AGENT = "user_agent" CLIENT_IP = "client_ip" X_FORWARDED_FOR = "x_forwarded_for" STATUS = "status" CONTENT_LENGTH = "content_length" XRAY_HEADER = "X-Amzn-Trace-Id" # for proxy header re-write ALT_XRAY_HEADER = "HTTP_X_AMZN_TRACE_ID" request_keys = (URL, METHOD, USER_AGENT, CLIENT_IP, X_FORWARDED_FOR) response_keys = (STATUS, CONTENT_LENGTH) aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/segment.py000066400000000000000000000066601321403223000242430ustar00rootroot00000000000000import copy from .entity import Entity from .traceid import TraceId from ..utils.atomic_counter import AtomicCounter from ..exceptions.exceptions import SegmentNameMissingException class Segment(Entity): """ The compute resources running your application logic send data about their work as segments. A segment provides the resource's name, details about the request, and details about the work done. """ def __init__(self, name, entityid=None, traceid=None, parent_id=None, sampled=True): """ Create a segment object. :param str name: segment name. If not specified a SegmentNameMissingException will be thrown. :param str entityid: hexdigits segment id. :param str traceid: The trace id of the segment. :param str parent_id: The parent id of the segment. It comes from id of an upstream segment or subsegment. :param bool sampled: If False this segment will not be sent to the X-Ray daemon. """ if not name: raise SegmentNameMissingException("Segment name is required.") super(Segment, self).__init__(name) if not traceid: traceid = TraceId().to_id() self.trace_id = traceid if entityid: self.id = entityid self.in_progress = True self.sampled = sampled self.ref_counter = AtomicCounter() self._subsegments_counter = AtomicCounter() if parent_id: self.parent_id = parent_id def add_subsegment(self, subsegment): """ Add input subsegment as a child subsegment and increment reference counter and total subsegments counter. """ super(Segment, self).add_subsegment(subsegment) self.increment() def increment(self): """ Increment reference counter to track on open subsegments and total subsegments counter to track total size of subsegments it currently hold. """ self.ref_counter.increment() self._subsegments_counter.increment() def decrement_ref_counter(self): """ Decrement reference counter by 1 when a subsegment is closed. """ self.ref_counter.decrement() def ready_to_send(self): """ Return True if the segment doesn't have any open subsegments and itself is not in progress. """ return self.ref_counter.get_current() <= 0 and not self.in_progress def get_total_subsegments_size(self): """ Return the number of total subsegments regardless of open or closed. """ return self._subsegments_counter.get_current() def decrement_subsegments_size(self): """ Decrement total subsegments by 1. This usually happens when a subsegment is streamed out. """ return self._subsegments_counter.decrement() def remove_subsegment(self, subsegment): """ Remove the reference of input subsegment. """ super(Segment, self).remove_subsegment(subsegment) self.decrement_subsegments_size() def __getstate__(self): """ Used by jsonpikle to remove unwanted fields. """ properties = copy.copy(self.__dict__) super(Segment, self)._delete_empty_properties(properties) del properties['ref_counter'] del properties['_subsegments_counter'] return properties aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/subsegment.py000066400000000000000000000054111321403223000247460ustar00rootroot00000000000000import copy from .entity import Entity from ..exceptions.exceptions import SegmentNotFoundException class Subsegment(Entity): """ The work done in a single segment can be broke down into subsegments. Subsegments provide more granular timing information and details about downstream calls that your application made to fulfill the original request. A subsegment can contain additional details about a call to an AWS service, an external HTTP API, or an SQL database. """ def __init__(self, name, namespace, segment): """ Create a new subsegment. :param str name: Subsegment name is required. :param str namespace: The namespace of the subsegment. Currently support `aws`, `remote` and `local`. :param Segment segment: The parent segment """ super(Subsegment, self).__init__(name) if not segment: raise SegmentNotFoundException("A parent segment is required for creating subsegments.") self.parent_segment = segment self.trace_id = segment.trace_id self.type = 'subsegment' self.namespace = namespace self.sql = {} def add_subsegment(self, subsegment): """ Add input subsegment as a child subsegment and increment reference counter and total subsegments counter of the parent segment. """ super(Subsegment, self).add_subsegment(subsegment) self.parent_segment.increment() def remove_subsegment(self, subsegment): """ Remove input subsegment from child subsegemnts and decrement parent segment total subsegments count. :param Subsegment: subsegment to remove. """ super(Subsegment, self).remove_subsegment(subsegment) self.parent_segment.decrement_subsegments_size() def close(self, end_time=None): """ Close the trace entity by setting `end_time` and flip the in progress flag to False. Also decrement parent segment's ref counter by 1. :param int end_time: Epoch in seconds. If not specified current time will be used. """ super(Subsegment, self).close(end_time) self.parent_segment.decrement_ref_counter() def set_sql(self, sql): """ Set sql related metadata. This function is used by patchers for database connectors and is not recommended to invoke manually. :param dict sql: sql related metadata """ self.sql = sql def __getstate__(self): properties = copy.copy(self.__dict__) super(Subsegment, self)._delete_empty_properties(properties) del properties['parent_segment'] if not self.sql: del properties['sql'] return properties aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/throwable.py000066400000000000000000000042201321403223000245560ustar00rootroot00000000000000import copy import os import binascii import logging from ..utils.compat import string_types log = logging.getLogger(__name__) class Throwable(object): """ An object recording exception infomation under trace entity `cause` section. The information includes the stack trace, working directory and message from the original exception. """ def __init__(self, exception, stack, remote=False): """ :param Exception exception: the catched exception. :param list stack: the formatted stack trace gathered through `traceback` module. :param bool remote: If False it means it's a client error instead of a downstream service. """ self.id = binascii.b2a_hex(os.urandom(8)).decode('utf-8') try: message = str(exception) # in case there is an exception cannot be converted to str except Exception: message = None # do not record non-string exception message if isinstance(message, string_types): self.message = message self.type = type(exception).__name__ self.remote = remote try: self._normalize_stack_trace(stack) except Exception: self.stack = None log.warning("can not parse stack trace string, ignore stack field.") if exception: setattr(exception, '_recorded', True) setattr(exception, '_cause_id', self.id) def _normalize_stack_trace(self, stack): if not stack: return None self.stack = [] for entry in stack: path = entry[0] line = entry[1] label = entry[2] if 'aws_xray_sdk/' in path: continue normalized = {} normalized['path'] = os.path.basename(path).replace('\"', ' ').strip() normalized['line'] = line normalized['label'] = label.strip() self.stack.append(normalized) def __getstate__(self): properties = copy.copy(self.__dict__) if not self.stack: del properties['stack'] return properties aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/trace_header.py000066400000000000000000000051421321403223000252010ustar00rootroot00000000000000import logging log = logging.getLogger(__name__) ROOT = 'Root' PARENT = 'Parent' SAMPLE = 'Sampled' HEADER_DELIMITER = ";" class TraceHeader(object): """ The sampling decision and trace ID are added to HTTP requests in tracing headers named ``X-Amzn-Trace-Id``. The first X-Ray-integrated service that the request hits adds a tracing header, which is read by the X-Ray SDK and included in the response. Learn more about `Tracing Header `_. """ def __init__(self, root=None, parent=None, sampled=None): """ :param str root: trace id :param str parent: parent id :param int sampled: 0 means not sampled, 1 means sampled """ self._root = root self._parent = parent self._sampled = None if sampled is not None: self._sampled = int(sampled) @classmethod def from_header_str(cls, header): """ Create a TraceHeader object from a tracing header string extracted from a http request headers. """ if not header: return cls() try: params = header.strip().split(HEADER_DELIMITER) header_dict = {} for param in params: entry = param.split('=') header_dict[entry[0]] = entry[1] return cls( root=header_dict.get(ROOT, None), parent=header_dict.get(PARENT, None), sampled=header_dict.get(SAMPLE, None), ) except Exception: log.warning("malformed tracing header %s, ignore.", header) return cls() def to_header_str(self): """ Convert to a tracing header string that can be injected to outgoing http request headers. """ h_str = '' if self.root: h_str = ROOT + '=' + self.root if self.parent: h_str = h_str + HEADER_DELIMITER + PARENT + '=' + self.parent if self.sampled is not None: h_str = h_str + HEADER_DELIMITER + SAMPLE + '=' + str(self.sampled) return h_str @property def root(self): """ Return trace id of the header """ return self._root @property def parent(self): """ Return the parent segment id in the header """ return self._parent @property def sampled(self): """ Return the sampling decision in the header. It's either 0 or 1. """ return self._sampled aws-xray-sdk-python-0.95/aws_xray_sdk/core/models/traceid.py000066400000000000000000000014061321403223000242050ustar00rootroot00000000000000import os import time import binascii class TraceId: """ A trace ID tracks the path of a request through your application. A trace collects all the segments generated by a single request. A trace ID is required for a segment. """ VERSION = '1' DELIMITER = '-' def __init__(self): """ Generate a random trace id. """ self.start_time = int(time.time()) self.__number = binascii.b2a_hex(os.urandom(12)).decode('utf-8') def to_id(self): """ Convert TraceId object to a string. """ return "%s%s%s%s%s" % (TraceId.VERSION, TraceId.DELIMITER, format(self.start_time, 'x'), TraceId.DELIMITER, self.__number) aws-xray-sdk-python-0.95/aws_xray_sdk/core/patcher.py000066400000000000000000000026531321403223000227420ustar00rootroot00000000000000import logging import importlib log = logging.getLogger(__name__) SUPPORTED_MODULES = ( 'aiobotocore', 'botocore', 'requests', 'sqlite3', 'mysql', ) _PATCHED_MODULES = set() def patch_all(): patch(SUPPORTED_MODULES, raise_errors=False) def patch(modules_to_patch, raise_errors=True): for m in modules_to_patch: _patch_module(m, raise_errors) def _patch_module(module_to_patch, raise_errors=True): # boto3 depends on botocore and patching botocore is sufficient if module_to_patch == 'boto3': module_to_patch = 'botocore' # aioboto3 depends on aiobotocore and patching aiobotocore is sufficient if module_to_patch == 'aioboto3': module_to_patch = 'aiobotocore' if module_to_patch not in SUPPORTED_MODULES: raise Exception('module %s is currently not supported for patching' % module_to_patch) try: _patch(module_to_patch) except Exception: if raise_errors: raise log.debug('failed to patch module %s', module_to_patch) def _patch(module_to_patch): path = 'aws_xray_sdk.ext.%s' % module_to_patch if module_to_patch in _PATCHED_MODULES: log.debug('%s already patched', module_to_patch) imported_module = importlib.import_module(path) imported_module.patch() _PATCHED_MODULES.add(module_to_patch) log.info('successfully patched module %s', module_to_patch) aws-xray-sdk-python-0.95/aws_xray_sdk/core/plugins/000077500000000000000000000000001321403223000224155ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/plugins/__init__.py000066400000000000000000000000001321403223000245140ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/plugins/ec2_plugin.py000066400000000000000000000014031321403223000250140ustar00rootroot00000000000000import logging import requests log = logging.getLogger(__name__) SERVICE_NAME = 'ec2' ORIGIN = 'AWS::EC2::Instance' def initialize(): """ Try to get EC2 instance-id and AZ if running on EC2 by querying http://169.254.169.254/latest/meta-data/. If not continue. """ global runtime_context try: runtime_context = {} r = requests.get('http://169.254.169.254/latest/meta-data/instance-id', timeout=1) runtime_context['instance_id'] = r.text r = requests.get('http://169.254.169.254/latest/meta-data/placement/availability-zone', timeout=1) runtime_context['availability_zone'] = r.text except Exception: runtime_context = None log.warning("failed to get ec2 instance metadata.") aws-xray-sdk-python-0.95/aws_xray_sdk/core/plugins/ecs_plugin.py000066400000000000000000000006601321403223000251210ustar00rootroot00000000000000import socket import logging log = logging.getLogger(__name__) SERVICE_NAME = 'ecs' ORIGIN = 'AWS::ECS::Container' def initialize(): global runtime_context try: runtime_context = {} host_name = socket.gethostname() if host_name: runtime_context['container'] = host_name except Exception: runtime_context = None log.warning("failed to get ecs container metadata") aws-xray-sdk-python-0.95/aws_xray_sdk/core/plugins/elasticbeanstalk_plugin.py000066400000000000000000000007251321403223000276620ustar00rootroot00000000000000import logging import json log = logging.getLogger(__name__) CONF_PATH = '/var/elasticbeanstalk/xray/environment.conf' SERVICE_NAME = 'elastic_beanstalk' ORIGIN = 'AWS::ElasticBeanstalk::Environment' def initialize(): global runtime_context try: with open(CONF_PATH) as f: runtime_context = json.load(f) except Exception: runtime_context = None log.warning("failed to load Elastic Beanstalk environment config file") aws-xray-sdk-python-0.95/aws_xray_sdk/core/plugins/utils.py000066400000000000000000000013651321403223000241340ustar00rootroot00000000000000import importlib from ..exceptions.exceptions import MissingPluginNames module_prefix = 'aws_xray_sdk.core.plugins.' PLUGIN_MAPPING = { 'elasticbeanstalkplugin': 'elasticbeanstalk_plugin', 'ec2plugin': 'ec2_plugin', 'ecsplugin': 'ecs_plugin' } def get_plugin_modules(plugins): """ Get plugin modules from input strings :param tuple plugins: a tuple of plugin names in str """ if not plugins: raise MissingPluginNames("input plugin names are required") modules = [] for plugin in plugins: short_name = PLUGIN_MAPPING.get(plugin.lower(), plugin.lower()) full_path = '%s%s' % (module_prefix, short_name) modules.append(importlib.import_module(full_path)) return tuple(modules) aws-xray-sdk-python-0.95/aws_xray_sdk/core/recorder.py000066400000000000000000000345241321403223000231230ustar00rootroot00000000000000import logging import time import os import traceback import json import wrapt from .models.segment import Segment from .models.subsegment import Subsegment from .models.default_dynamic_naming import DefaultDynamicNaming from .models.dummy_entities import DummySegment, DummySubsegment from .emitters.udp_emitter import UDPEmitter from .sampling.default_sampler import DefaultSampler from .context import Context from .plugins.utils import get_plugin_modules from .lambda_launcher import check_in_lambda from .exceptions.exceptions import SegmentNameMissingException from .utils.compat import string_types log = logging.getLogger(__name__) TRACING_NAME_KEY = 'AWS_XRAY_TRACING_NAME' DAEMON_ADDR_KEY = 'AWS_XRAY_DAEMON_ADDRESS' CONTEXT_MISSING_KEY = 'AWS_XRAY_CONTEXT_MISSING' class AWSXRayRecorder(object): """ A global AWS X-Ray recorder that will begin/end segments/subsegments and send them to the X-Ray daemon. This recorder is initialized during loading time so you can use:: from aws_xray_sdk.core import xray_recorder in your module to access it """ def __init__(self): context = check_in_lambda() if context: self._context = context self._max_subsegments = 0 else: self._context = Context() self._max_subsegments = 30 self._emitter = UDPEmitter() self._sampler = DefaultSampler() self._sampling = True self._max_trace_back = 10 self._plugins = None self._service = os.getenv(TRACING_NAME_KEY) self._dynamic_naming = None def configure(self, sampling=None, plugins=None, context_missing=None, sampling_rules=None, daemon_address=None, service=None, context=None, emitter=None, dynamic_naming=None): """Configure global X-Ray recorder. Configure needs to run before patching thrid party libraries to avoid creating dangling subsegment. :param bool sampling: If sampling is enabled, every time the recorder creates a segment it decides whether to send this segment to the X-Ray daemon. This setting is not used if the recorder is running in AWS Lambda. :param sampling_rules: Pass a set of custom sampling rules. Can be an absolute path of the sampling rule config json file or a dictionary that defines those rules. :param tuple plugins: plugins that add extra metadata to each segment. Currently available plugins are EC2Plugin, ECS plugin and ElasticBeanstalkPlugin. :param str context_missing: recorder behavior when it tries to mutate a segment or add a subsegment but there is no active segment. RUNTIME_ERROR means the recorder will raise an exception. LOG_ERROR means the recorder will only log the error and do nothing. :param str daemon_address: The X-Ray daemon address where the recorder sends data to. :param str service: default segment name if creating a segment without providing a name. :param context: You can pass your own implementation of context storage for active segment/subsegment by overriding the default ``Context`` class. :param emitter: The emitter that sends a segment/subsegment to the X-Ray daemon. You can override ``UDPEmitter`` class. :param dynamic_naming: a string that defines a pattern that host names should match. Alternatively you can pass a module which overrides ``DefaultDynamicNaming`` module. Environment variables AWS_XRAY_DAEMON_ADDRESS, AWS_XRAY_CONTEXT_MISSING and AWS_XRAY_TRACING_NAME respectively overrides arguments daemon_address, context_missing and service. """ if sampling is not None: self.sampling = sampling if service: self.service = os.getenv(TRACING_NAME_KEY, service) if sampling_rules: self._load_sampling_rules(sampling_rules) if emitter: self.emitter = emitter if daemon_address: self.emitter.set_daemon_address(os.getenv(DAEMON_ADDR_KEY, daemon_address)) if context: self.context = context if context_missing: self.context.context_missing = os.getenv(CONTEXT_MISSING_KEY, context_missing) if dynamic_naming: self.dynamic_naming = dynamic_naming plugin_modules = None if plugins: plugin_modules = get_plugin_modules(plugins) for module in plugin_modules: module.initialize() self._plugins = plugin_modules def begin_segment(self, name=None, traceid=None, parent_id=None, sampling=None): """ Begin a segment on the current thread and return it. The recorder only keeps one segment at a time. Create the second one without closing existing one will overwrite it. :param str name: the name of the segment :param str traceid: trace id of the segment :param int sampling: 0 means not sampled, 1 means sampled """ seg_name = name or self.service if not seg_name: raise SegmentNameMissingException("Segment name is required.") # we respect sampling decision regardless of recorder configuration. dummy = False if sampling == 0: dummy = True elif sampling == 1: dummy = False elif self.sampling and not self._sampler.should_trace(): dummy = True if dummy: segment = DummySegment(seg_name) else: segment = Segment(name=seg_name, traceid=traceid, parent_id=parent_id) self._populate_runtime_context(segment) self.context.put_segment(segment) return segment def end_segment(self, end_time=None): """ End the current segment and send it to X-Ray daemon if it is ready to send. Ready means segment and all its subsegments are closed. :param float end_time: segment compeletion in unix epoch in seconds. """ self.context.end_segment(end_time) if self.current_segment().ready_to_send(): self._send_segment() def current_segment(self): """ Return the currently active segment. In a multithreading environment, this will make sure the segment returned is the one created by the same thread. """ entity = self.get_trace_entity() if self._is_subsegment(entity): return entity.parent_segment else: return entity def begin_subsegment(self, name, namespace='local'): """ Begin a new subsegment. If there is open subsegment, the newly created subsegment will be the child of latest opened subsegment. If not, it will be the child of the current open segment. :param str name: the name of the subsegment. :param str namespace: currently can only be 'local', 'remote', 'aws'. """ segment = self.current_segment() if not segment: log.warning("No segment found, cannot begin subsegment %s." % name) return None if not segment.sampled: subsegment = DummySubsegment(segment, name) else: subsegment = Subsegment(name, namespace, segment) self.context.put_subsegment(subsegment) return subsegment def current_subsegment(self): """ Return the latest opened subsegment. In a multithreading environment, this will make sure the subsegment returned is one created by the same thread. """ entity = self.get_trace_entity() if self._is_subsegment(entity): return entity else: return None def end_subsegment(self, end_time=None): """ End the current active subsegment. If this is the last one open under its parent segment, the entire segment will be sent. :param float end_time: subsegment compeletion in unix epoch in seconds. """ if not self.context.end_subsegment(end_time): return # if segment is already close, we check if we can send entire segment # otherwise we check if we need to stream some subsegments if self.current_segment().ready_to_send(): self._send_segment() else: self.stream_subsegments() def get_trace_entity(self): """ A pass through method to ``context.get_trace_entity()``. """ return self.context.get_trace_entity() def set_trace_entity(self, trace_entity): """ A pass through method to ``context.set_trace_entity()``. """ self.context.set_trace_entity(trace_entity) def clear_trace_entities(self): """ A pass through method to ``context.clear_trace_entities()``. """ self.context.clear_trace_entities() def stream_subsegments(self): """ Stream all closed subsegments to the daemon and remove reference to the parent segment. No-op for a not sampled segment. """ segment = self.current_segment() if not segment or not segment.sampled: return if segment.get_total_subsegments_size() <= self._max_subsegments: return # find all subsegments that has no open child subsegments and # send them to the daemon self._stream_eligible_subsegments(segment) def _stream_eligible_subsegments(self, subsegment): children = subsegment.subsegments children_ready = [] if len(children) > 0: for child in children: if self._stream_eligible_subsegments(child): children_ready.append(child) if len(children_ready) == len(children) and not subsegment.in_progress: return True # stream all ready children before return False for child in children_ready: self._stream_subsegment(child) subsegment.remove_subsegment(child) return False def capture(self, name=None): """ A decorator that records enclosed function in a subsegment. It only works with synchronous functions. params str name: The name of the subsegment. If not specified the function name will be used. """ @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): func_name = name if not func_name: func_name = wrapped.__name__ return self.record_subsegment( wrapped, instance, args, kwargs, name=func_name, namespace='local', meta_processor=None, ) return wrapper def record_subsegment(self, wrapped, instance, args, kwargs, name, namespace, meta_processor): subsegment = self.begin_subsegment(name, namespace) exception = None stack = None return_value = None try: return_value = wrapped(*args, **kwargs) return return_value except Exception as e: exception = e stack = traceback.extract_stack(limit=self._max_trace_back) raise finally: end_time = time.time() if callable(meta_processor): meta_processor( wrapped=wrapped, instance=instance, args=args, kwargs=kwargs, return_value=return_value, exception=exception, subsegment=subsegment, stack=stack, ) elif exception: if subsegment: subsegment.add_exception(exception, stack) self.end_subsegment(end_time) def _populate_runtime_context(self, segment): if not self._plugins: return aws_meta = {} for plugin in self._plugins: if plugin.runtime_context: aws_meta[plugin.SERVICE_NAME] = plugin.runtime_context setattr(segment, 'origin', plugin.ORIGIN) segment.set_aws(aws_meta) def _send_segment(self): """ Send the current segment to X-Ray daemon if it is present and sampled, then clean up context storage. The emitter will handle failures. """ segment = self.current_segment() if not segment: return if segment.sampled: self.emitter.send_entity(segment) self.clear_trace_entities() def _stream_subsegment(self, subsegment): log.debug("streaming subsegments...") self.emitter.send_entity(subsegment) def _load_sampling_rules(self, sampling_rules): if not sampling_rules: return if isinstance(sampling_rules, dict): self.sampler = DefaultSampler(sampling_rules) else: with open(sampling_rules) as f: self.sampler = DefaultSampler(json.load(f)) def _is_subsegment(self, entity): return (hasattr(entity, 'type') and entity.type == 'subsegment') @property def sampling(self): return self._sampling @sampling.setter def sampling(self, value): self._sampling = value @property def sampler(self): return self._sampler @sampler.setter def sampler(self, value): self._sampler = value @property def service(self): return self._service @service.setter def service(self, value): self._service = value @property def dynamic_naming(self): return self._dynamic_naming @dynamic_naming.setter def dynamic_naming(self, value): if isinstance(value, string_types): self._dynamic_naming = DefaultDynamicNaming(value, self.service) else: self._dynamic_naming = value @property def context(self): return self._context @context.setter def context(self, cxt): self._context = cxt @property def emitter(self): return self._emitter @emitter.setter def emitter(self, value): self._emitter = value aws-xray-sdk-python-0.95/aws_xray_sdk/core/sampling/000077500000000000000000000000001321403223000225465ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/sampling/__init__.py000066400000000000000000000000001321403223000246450ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/sampling/default_sampler.py000066400000000000000000000061731321403223000262760ustar00rootroot00000000000000import os import json from random import Random from pkg_resources import resource_filename from .sampling_rule import SamplingRule from ..exceptions.exceptions import InvalidSamplingManifestError with open(resource_filename(__name__, 'default_sampling_rule.json')) as f: default_sampling_rule = json.load(f) class DefaultSampler(object): """ The default sampler that holds either custom sampling rules or default sampling rules. Every time before the X-Ray recorder generates a segment, it calculates if this segment is sampled or not. """ def __init__(self, rules=default_sampling_rule): """ :param dict rules: a dict that defines custom sampling rules. An example configuration: { "version": 1, "rules": [ { "description": "Player moves.", "service_name": "*", "http_method": "*", "url_path": "/api/move/*", "fixed_target": 0, "rate": 0.05 } ], "default": { "fixed_target": 1, "rate": 0.1 } } This example defines one custom rule and a default rule. The custom rule applies a five-percent sampling rate with no minimum number of requests to trace for paths under /api/move/. The default rule traces the first request each second and 10 percent of additional requests. The SDK applies custom rules in the order in which they are defined. If a request matches multiple custom rules, the SDK applies only the first rule. """ version = rules.get('version', None) if version != 1: raise InvalidSamplingManifestError('Manifest version: %s is not supported.', version) if 'default' not in rules: raise InvalidSamplingManifestError('A default rule must be provided.') self._default_rule = SamplingRule(rule_dict=rules['default'], default=True) self._rules = [] if 'rules' in rules: for rule in rules['rules']: self._rules.append(SamplingRule(rule)) self._random = Random() def should_trace(self, service_name=None, method=None, path=None): """ Return True if the sampler decide to sample based on input information and sampling rules. It will first check if any custom rule should be applied, if not it falls back to the default sampling rule. All optional arugments are extracted from incoming requests by X-Ray middleware to perform path based sampling. """ if service_name and method and path: for rule in self._rules: if rule.applies(service_name, method, path): return self._should_trace(rule) return self._should_trace(self._default_rule) def _should_trace(self, sampling_rule): if sampling_rule.reservoir.take(): return True else: return self._random.random() < sampling_rule.rate aws-xray-sdk-python-0.95/aws_xray_sdk/core/sampling/default_sampling_rule.json000066400000000000000000000001501321403223000300020ustar00rootroot00000000000000{ "version": 1, "default": { "fixed_target": 1, "rate": 0.05 }, "rules": [ ] }aws-xray-sdk-python-0.95/aws_xray_sdk/core/sampling/reservoir.py000066400000000000000000000020041321403223000251340ustar00rootroot00000000000000import time import threading class Reservoir(object): """ Keeps track of the number of sampled segments within a single second. This class is implemented to be thread-safe to achieve accurate sampling. """ def __init__(self, traces_per_sec=0): """ :param int traces_per_sec: number of guranteed sampled segments. """ self._lock = threading.Lock() self.traces_per_sec = traces_per_sec self.used_this_sec = 0 self.this_sec = int(time.time()) def take(self): """ Returns True if there are segments left within the current second, otherwise return False. """ with self._lock: now = int(time.time()) if now != self.this_sec: self.used_this_sec = 0 self.this_sec = now if self.used_this_sec >= self.traces_per_sec: return False self.used_this_sec = self.used_this_sec + 1 return True aws-xray-sdk-python-0.95/aws_xray_sdk/core/sampling/sampling_rule.py000066400000000000000000000065471321403223000257750ustar00rootroot00000000000000from .reservoir import Reservoir from ..utils.search_pattern import wildcard_match from ..exceptions.exceptions import InvalidSamplingManifestError class SamplingRule(object): """ One SamolingRule represents one rule defined from rule json file or from a dictionary. It can be either a custom rule or default rule. """ FIXED_TARGET = 'fixed_target' RATE = 'rate' SERVICE_NAME = 'service_name' METHOD = 'http_method' PATH = 'url_path' def __init__(self, rule_dict, default=False): """ :param dict rule_dict: The dictionary that defines a single rule. :param bool default: Indicates if this is the default rule. A default rule cannot have `service_name`, `http_method` or `url_path`. """ self._fixed_target = rule_dict.get(self.FIXED_TARGET, None) self._rate = rule_dict.get(self.RATE, None) self._service_name = rule_dict.get(self.SERVICE_NAME, None) self._method = rule_dict.get(self.METHOD, None) self._path = rule_dict.get(self.PATH, None) self._default = default self._validate() self._reservoir = Reservoir(self.fixed_target) def applies(self, service_name, method, path): """ Determines whether or not this sampling rule applies to the incoming request based on some of the request's parameters. Any None parameters provided will be considered an implicit match. """ return (not service_name or wildcard_match(self.service_name, service_name)) \ and (not method or wildcard_match(self.service_name, method)) \ and (not path or wildcard_match(self.path, path)) @property def fixed_target(self): """ Defines fixed number of sampled segments per second. This doesn't count for sampling rate. """ return self._fixed_target @property def rate(self): """ A float number less than 1.0 defines the sampling rate. """ return self._rate @property def service_name(self): """ The host name of the reqest to sample. """ return self._service_name @property def method(self): """ HTTP method of the request to sample. """ return self._method @property def path(self): """ The url path of the request to sample. """ return self._path @property def reservoir(self): """ Keeps track of used sampled targets within the second. """ return self._reservoir def _validate(self): if self.fixed_target < 0 or self.rate < 0: raise InvalidSamplingManifestError('All rules must have non-negative values for ' 'fixed_target and rate') if self._default: if self.service_name or self.method or self.path: raise InvalidSamplingManifestError('The default rule must not specify values for ' 'url_path, service_name, or http_method') else: if not self.service_name or not self.method or not self.path: raise InvalidSamplingManifestError('All non-default rules must have values for ' 'url_path, service_name, and http_method') aws-xray-sdk-python-0.95/aws_xray_sdk/core/utils/000077500000000000000000000000001321403223000220745ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/utils/__init__.py000066400000000000000000000000001321403223000241730ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/core/utils/atomic_counter.py000066400000000000000000000012701321403223000254610ustar00rootroot00000000000000import threading class AtomicCounter(object): """ A helper class that implements a thread-safe counter. """ def __init__(self, initial=0): self.value = initial self._lock = threading.Lock() self._initial = initial def increment(self, num=1): with self._lock: self.value += num return self.value def decrement(self, num=1): with self._lock: self.value -= num return self.value def get_current(self): with self._lock: return self.value def reset(self): with self._lock: self.value = self._initial return self.value aws-xray-sdk-python-0.95/aws_xray_sdk/core/utils/compat.py000066400000000000000000000004351321403223000237330ustar00rootroot00000000000000import sys PY2 = sys.version_info < (3,) PY35 = sys.version_info >= (3, 5) if PY2: annotation_value_types = (int, long, float, bool, str) # noqa: F821 string_types = basestring # noqa: F821 else: annotation_value_types = (int, float, bool, str) string_types = str aws-xray-sdk-python-0.95/aws_xray_sdk/core/utils/search_pattern.py000066400000000000000000000056021321403223000254530ustar00rootroot00000000000000def wildcard_match(pattern, text, case_insensitive=True): """ Performs a case-insensitive wildcard match against two strings. This method works with pseduo-regex chars; specifically ? and * are supported. An asterisk (*) represents any combination of characters. A question mark (?) represents any single character. :param str pattern: the regex-like pattern to be compared against :param str text: the string to compare against the pattern :param boolean case_insensitive: dafault is True return whether the text matches the pattern """ if pattern is None or text is None: return False pattern_len = len(pattern) text_len = len(text) if pattern_len == 0: return text_len == 0 # Check the special case of a single * pattern, as it's common if pattern == '*': return True if case_insensitive: pattern = pattern.lower() text = text.lower() # Infix globs are relatively rare, and the below search is expensive. # Check for infix globs and, in their absence, do the simple thing. if '*' not in pattern or pattern.index('*') == len(pattern) - 1: return _simple_wildcard_match(pattern, text) # The res[i] is used to record if there is a match between # the first i chars in text and the first j chars in pattern. # So will return res[textLength+1] in the end # Loop from the beginning of the pattern # case not '*': if text[i]==pattern[j] or pattern[j] is '?', # and res[i] is true, set res[i+1] to true, otherwise false. # case '*': since '*' can match any globing, as long as there is a true # in res before i, all the res[i+1], res[i+2],...,res[textLength] # could be true res = [None] * (text_len + 1) res[0] = True for j in range(0, pattern_len): p = pattern[j] if p != '*': for i in range(text_len - 1, -1, -1): res[i + 1] = res[i] and (p == '?' or (p == text[i])) else: i = 0 while i <= text_len and not res[i]: i += 1 for m in range(i, text_len + 1): res[m] = True res[0] = res[0] and (p == '*') return res[text_len] def _simple_wildcard_match(pattern, text): j = 0 pattern_len = len(pattern) text_len = len(text) for i in range(0, pattern_len): p = pattern[i] if p == '*': # Presumption for this method is that globs only occur at end return True elif p == '?': if j == text_len: # No character to match return False j += 1 else: if j >= text_len: return False if(p != text[j]): return False j += 1 # Ate up all the pattern and didn't end at a glob, so a match # will have consumed all the text return j == text_len aws-xray-sdk-python-0.95/aws_xray_sdk/ext/000077500000000000000000000000001321403223000206045ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/__init__.py000066400000000000000000000000001321403223000227030ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/aiobotocore/000077500000000000000000000000001321403223000231115ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/aiobotocore/__init__.py000066400000000000000000000000561321403223000252230ustar00rootroot00000000000000from .patch import patch __all__ = ['patch'] aws-xray-sdk-python-0.95/aws_xray_sdk/ext/aiobotocore/patch.py000066400000000000000000000020041321403223000245560ustar00rootroot00000000000000import aiobotocore.client import wrapt from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.boto_utils import inject_header, aws_meta_processor def patch(): """ Patch aiobotocore client so it generates subsegments when calling AWS services. """ if hasattr(aiobotocore.client, '_xray_enabled'): return setattr(aiobotocore.client, '_xray_enabled', True) wrapt.wrap_function_wrapper( 'aiobotocore.client', 'AioBaseClient._make_api_call', _xray_traced_aiobotocore, ) wrapt.wrap_function_wrapper( 'aiobotocore.endpoint', 'AioEndpoint._encode_headers', inject_header, ) async def _xray_traced_aiobotocore(wrapped, instance, args, kwargs): service = instance._service_model.metadata["endpointPrefix"] result = await xray_recorder.record_subsegment_async( wrapped, instance, args, kwargs, name=service, namespace='aws', meta_processor=aws_meta_processor, ) return result aws-xray-sdk-python-0.95/aws_xray_sdk/ext/aiohttp/000077500000000000000000000000001321403223000222545ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/aiohttp/__init__.py000066400000000000000000000000001321403223000243530ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/aiohttp/middleware.py000066400000000000000000000054601321403223000247500ustar00rootroot00000000000000""" AioHttp Middleware """ import traceback from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http from aws_xray_sdk.ext.util import calculate_sampling_decision, calculate_segment_name, construct_xray_header async def middleware(app, handler): """ AioHttp Middleware Factory """ async def _middleware(request): """ Main middleware function, deals with all the X-Ray segment logic """ # Create X-Ray headers xray_header = construct_xray_header(request.headers) # Get name of service or generate a dynamic one from host name = calculate_segment_name(request.headers['host'].split(':', 1)[0], xray_recorder) sampling_decision = calculate_sampling_decision( trace_header=xray_header, recorder=xray_recorder, service_name=request.headers['host'], method=request.method, path=request.path, ) # Start a segment segment = xray_recorder.begin_segment( name=name, traceid=xray_header.root, parent_id=xray_header.parent, sampling=sampling_decision, ) # Store request metadata in the current segment segment.put_http_meta(http.URL, request.url) segment.put_http_meta(http.METHOD, request.method) if 'User-Agent' in request.headers: segment.put_http_meta(http.USER_AGENT, request.headers['User-Agent']) if 'X-Forwarded-For' in request.headers: segment.put_http_meta(http.CLIENT_IP, request.headers['X-Forwarded-For']) segment.put_http_meta(http.X_FORWARDED_FOR, True) elif 'remote_addr' in request.headers: segment.put_http_meta(http.CLIENT_IP, request.headers['remote_addr']) else: segment.put_http_meta(http.CLIENT_IP, request.remote) try: # Call next middleware or request handler response = await handler(request) except Exception as err: # Store exception information including the stacktrace to the segment segment = xray_recorder.current_segment() segment.put_http_meta(http.STATUS, 500) stack = traceback.extract_stack(limit=xray_recorder._max_trace_back) segment.add_exception(err, stack) xray_recorder.end_segment() raise # Store response metadata into the current segment segment.put_http_meta(http.STATUS, response.status) if 'Content-Length' in response.headers: length = int(response.headers['Content-Length']) segment.put_http_meta(http.CONTENT_LENGTH, length) # Close segment so it can be dispatched off to the daemon xray_recorder.end_segment() return response return _middleware aws-xray-sdk-python-0.95/aws_xray_sdk/ext/boto_utils.py000066400000000000000000000077771321403223000233630ustar00rootroot00000000000000from __future__ import absolute_import # Need absolute import as botocore is also in the current folder for py27 import json from pkg_resources import resource_filename from botocore.exceptions import ClientError from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http from aws_xray_sdk.ext.util import inject_trace_header, to_snake_case with open(resource_filename(__name__, 'resources/aws_para_whitelist.json'), 'r') as data_file: whitelist = json.load(data_file) def inject_header(wrapped, instance, args, kwargs): headers = args[0] inject_trace_header(headers, xray_recorder.current_subsegment()) return wrapped(*args, **kwargs) def aws_meta_processor(wrapped, instance, args, kwargs, return_value, exception, subsegment, stack): region = instance.meta.region_name if 'operation_name' in kwargs: operation_name = kwargs['operation_name'] else: operation_name = args[0] aws_meta = { 'operation': operation_name, 'region': region, } if return_value: resp_meta = return_value.get('ResponseMetadata') if resp_meta: aws_meta['request_id'] = resp_meta.get('RequestId') subsegment.put_http_meta(http.STATUS, resp_meta.get('HTTPStatusCode')) # for service like S3 that returns special request id in response headers if 'HTTPHeaders' in resp_meta and resp_meta['HTTPHeaders'].get('x-amz-id-2'): aws_meta['id_2'] = resp_meta['HTTPHeaders']['x-amz-id-2'] elif exception: _aws_error_handler(exception, stack, subsegment, aws_meta) _extract_whitelisted_params(subsegment.name, operation_name, aws_meta, args, kwargs, return_value) subsegment.set_aws(aws_meta) def _aws_error_handler(exception, stack, subsegment, aws_meta): if not exception or not isinstance(exception, ClientError): return response_metadata = exception.response.get('ResponseMetadata') if not response_metadata: return aws_meta['request_id'] = response_metadata.get('RequestId') status_code = response_metadata.get('HTTPStatusCode') subsegment.put_http_meta(http.STATUS, status_code) subsegment.add_exception(exception, stack, True) def _extract_whitelisted_params(service, operation, aws_meta, args, kwargs, response): # check if service is whitelisted if service not in whitelist['services']: return operations = whitelist['services'][service]['operations'] # check if operation is whitelisted if operation not in operations: return params = operations[operation] # record whitelisted request/response parameters if 'request_parameters' in params: _record_params(params['request_parameters'], args[1], aws_meta) if 'request_descriptors' in params: _record_special_params(params['request_descriptors'], args[1], aws_meta) if 'response_parameters' in params and response: _record_params(params['response_parameters'], response, aws_meta) if 'response_descriptors' in params and response: _record_special_params(params['response_descriptors'], response, aws_meta) def _record_params(whitelisted, actual, aws_meta): for key in whitelisted: if key in actual: snake_key = to_snake_case(key) aws_meta[snake_key] = actual[key] def _record_special_params(whitelisted, actual, aws_meta): for key in whitelisted: if key in actual: _process_descriptor(whitelisted[key], actual[key], aws_meta) def _process_descriptor(descriptor, value, aws_meta): # "get_count" = true if 'get_count' in descriptor and descriptor['get_count']: value = len(value) # "get_keys" = true if 'get_keys' in descriptor and descriptor['get_keys']: value = value.keys() aws_meta[descriptor['rename_to']] = value aws-xray-sdk-python-0.95/aws_xray_sdk/ext/botocore/000077500000000000000000000000001321403223000224205ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/botocore/__init__.py000066400000000000000000000000561321403223000245320ustar00rootroot00000000000000from .patch import patch __all__ = ['patch'] aws-xray-sdk-python-0.95/aws_xray_sdk/ext/botocore/patch.py000066400000000000000000000017001321403223000240670ustar00rootroot00000000000000import wrapt import botocore.client from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.boto_utils import inject_header, aws_meta_processor def patch(): """ Patch botocore client so it generates subsegments when calling AWS services. """ if hasattr(botocore.client, '_xray_enabled'): return setattr(botocore.client, '_xray_enabled', True) wrapt.wrap_function_wrapper( 'botocore.client', 'BaseClient._make_api_call', _xray_traced_botocore, ) wrapt.wrap_function_wrapper( 'botocore.endpoint', 'Endpoint._encode_headers', inject_header, ) def _xray_traced_botocore(wrapped, instance, args, kwargs): service = instance._service_model.metadata["endpointPrefix"] return xray_recorder.record_subsegment( wrapped, instance, args, kwargs, name=service, namespace='aws', meta_processor=aws_meta_processor, ) aws-xray-sdk-python-0.95/aws_xray_sdk/ext/dbapi2.py000066400000000000000000000032771321403223000223300ustar00rootroot00000000000000import copy import wrapt from aws_xray_sdk.core import xray_recorder class XRayTracedConn(wrapt.ObjectProxy): _xray_meta = None def __init__(self, conn, meta={}): super(XRayTracedConn, self).__init__(conn) self._xray_meta = meta def cursor(self, *args, **kwargs): cursor = self.__wrapped__.cursor(*args, **kwargs) return XRayTracedCursor(cursor, self._xray_meta) class XRayTracedCursor(wrapt.ObjectProxy): _xray_meta = None def __init__(self, cursor, meta={}): super(XRayTracedCursor, self).__init__(cursor) self._xray_meta = meta # we preset database type if db is framework built-in if not self._xray_meta.get('database_type'): db_type = cursor.__class__.__module__.split('.')[0] self._xray_meta['database_type'] = db_type @xray_recorder.capture() def execute(self, query, *args, **kwargs): add_sql_meta(self._xray_meta) return self.__wrapped__.execute(query, *args, **kwargs) @xray_recorder.capture() def executemany(self, query, *args, **kwargs): add_sql_meta(self._xray_meta) return self.__wrapped__.executemany(query, *args, **kwargs) @xray_recorder.capture() def callproc(self, proc, args): add_sql_meta(self._xray_meta) return self.__wrapped__.callproc(proc, args) def add_sql_meta(meta): subsegment = xray_recorder.current_subsegment() if not subsegment: return if meta.get('name', None): subsegment.name = meta['name'] sql_meta = copy.copy(meta) if sql_meta.get('name', None): del sql_meta['name'] subsegment.set_sql(sql_meta) subsegment.namespace = 'remote' aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/000077500000000000000000000000001321403223000220465ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/__init__.py000066400000000000000000000000771321403223000241630ustar00rootroot00000000000000default_app_config = 'aws_xray_sdk.ext.django.apps.XRayConfig' aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/apps.py000066400000000000000000000031571321403223000233710ustar00rootroot00000000000000import logging from django.apps import AppConfig from .conf import settings from .db import patch_db from .templates import patch_template from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.exceptions.exceptions import SegmentNameMissingException log = logging.getLogger(__name__) class XRayConfig(AppConfig): name = 'aws_xray_sdk.ext.django' def ready(self): """ Configure global XRay recorder based on django settings under XRAY_RECORDER namespace. This method could be called twice during server startup because of base command and reload command. So this function must be idempotent """ if not settings.AWS_XRAY_TRACING_NAME: raise SegmentNameMissingException('Segment name is required.') xray_recorder.configure( daemon_address=settings.AWS_XRAY_DAEMON_ADDRESS, sampling=settings.SAMPLING, sampling_rules=settings.SAMPLING_RULES, context_missing=settings.AWS_XRAY_CONTEXT_MISSING, plugins=settings.PLUGINS, service=settings.AWS_XRAY_TRACING_NAME, dynamic_naming=settings.DYNAMIC_NAMING, ) # if turned on subsegment will be generated on # built-in database and template rendering if settings.AUTO_INSTRUMENT: try: patch_db() except Exception: log.debug('failed to patch Django built-in database') try: patch_template() except Exception: log.debug('failed to patch Django built-in template engine') aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/conf.py000066400000000000000000000040261321403223000233470ustar00rootroot00000000000000import os from django.conf import settings as django_settings from django.test.signals import setting_changed DEFAULTS = { 'AWS_XRAY_DAEMON_ADDRESS': '127.0.0.1:2000', 'AUTO_INSTRUMENT': True, 'AWS_XRAY_CONTEXT_MISSING': 'RUNTIME_ERROR', 'PLUGINS': (), 'SAMPLING': True, 'SAMPLING_RULES': None, 'AWS_XRAY_TRACING_NAME': None, 'DYNAMIC_NAMING': None, } XRAY_NAMESPACE = 'XRAY_RECORDER' SUPPORTED_ENV_VARS = ('AWS_XRAY_DAEMON_ADDRESS', 'AWS_XRAY_CONTEXT_MISSING', 'AWS_XRAY_TRACING_NAME', ) class XRaySettings(object): """ A object of Django settings to easily modify certain fields. The precedence for configurations at different places is as follows: environment variables > user settings in settings.py > default settings """ def __init__(self, user_settings=None): self.defaults = DEFAULTS if user_settings: self._user_settings = user_settings @property def user_settings(self): if not hasattr(self, '_user_settings'): self._user_settings = getattr(django_settings, XRAY_NAMESPACE, {}) return self._user_settings def __getattr__(self, attr): if attr not in self.defaults: raise AttributeError('Invalid setting: %s' % attr) if self.user_settings.get(attr, None) is not None: if attr in SUPPORTED_ENV_VARS: return os.getenv(attr, self.user_settings[attr]) else: return self.user_settings[attr] elif attr in SUPPORTED_ENV_VARS: return os.getenv(attr, self.defaults[attr]) else: return self.defaults[attr] settings = XRaySettings() def reload_settings(*args, **kwargs): """ Reload X-Ray user settings upon Django server hot restart """ global settings setting, value = kwargs['setting'], kwargs['value'] if setting == XRAY_NAMESPACE: settings = XRaySettings(value) setting_changed.connect(reload_settings) aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/db.py000066400000000000000000000021161321403223000230050ustar00rootroot00000000000000import logging import importlib from django.db import connections from aws_xray_sdk.ext.dbapi2 import XRayTracedCursor log = logging.getLogger(__name__) def patch_db(): for conn in connections.all(): module = importlib.import_module(conn.__module__) _patch_conn(getattr(module, conn.__class__.__name__)) def _patch_conn(conn): attr = '_xray_original_cursor' if hasattr(conn, attr): log.debug('django built-in db already patched') return setattr(conn, attr, conn.cursor) meta = {} if hasattr(conn, 'vendor'): meta['database_type'] = conn.vendor def cursor(self, *args, **kwargs): host = None user = None if hasattr(self, 'settings_dict'): settings = self.settings_dict host = settings.get('HOST', None) user = settings.get('USER', None) if host: meta['name'] = host if user: meta['user'] = user return XRayTracedCursor( self._xray_original_cursor(*args, **kwargs), meta) conn.cursor = cursor aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/middleware.py000066400000000000000000000054171321403223000245440ustar00rootroot00000000000000import logging import traceback from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http from aws_xray_sdk.ext.util import calculate_sampling_decision, \ calculate_segment_name, construct_xray_header log = logging.getLogger(__name__) # Django will rewrite some http request headers. USER_AGENT_KEY = 'HTTP_USER_AGENT' X_FORWARDED_KEY = 'HTTP_X_FORWARDED_FOR' REMOTE_ADDR_KEY = 'REMOTE_ADDR' HOST_KEY = 'HTTP_HOST' CONTENT_LENGTH_KEY = 'content-length' class XRayMiddleware(object): """ Middleware that wraps each incoming request to a segment. """ def __init__(self, get_response): self.get_response = get_response # hooks for django version >= 1.10 def __call__(self, request): sampling_decision = None meta = request.META xray_header = construct_xray_header(meta) # a segment name is required name = calculate_segment_name(meta.get(HOST_KEY), xray_recorder) sampling_decision = calculate_sampling_decision( trace_header=xray_header, recorder=xray_recorder, service_name=meta.get(HOST_KEY), method=request.method, path=request.path, ) segment = xray_recorder.begin_segment( name=name, traceid=xray_header.root, parent_id=xray_header.parent, sampling=sampling_decision, ) segment.put_http_meta(http.URL, request.build_absolute_uri()) segment.put_http_meta(http.METHOD, request.method) if meta.get(USER_AGENT_KEY): segment.put_http_meta(http.USER_AGENT, meta.get(USER_AGENT_KEY)) if meta.get(X_FORWARDED_KEY): # X_FORWARDED_FOR may come from untrusted source so we # need to set the flag to true as additional information segment.put_http_meta(http.CLIENT_IP, meta.get(X_FORWARDED_KEY)) segment.put_http_meta(http.X_FORWARDED_FOR, True) elif meta.get(REMOTE_ADDR_KEY): segment.put_http_meta(http.CLIENT_IP, meta.get(REMOTE_ADDR_KEY)) response = self.get_response(request) segment.put_http_meta(http.STATUS, response.status_code) if response.has_header(CONTENT_LENGTH_KEY): length = int(response[CONTENT_LENGTH_KEY]) segment.put_http_meta(http.CONTENT_LENGTH, length) xray_recorder.end_segment() return response def process_exception(self, request, exception): """ Add exception information and fault flag to the current segment. """ segment = xray_recorder.current_segment() segment.put_http_meta(http.STATUS, 500) stack = traceback.extract_stack(limit=xray_recorder._max_trace_back) segment.add_exception(exception, stack) aws-xray-sdk-python-0.95/aws_xray_sdk/ext/django/templates.py000066400000000000000000000013521321403223000244170ustar00rootroot00000000000000import logging from django.template import Template from aws_xray_sdk.core import xray_recorder log = logging.getLogger(__name__) def patch_template(): attr = '_xray_original_render' if getattr(Template, attr, None): log.debug("already patched") return setattr(Template, attr, Template.render) @xray_recorder.capture('template_render') def xray_render(self, context): template_name = self.name or getattr(context, 'template_name', None) if template_name: name = str(template_name) subsegment = xray_recorder.current_subsegment() subsegment.name = name return Template._xray_original_render(self, context) Template.render = xray_render aws-xray-sdk-python-0.95/aws_xray_sdk/ext/flask/000077500000000000000000000000001321403223000217045ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/flask/__init__.py000066400000000000000000000000001321403223000240030ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/flask/middleware.py000066400000000000000000000054361321403223000244030ustar00rootroot00000000000000import traceback import flask.templating from flask import request from aws_xray_sdk.core.models import http from aws_xray_sdk.ext.util import calculate_sampling_decision, \ calculate_segment_name, construct_xray_header class XRayMiddleware(object): def __init__(self, app, recorder): self.app = app self.app.logger.info("initializing xray middleware") self._recorder = recorder self.app.before_request(self._before_request) self.app.after_request(self._after_request) self.app.teardown_request(self._handle_exception) _patch_render(recorder) def _before_request(self): headers = request.headers xray_header = construct_xray_header(headers) req = request._get_current_object() name = calculate_segment_name(req.host, self._recorder) sampling_decision = calculate_sampling_decision( trace_header=xray_header, recorder=self._recorder, service_name=req.host, method=req.method, path=req.path, ) segment = self._recorder.begin_segment( name=name, traceid=xray_header.root, parent_id=xray_header.parent, sampling=sampling_decision, ) segment.put_http_meta(http.URL, req.base_url) segment.put_http_meta(http.METHOD, req.method) segment.put_http_meta(http.USER_AGENT, headers.get('User-Agent')) client_ip = headers.get('X-Forwarded-For') or headers.get('HTTP_X_FORWARDED_FOR') if client_ip: segment.put_http_meta(http.CLIENT_IP, client_ip) segment.put_http_meta(http.X_FORWARDED_FOR, True) else: segment.put_http_meta(http.CLIENT_IP, req.remote_addr) def _after_request(self, response): segment = self._recorder.current_segment() segment.put_http_meta(http.STATUS, response.status_code) cont_len = response.headers.get('Content-Length') if cont_len: segment.put_http_meta(http.CONTENT_LENGTH, int(cont_len)) self._recorder.end_segment() return response def _handle_exception(self, exception): if not exception: return segment = self._recorder.current_segment() segment.put_http_meta(http.STATUS, 500) stack = traceback.extract_stack(limit=self._recorder._max_trace_back) segment.add_exception(exception, stack) self._recorder.end_segment() def _patch_render(recorder): _render = flask.templating._render @recorder.capture('template_render') def _traced_render(template, context, app): if template.name: recorder.current_subsegment().name = template.name return _render(template, context, app) flask.templating._render = _traced_render aws-xray-sdk-python-0.95/aws_xray_sdk/ext/mysql/000077500000000000000000000000001321403223000217515ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/mysql/__init__.py000066400000000000000000000000571321403223000240640ustar00rootroot00000000000000from .patch import patch __all__ = ['patch'] aws-xray-sdk-python-0.95/aws_xray_sdk/ext/mysql/patch.py000066400000000000000000000017321321403223000234250ustar00rootroot00000000000000import wrapt import mysql.connector from aws_xray_sdk.ext.dbapi2 import XRayTracedConn MYSQL_ATTR = { '_host': 'name', '_user': 'user', } def patch(): wrapt.wrap_function_wrapper( 'mysql.connector', 'connect', _xray_traced_connect ) # patch alias if hasattr(mysql.connector, 'Connect'): mysql.connector.Connect = mysql.connector.connect def _xray_traced_connect(wrapped, instance, args, kwargs): conn = wrapped(*args, **kwargs) meta = {} for attr, key in MYSQL_ATTR.items(): if hasattr(conn, attr): meta[key] = getattr(conn, attr) if hasattr(conn, '_server_version'): version = sanitize_db_ver(getattr(conn, '_server_version')) if version: meta['database_version'] = version return XRayTracedConn(conn, meta) def sanitize_db_ver(raw): if not raw or not isinstance(raw, tuple): return raw return '.'.join(str(num) for num in raw) aws-xray-sdk-python-0.95/aws_xray_sdk/ext/requests/000077500000000000000000000000001321403223000224575ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/requests/__init__.py000066400000000000000000000000561321403223000245710ustar00rootroot00000000000000from .patch import patch __all__ = ['patch'] aws-xray-sdk-python-0.95/aws_xray_sdk/ext/requests/patch.py000066400000000000000000000026401321403223000241320ustar00rootroot00000000000000import wrapt from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.models import http from aws_xray_sdk.ext.util import inject_trace_header def patch(): wrapt.wrap_function_wrapper( 'requests', 'Session.request', _xray_traced_requests ) wrapt.wrap_function_wrapper( 'requests', 'Session.prepare_request', _inject_header ) def _xray_traced_requests(wrapped, instance, args, kwargs): url = kwargs.get('url') or args[1] return xray_recorder.record_subsegment( wrapped, instance, args, kwargs, name=url, namespace='remote', meta_processor=requests_processor, ) def _inject_header(wrapped, instance, args, kwargs): request = args[0] headers = getattr(request, 'headers', {}) inject_trace_header(headers, xray_recorder.current_subsegment()) setattr(request, 'headers', headers) return wrapped(*args, **kwargs) def requests_processor(wrapped, instance, args, kwargs, return_value, exception, subsegment, stack): method = kwargs.get('method') or args[0] url = kwargs.get('url') or args[1] subsegment.put_http_meta(http.METHOD, method) subsegment.put_http_meta(http.URL, url) if return_value is not None: subsegment.put_http_meta(http.STATUS, return_value.status_code) elif exception: subsegment.add_exception(exception, stack) aws-xray-sdk-python-0.95/aws_xray_sdk/ext/resources/000077500000000000000000000000001321403223000226165ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/resources/aws_para_whitelist.json000066400000000000000000000201121321403223000273760ustar00rootroot00000000000000{ "services": { "dynamodb": { "operations": { "BatchGetItem": { "request_descriptors": { "RequestItems": { "map": true, "get_keys": true, "rename_to": "table_names" } }, "response_parameters": [ "ConsumedCapacity" ] }, "BatchWriteItem": { "request_descriptors": { "RequestItems": { "map": true, "get_keys": true, "rename_to": "table_names" } }, "response_parameters": [ "ConsumedCapacity", "ItemCollectionMetrics" ] }, "CreateTable": { "request_parameters": [ "GlobalSecondaryIndexes", "LocalSecondaryIndexes", "ProvisionedThroughput", "TableName" ] }, "DeleteItem": { "request_parameters": [ "TableName" ], "response_parameters": [ "ConsumedCapacity", "ItemCollectionMetrics" ] }, "DeleteTable": { "request_parameters": [ "TableName" ] }, "DescribeTable": { "request_parameters": [ "TableName" ] }, "GetItem": { "request_parameters": [ "ConsistentRead", "ProjectionExpression", "TableName" ], "response_parameters": [ "ConsumedCapacity" ] }, "ListTables": { "request_parameters": [ "ExclusiveStartTableName", "Limit" ], "response_descriptors": { "TableNames": { "list": true, "get_count": true, "rename_to": "table_count" } } }, "PutItem": { "request_parameters": [ "TableName" ], "response_parameters": [ "ConsumedCapacity", "ItemCollectionMetrics" ] }, "Query": { "request_parameters": [ "AttributesToGet", "ConsistentRead", "IndexName", "Limit", "ProjectionExpression", "ScanIndexForward", "Select", "TableName" ], "response_parameters": [ "ConsumedCapacity" ] }, "Scan": { "request_parameters": [ "AttributesToGet", "ConsistentRead", "IndexName", "Limit", "ProjectionExpression", "Segment", "Select", "TableName", "TotalSegments" ], "response_parameters": [ "ConsumedCapacity", "Count", "ScannedCount" ] }, "UpdateItem": { "request_parameters": [ "TableName" ], "response_parameters": [ "ConsumedCapacity", "ItemCollectionMetrics" ] }, "UpdateTable": { "request_parameters": [ "AttributeDefinitions", "GlobalSecondaryIndexUpdates", "ProvisionedThroughput", "TableName" ] } } }, "sqs": { "operations": { "AddPermission": { "request_parameters": [ "Label", "QueueUrl" ] }, "ChangeMessageVisibility": { "request_parameters": [ "QueueUrl", "VisibilityTimeout" ] }, "ChangeMessageVisibilityBatch": { "request_parameters": [ "QueueUrl" ], "response_parameters": [ "Failed" ] }, "CreateQueue": { "request_parameters": [ "Attributes", "QueueName" ] }, "DeleteMessage": { "request_parameters": [ "QueueUrl" ] }, "DeleteMessageBatch": { "request_parameters": [ "QueueUrl" ], "response_parameters": [ "Failed" ] }, "DeleteQueue": { "request_parameters": [ "QueueUrl" ] }, "GetQueueAttributes": { "request_parameters": [ "QueueUrl" ], "response_parameters": [ "Attributes" ] }, "GetQueueUrl": { "request_parameters": [ "QueueName", "QueueOwnerAWSAccountId" ], "response_parameters": [ "QueueUrl" ] }, "ListDeadLetterSourceQueues": { "request_parameters": [ "QueueUrl" ], "response_parameters": [ "QueueUrls" ] }, "ListQueues": { "request_parameters": [ "QueueNamePrefix" ], "response_descriptors": { "QueueUrls": { "list": true, "get_count": true, "rename_to": "queue_count" } } }, "PurgeQueue": { "request_parameters": [ "QueueUrl" ] }, "ReceiveMessage": { "request_parameters": [ "AttributeNames", "MaxNumberOfMessages", "MessageAttributeNames", "QueueUrl", "VisibilityTimeout", "WaitTimeSeconds" ], "response_descriptors": { "Messages": { "list": true, "get_count": true, "rename_to": "message_count" } } }, "RemovePermission": { "request_parameters": [ "QueueUrl" ] }, "SendMessage": { "request_parameters": [ "DelaySeconds", "QueueUrl" ], "request_descriptors": { "MessageAttributes": { "map": true, "get_keys": true, "rename_to": "message_attribute_names" } }, "response_parameters": [ "MessageId" ] }, "SendMessageBatch": { "request_parameters": [ "QueueUrl" ], "request_descriptors": { "Entries": { "list": true, "get_count": true, "rename_to": "message_count" } }, "response_descriptors": { "Failed": { "list": true, "get_count": true, "rename_to": "failed_count" }, "Successful": { "list": true, "get_count": true, "rename_to": "successful_count" } } }, "SetQueueAttributes": { "request_parameters": [ "QueueUrl" ], "request_descriptors": { "Attributes": { "map": true, "get_keys": true, "rename_to": "attribute_names" } } } } }, "lambda": { "operations": { "Invoke": { "request_parameters": [ "FunctionName", "InvocationType", "LogType", "Qualifier" ], "response_parameters": [ "FunctionError", "StatusCode" ] }, "InvokeAsync": { "request_parameters": [ "FunctionName" ], "response_parameters": [ "Status" ] } } } } }aws-xray-sdk-python-0.95/aws_xray_sdk/ext/sqlite3/000077500000000000000000000000001321403223000221705ustar00rootroot00000000000000aws-xray-sdk-python-0.95/aws_xray_sdk/ext/sqlite3/__init__.py000066400000000000000000000000571321403223000243030ustar00rootroot00000000000000from .patch import patch __all__ = ['patch'] aws-xray-sdk-python-0.95/aws_xray_sdk/ext/sqlite3/patch.py000066400000000000000000000013041321403223000236370ustar00rootroot00000000000000import wrapt import sqlite3 from aws_xray_sdk.ext.dbapi2 import XRayTracedConn def patch(): wrapt.wrap_function_wrapper( 'sqlite3', 'connect', _xray_traced_connect ) def _xray_traced_connect(wrapped, instance, args, kwargs): conn = wrapped(*args, **kwargs) meta = {} meta['name'] = args[0] meta['database_version'] = sqlite3.sqlite_version traced_conn = XRayTracedSQLite(conn, meta) return traced_conn class XRayTracedSQLite(XRayTracedConn): def execute(self, *args, **kwargs): return self.cursor().execute(*args, **kwargs) def executemany(self, *args, **kwargs): return self.cursor().executemany(*args, **kwargs) aws-xray-sdk-python-0.95/aws_xray_sdk/ext/util.py000066400000000000000000000051271321403223000221400ustar00rootroot00000000000000import re from aws_xray_sdk.core.models.trace_header import TraceHeader from aws_xray_sdk.core.models import http first_cap_re = re.compile('(.)([A-Z][a-z]+)') all_cap_re = re.compile('([a-z0-9])([A-Z])') def inject_trace_header(headers, entity): """ Extract trace id, entity id and sampling decision from the input entity and inject these information to headers. :param dict headers: http headers to inject :param Entity entity: trace entity that the trace header value generated from. """ if not entity: return to_insert = TraceHeader( root=entity.trace_id, parent=entity.id, sampled=entity.sampled, ) value = to_insert.to_header_str() headers[http.XRAY_HEADER] = value def calculate_sampling_decision(trace_header, recorder, service_name, method, path): """ Return 1 if should sample and 0 if should not. The sampling decision coming from ``trace_header`` always has the highest precedence. If the ``trace_header`` doesn't contain sampling decision then it checks if sampling is enabled or not in the recorder. If not enbaled it returns 1. Otherwise it uses sampling rule to decide. """ if trace_header.sampled is not None: return trace_header.sampled elif not recorder.sampling: return 1 elif recorder.sampler.should_trace( service_name=service_name, method=method, path=path, ): return 1 else: return 0 def construct_xray_header(headers): """ Construct a ``TraceHeader`` object from dictionary headers of the incoming request. This method should always return a ``TraceHeader`` object regardless of tracing header's presence in the incoming request. """ header_str = headers.get(http.XRAY_HEADER) or headers.get(http.ALT_XRAY_HEADER) if header_str: return TraceHeader.from_header_str(header_str) else: return TraceHeader() def calculate_segment_name(host_name, recorder): """ Returns the segment name based on recorder configuration and input host name. This is a helper generally used in web framework middleware where a host name is available from incoming request's headers. """ if recorder.dynamic_naming: return recorder.dynamic_naming.get_name(host_name) else: return recorder.service def to_snake_case(name): """ Convert the input string to snake-cased string. """ s1 = first_cap_re.sub(r'\1_\2', name) # handle acronym words return all_cap_re.sub(r'\1_\2', s1).lower() aws-xray-sdk-python-0.95/docs/000077500000000000000000000000001321403223000162365ustar00rootroot00000000000000aws-xray-sdk-python-0.95/docs/.gitignore000066400000000000000000000000061321403223000202220ustar00rootroot00000000000000_buildaws-xray-sdk-python-0.95/docs/Makefile000066400000000000000000000011441321403223000176760ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx SPHINXPROJ = aws-xray-sdk SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.emitters.rst000066400000000000000000000007061321403223000242530ustar00rootroot00000000000000aws\_xray\_sdk\.core\.emitters package ====================================== Submodules ---------- aws\_xray\_sdk\.core\.emitters\.udp\_emitter module --------------------------------------------------- .. automodule:: aws_xray_sdk.core.emitters.udp_emitter :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core.emitters :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.exceptions.rst000066400000000000000000000007151321403223000246000ustar00rootroot00000000000000aws\_xray\_sdk\.core\.exceptions package ======================================== Submodules ---------- aws\_xray\_sdk\.core\.exceptions\.exceptions module --------------------------------------------------- .. automodule:: aws_xray_sdk.core.exceptions.exceptions :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core.exceptions :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.models.rst000066400000000000000000000044251321403223000237040ustar00rootroot00000000000000aws\_xray\_sdk\.core\.models package ==================================== Submodules ---------- aws\_xray\_sdk\.core\.models\.default\_dynamic\_naming module ------------------------------------------------------------- .. automodule:: aws_xray_sdk.core.models.default_dynamic_naming :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.dummy\_entities module ---------------------------------------------------- .. automodule:: aws_xray_sdk.core.models.dummy_entities :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.entity module ------------------------------------------- .. automodule:: aws_xray_sdk.core.models.entity :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.facade\_segment module ---------------------------------------------------- .. automodule:: aws_xray_sdk.core.models.facade_segment :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.http module ----------------------------------------- .. automodule:: aws_xray_sdk.core.models.http :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.segment module -------------------------------------------- .. automodule:: aws_xray_sdk.core.models.segment :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.subsegment module ----------------------------------------------- .. automodule:: aws_xray_sdk.core.models.subsegment :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.throwable module ---------------------------------------------- .. automodule:: aws_xray_sdk.core.models.throwable :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.trace\_header module -------------------------------------------------- .. automodule:: aws_xray_sdk.core.models.trace_header :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.models\.traceid module -------------------------------------------- .. automodule:: aws_xray_sdk.core.models.traceid :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core.models :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.plugins.rst000066400000000000000000000021171321403223000240760ustar00rootroot00000000000000aws\_xray\_sdk\.core\.plugins package ===================================== Submodules ---------- aws\_xray\_sdk\.core\.plugins\.ec2\_plugin module ------------------------------------------------- .. automodule:: aws_xray_sdk.core.plugins.ec2_plugin :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.plugins\.ecs\_plugin module ------------------------------------------------- .. automodule:: aws_xray_sdk.core.plugins.ecs_plugin :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.plugins\.elasticbeanstalk\_plugin module -------------------------------------------------------------- .. automodule:: aws_xray_sdk.core.plugins.elasticbeanstalk_plugin :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.plugins\.utils module ------------------------------------------- .. automodule:: aws_xray_sdk.core.plugins.utils :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core.plugins :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.rst000066400000000000000000000022061321403223000224150ustar00rootroot00000000000000aws\_xray\_sdk\.core package ============================ Subpackages ----------- .. toctree:: aws_xray_sdk.core.emitters aws_xray_sdk.core.exceptions aws_xray_sdk.core.models aws_xray_sdk.core.plugins aws_xray_sdk.core.sampling aws_xray_sdk.core.utils Submodules ---------- aws\_xray\_sdk\.core\.context module ------------------------------------ .. automodule:: aws_xray_sdk.core.context :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.lambda\_launcher module --------------------------------------------- .. automodule:: aws_xray_sdk.core.lambda_launcher :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.patcher module ------------------------------------ .. automodule:: aws_xray_sdk.core.patcher :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.recorder module ------------------------------------- .. automodule:: aws_xray_sdk.core.recorder :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.sampling.rst000066400000000000000000000016041321403223000242270ustar00rootroot00000000000000aws\_xray\_sdk\.core\.sampling package ====================================== Submodules ---------- aws\_xray\_sdk\.core\.sampling\.default\_sampler module ------------------------------------------------------- .. automodule:: aws_xray_sdk.core.sampling.default_sampler :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.sampling\.reservoir module ------------------------------------------------ .. automodule:: aws_xray_sdk.core.sampling.reservoir :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.sampling\.sampling\_rule module ----------------------------------------------------- .. automodule:: aws_xray_sdk.core.sampling.sampling_rule :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core.sampling :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.core.utils.rst000066400000000000000000000015271321403223000235610ustar00rootroot00000000000000aws\_xray\_sdk\.core\.utils package =================================== Submodules ---------- aws\_xray\_sdk\.core\.utils\.atomic\_counter module --------------------------------------------------- .. automodule:: aws_xray_sdk.core.utils.atomic_counter :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.utils\.compat module ------------------------------------------ .. automodule:: aws_xray_sdk.core.utils.compat :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.core\.utils\.search\_pattern module --------------------------------------------------- .. automodule:: aws_xray_sdk.core.utils.search_pattern :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.core.utils :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.aiobotocore.rst000066400000000000000000000006761321403223000246020ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.aiobotocore package ======================================== Submodules ---------- aws\_xray\_sdk\.ext\.aiobotocore\.patch module ---------------------------------------------- .. automodule:: aws_xray_sdk.ext.aiobotocore.patch :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.aiobotocore :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.aiohttp.rst000066400000000000000000000006651321403223000237430ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.aiohttp package ==================================== Submodules ---------- aws\_xray\_sdk\.ext\.aiohttp\.middleware module ----------------------------------------------- .. automodule:: aws_xray_sdk.ext.aiohttp.middleware :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.aiohttp :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.botocore.rst000066400000000000000000000011731321403223000241020ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.botocore package ===================================== Submodules ---------- aws\_xray\_sdk\.ext\.botocore\.patch module ------------------------------------------- .. automodule:: aws_xray_sdk.ext.botocore.patch :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.ext\.botocore\.resources module ----------------------------------------------- .. automodule:: aws_xray_sdk.ext.botocore.resources :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.botocore :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.django.rst000066400000000000000000000022401321403223000235240ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.django package =================================== Submodules ---------- aws\_xray\_sdk\.ext\.django\.apps module ---------------------------------------- .. automodule:: aws_xray_sdk.ext.django.apps :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.ext\.django\.conf module ---------------------------------------- .. automodule:: aws_xray_sdk.ext.django.conf :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.ext\.django\.db module -------------------------------------- .. automodule:: aws_xray_sdk.ext.django.db :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.ext\.django\.middleware module ---------------------------------------------- .. automodule:: aws_xray_sdk.ext.django.middleware :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.ext\.django\.templates module --------------------------------------------- .. automodule:: aws_xray_sdk.ext.django.templates :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.django :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.flask.rst000066400000000000000000000006511321403223000233660ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.flask package ================================== Submodules ---------- aws\_xray\_sdk\.ext\.flask\.middleware module --------------------------------------------- .. automodule:: aws_xray_sdk.ext.flask.middleware :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.flask :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.mysql.rst000066400000000000000000000006321321403223000234320ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.mysql package ================================== Submodules ---------- aws\_xray\_sdk\.ext\.mysql\.patch module ---------------------------------------- .. automodule:: aws_xray_sdk.ext.mysql.patch :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.mysql :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.requests.rst000066400000000000000000000006541321403223000241440ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.requests package ===================================== Submodules ---------- aws\_xray\_sdk\.ext\.requests\.patch module ------------------------------------------- .. automodule:: aws_xray_sdk.ext.requests.patch :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.requests :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.rst000066400000000000000000000013531321403223000222670ustar00rootroot00000000000000aws\_xray\_sdk\.ext package =========================== Subpackages ----------- .. toctree:: aws_xray_sdk.ext.botocore aws_xray_sdk.ext.django aws_xray_sdk.ext.flask aws_xray_sdk.ext.mysql aws_xray_sdk.ext.requests aws_xray_sdk.ext.sqlite3 Submodules ---------- aws\_xray\_sdk\.ext\.dbapi2 module ---------------------------------- .. automodule:: aws_xray_sdk.ext.dbapi2 :members: :undoc-members: :show-inheritance: aws\_xray\_sdk\.ext\.util module -------------------------------- .. automodule:: aws_xray_sdk.ext.util :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.ext.sqlite3.rst000066400000000000000000000006461321403223000236560ustar00rootroot00000000000000aws\_xray\_sdk\.ext\.sqlite3 package ==================================== Submodules ---------- aws\_xray\_sdk\.ext\.sqlite3\.patch module ------------------------------------------ .. automodule:: aws_xray_sdk.ext.sqlite3.patch :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: aws_xray_sdk.ext.sqlite3 :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/aws_xray_sdk.rst000066400000000000000000000003711321403223000214670ustar00rootroot00000000000000aws\_xray\_sdk package ====================== Subpackages ----------- .. toctree:: aws_xray_sdk.core aws_xray_sdk.ext Module contents --------------- .. automodule:: aws_xray_sdk :members: :undoc-members: :show-inheritance: aws-xray-sdk-python-0.95/docs/basic.rst000066400000000000000000000066231321403223000200600ustar00rootroot00000000000000.. _basic: Basic Usage =========== The SDK provides a global recorder, ``xray_recorder``, to generate segments and subsegments. Manually create segment/subsegment ---------------------------------- If you're using a web framework or library that is not supported, or you want to define your own structure on segments/subsegments, you can manually create segments and subsegments by using code like the following:: from aws_xray_sdk.core import xray_recorder xray_recorder.begin_segment('name') # your code here xray_recorder.begin_subsegment('name') # some code block you want to record xray_recorder.end_subsegment() xray_recorder.end_segment() The ``xray_recorder`` keeps one segment per thread. Therefore, in manual mode, call ``xray_recorder.end_segment()`` before creating a new segment, otherwise the new segment overwrites the existing one. To trace a particular code block inside a segment, use a subsegment. If you open a new subsegment while there is already an open subsegment, the new subsegment becomes the child of the existing subsegment. Decorator for function auto-capture ----------------------------------- A decorator is provided to easily capture basic information as a subsegment on user defined functions. You can use the decorator like the following:: @xray_recorder.capture('name') def my_func(): #do something ``xray_recorder`` generates a subsegment for the decorated function, where the name is optional. If the name argument is not provided, the function name is used as the subsegment name. If the function is called without an open segment in the context storage, the subsegment is discarded. Currently the decorator only works with synchronous functions. Set annotation or metadata -------------------------- You can add annotations and metadata to an active segment/subsegment. Annotations are simple key-value pairs that are indexed for use with `filter expressions `_. Use annotations to record data that you want to use to group traces in the console, or when calling the GetTraceSummaries API. Metadata are key-value pairs with values of any type, including objects and lists, but that are not indexed. Use metadata to record data you want to store in the trace but don't need to use for searching traces. You can add annotations/metadata like the following:: from aws_xray_sdk.core import xray_recorder segment = xray_recorder.current_segment() # value can be string, number or bool segment.put_annotation('key', value) # namespace and key must be string and value is an object # that can be serialized to json segment.put_metadata('key', json, 'namespace') The ``current_segment`` and ``current_subsegment`` functions get the current open segment or subsegment, respectively, from context storage. Put these calls between segment or subsegment begin and end statements. AWS Lambda Integration ---------------------- To integrate with Lambda you must first enable active tracing on a Lambda function. See http://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html#using-x-ray for details. In your Lambda function, you can only begin and end a subsegment. The Lambda service emits a segment as the root. This segment cannot be mutated. Instrument the SDK as you would in any Python script. Subsegments generated outside of the Lambda handler are discarded. aws-xray-sdk-python-0.95/docs/changes.rst000066400000000000000000000000531321403223000203760ustar00rootroot00000000000000.. _changes: .. include:: ../CHANGELOG.rstaws-xray-sdk-python-0.95/docs/conf.py000066400000000000000000000125651321403223000175460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # aws-xray-sdk documentation build configuration file, created by # sphinx-quickstart on Wed Aug 2 15:33:56 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.append(os.path.join(os.path.dirname(__name__), '..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'aws-xray-sdk' copyright = u'2017, Amazon Web Services' author = u'Amazon Web Services' # 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 short X.Y version. version = u'0.95' # The full version, including alpha/beta/rc tags. release = u'0.95' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { '**': [ 'about.html', 'navigation.html', 'relations.html', # needs 'show_related': True theme option to display 'searchbox.html', 'donate.html', ] } # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'aws-xray-sdkdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'aws-xray-sdk.tex', u'aws-xray-sdk Documentation', u'Amazon Web Services', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'aws-xray-sdk', u'aws-xray-sdk Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'aws-xray-sdk', u'aws-xray-sdk Documentation', author, 'aws-xray-sdk', 'One line description of project.', 'Miscellaneous'), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/': None} aws-xray-sdk-python-0.95/docs/configurations.rst000066400000000000000000000150571321403223000220320ustar00rootroot00000000000000.. _configurations: Configure Global Recorder ========================= Sampling -------- Sampling is enabled by default. Whenever the global recorder creates a segment, it decides whether to sample this segment. If it does not sample this segment, it is discarded and not sent to the X-Ray daemon. To turn off sampling, use code like the following:: from aws_xray_sdk.core import xray_recorder xray_recorder.configure(sampling=False) You can also configure the sampling rules:: xray_recorder.configure(sampling_rules=your_rules) The input can either be an absolute path to your sampling rule *.json* file or a dictionary. The following code is an example of a rule configuration:: { "version": 1, "rules": [ { "description": "Player moves.", "service_name": "*", "http_method": "*", "url_path": "/api/move/*", "fixed_target": 0, "rate": 0.05 } ], "default": { "fixed_target": 1, "rate": 0.1 } } This example defines one custom rule and a default rule. The custom rule applies a five-percent sampling rate with no minimum number of requests to trace for paths under */api/move/*. The default rule traces the first request each second and 10 percent of additional requests. The SDK applies custom rules in the order in which they are defined. If a request matches multiple custom rules, the SDK applies only the first rule. You can use wildcard character "*" and "?" in service_name, http_method and url_path. "*" represents any combination of characters. "?" represents a single character. Note that sampling configurations have no effect if the application runs in AWS Lambda. Plugins ------- The plugin adds extra metadata for each segment if the app is running on that environment. The SDK provides three plugins: * Amazon EC2 – EC2Plugin adds the instance ID and Availability Zone. * Elastic Beanstalk – ElasticBeanstalkPlugin adds the environment name, version label, and deployment ID. * Amazon ECS – ECSPlugin adds the container host name To use plugins, use code like the following:: # a tuple of strings plugins = ('elasticbeanstalk_plugin', 'ec2_plugin', 'ecs_plugin') # alternatively you can use plugins = ('ElasticBeanstalkPlugin', 'EC2Plugin', 'ECSPlugin') xray_recorder.configure(plugins=plugins) Order matters in the tuple and the origin of the segment is set from the last plugin. Therefore, in the previous example, if the program runs on ECS, the segment origin is 'AWS::ECS::CONTAINER'. Plugins must be configured before patching any third party libraries to avoid unexpected behavior. Plugins are employed after they are specified. Context Missing Strategy ------------------------ Defines the recorder behavior when your instrumented code attempts to record data when no segment is open. Configure like the following:: xray_recorder.configure(context_missing='Your Strategy Name Here') Supported strategies are: * RUNTIME_ERROR: throw an SegmentNotFoundException * LOG_ERROR: log an error and continue Segment Dynamic Naming ---------------------- For a web application you might want to name the segment using host names. You can pass in a pattern with wildcard character "*" and "?". "*" represents any combination of characters. "?" represents a single character. If the host name from incoming request's header matches the pattern, the host name will be used as the segment name, otherwise it uses fallback name defined in ``AWS_XRAY_TRACING_NAME``. To configure dynamic naming, use code like the following:: xray_recorder.configure(dynamic_naming='*.example.com') Environment Variables --------------------- There are three supported environment variables to configure the global recorder: * AWS_XRAY_CONTEXT_MISSING: configure context missing strategy * AWS_XRAY_TRACING_NAME: default segment name * AWS_XRAY_DAEMON_ADDRESS: where the recorder sends data to over UDP Environment variables has higher precedence over ``xray_recorder.configure()`` Logging ------- The SDK uses Python's built-in ``logging`` module to perform logging. You can configure the SDK logging just like how you configure other python libraries. An example of set the SDK log level is like the following:: logging.basicConfig(level='DEBUG') logging.getLogger('aws_xray_sdk').setLevel(logging.WARNING) Context Storage --------------- The global recorder uses threadlocal to store active segments/subsegments. You can override the default context class to implement your own context storage:: from aws_xray_sdk.core.context import Context class MyOwnContext(Context): def put_segment(self, segment): # store the segment created by ``xray_recorder`` to the context. pass def end_segment(self, end_time=None): # end the segment in the current context. pass def put_subsegment(self, subsegment): # store the subsegment created by ``xray_recorder`` to the context. pass def end_subsegment(self, end_time=None): # end the subsegment in the current context. pass def get_trace_entity(self): # get the current active trace entity(segment or subsegment). pass def set_trace_entity(self, trace_entity): # manually inject a trace entity to the context storage. pass def clear_trace_entities(self): # clean up context storage. pass def handle_context_missing(self): # behavior on no trace entity to access or mutate. pass The function ``current_segment`` and ``current_subsegment`` on recorder level uses ``context.get_trace_entity()`` and dynamically return the expected type by using internal references inside segment/subsegment objects. Then you can pass your own context:: my_context=MyOwnContext() xray_recorder.configure(context=my_context) Emitter ------- The default emitter uses non-blocking socket to send data to the X-Ray daemon. It doesn't retry on IOError. To override the default emitter:: from aws_xray_sdk.core.emitters.udp_emitter import UDPEmitter class MyOwnEmitter(UDPEmitter): def send_entity(self, entity): # send the input segment/subsegment to the X-Ray daemon. # Return True on success and False on failure. pass def set_daemon_address(self, address): # parse input full address like 127.0.0.1:8000 to ip and port and # store them to the local emitter properties. pass Then you can pass your own emitter:: my_emitter = MyOwnEmitter() xray_recorder.configure(emitter=my_emitter) aws-xray-sdk-python-0.95/docs/frameworks.rst000066400000000000000000000110531321403223000211500ustar00rootroot00000000000000.. _frameworks: Django ====== Configure X-Ray Recorder ------------------------ Make sure you add ``XRayMiddleWare`` as the first entry in your Django *settings.py* file, as shown in the following example:: MIDDLEWARE = [ 'aws_xray_sdk.ext.django.middleware.XRayMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ] The incoming requests to the Django app are then automatically recorded as a segment. To get the current segment and add annotations or metadata as needed, use the following statement in your application code when processing request:: segment = xray_recorder.current_segement() For more configurations in your Django ``settings.py`` file, add the following line:: INSTALLED_APPS = [ 'django.contrib.admin', ... 'django.contrib.sessions', 'aws_xray_sdk.ext.django', ] You can configure the X-Ray recorder in a Django app under the 'XRAY_RECORDER' namespace. The default values are as follows:: XRAY_RECORDER = { 'AWS_XRAY_DAEMON_ADDRESS': '127.0.0.1:2000', 'AUTO_INSTRUMENT': True, # If turned on built-in database queries and template rendering will be recorded as subsegments 'AWS_XRAY_CONTEXT_MISSING': 'RUNTIME_ERROR', 'PLUGINS': (), 'SAMPLING': True, 'SAMPLING_RULES': None, 'AWS_XRAY_TRACING_NAME': None, # the segment name for segments generated from incoming requests 'DYNAMIC_NAMING': None, # defines a pattern that host names should match } Environment variables have higher precedence over user settings. If neither is set, the defaults values shown previously are used. 'AWS_XRAY_TRACING_NAME' is required unless specified as an environment variable. All other keys are optional. For further information on individual settings, see the :ref:`Configure Global Recorder ` section. Local Development ----------------- When doing Django app local development, if you configured Django built-in database with ``AUTO_INSTRUMENT`` turned-on, the command ``manage.py runserver`` may fail if ``AWS_XRAY_CONTEXT_MISSING`` is set to ``RUNTIME_ERROR``. This is because the command ``runserver`` performs migrations check which will generate a subsegment, the ``xray_recorder`` will raise an error since there is no active segment. One solution is to set ``AWS_XRAY_CONTEXT_MISSING`` to ``LOG_ERROR`` so it only emits a error message on server startup. Alternatively if you have defined your own ``ready()`` function for code execution at startup you can manually create a segment as a placeholder. By Django official guide it's recommanded to deploy Django to other servers in production so this particular issue normally doesn't exist in production. Flask ===== To generate segment based on incoming requests, you need to instantiate the X-Ray middleware for flask:: from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.flask.middleware import XRayMiddleware app = Flask(__name__) xray_recorder.configure(service='my_app_name') XRayMiddleware(app, xray_recorder) Flask built-in template rendering will be wrapped into subsegments. You can configure the recorder, see :ref:`Configure Global Recorder ` for more details. aiohttp Server ============== For X-Ray to create a segment based on an incoming request, you need register some middleware with aiohttp. As aiohttp is an asyncronous framework, X-Ray will also need to be configured with an ``AsyncContext`` compared to the default threaded version.:: import asyncio from aiohttp import web from aws_xray_sdk.ext.aiohttp.middleware import middleware from aws_xray_sdk.core.async_context import AsyncContext from aws_xray_sdk.core import xray_recorder # Configure X-Ray to use AsyncContext xray_recorder.configure(service='service_name', context=AsyncContext()) async def handler(request): return web.Response(body='Hello World') loop = asyncio.get_event_loop() # Use X-Ray SDK middleware, its crucial the X-Ray middleware comes first app = web.Application(middlewares=[middleware]) app.router.add_get("/", handler) web.run_app(app) There are two things to note from the example above. Firstly a middleware corountine from aws-xray-sdk is provided during the creation of an aiohttp server app. Lastly the ``xray_recorder`` has also been configured with a name and an ``AsyncContext``. See :ref:`Configure Global Recorder ` for more information about configuring the ``xray_recorder``. aws-xray-sdk-python-0.95/docs/index.rst000066400000000000000000000032011321403223000200730ustar00rootroot00000000000000.. aws-xray-sdk documentation master file, created by sphinx-quickstart on Wed Aug 2 15:33:56 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to the AWS X-Ray SDK for Python! ======================================== This project is open sourced in Github. Please see: https://github.com/aws/aws-xray-sdk-python. The AWS X-Ray service accepts application information in the form of trace segments. A trace segment represents the work done by a single machine as a part of the entire task or request. A set of trace segments which share the same trace ID form a trace. A trace represents a full unit of work completed for a single task or request. Learn more about AWS X-Ray service: https://aws.amazon.com/xray/. The AWS X-Ray SDK for Python (the SDK) enables Python developers to record and emit information from within their applications to the AWS X-Ray service. You can get started in minutes using ``pip`` or by downloading a zip file. Currently supported web frameworks and libraries: * django >=1.10 * flask * boto3 * botocore * requests * sqlite3 * mysql-connector You must have the X-Ray daemon running to use the SDK. For information about installing and configuring the daemon see: http://docs.aws.amazon.com/xray/latest/devguide/xray-daemon.html. Contents: .. toctree:: :maxdepth: 2 Basic Usage Recorder Configurations Third Party Libraries Working with Web Frameworks Change Log License Indices and tables ================== * :ref:`modindex` * :ref:`search` aws-xray-sdk-python-0.95/docs/license.rst000066400000000000000000000001711321403223000204110ustar00rootroot00000000000000.. _license: License ======= Please see Github page on https://github.com/aws/aws-xray-sdk-python/blob/master/LICENSE. aws-xray-sdk-python-0.95/docs/make.bat000066400000000000000000000166351321403223000176560ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation pushd %~dp0 if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=python -msphinx ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_elements.papersize=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_elements.papersize=%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 an 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. epub3 to make an epub3 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. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled echo. dummy to check syntax errors of document sources goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available %SPHINXBUILD% 1>NUL 2>NUL if errorlevel 1 ( echo. echo.The Sphinx module was not found. Make sure you have Sphinx installed, echo.then set the SPHINXBUILD environment variable to point to the full echo.path of the 'sphinx-build' executable. Alternatively you may add the echo.Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) 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\aws_xray_sdk.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\aws_xray_sdk.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" == "epub3" ( %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 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" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF 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 ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) if "%1" == "dummy" ( %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy if errorlevel 1 exit /b 1 echo. echo.Build finished. Dummy builder generates no files. goto end ) :end popd aws-xray-sdk-python-0.95/docs/modules.rst000066400000000000000000000001111321403223000204310ustar00rootroot00000000000000aws_xray_sdk ============ .. toctree:: :maxdepth: 4 aws_xray_sdk aws-xray-sdk-python-0.95/docs/thirdparty.rst000066400000000000000000000037321321403223000211670ustar00rootroot00000000000000.. _thirdparty: Third Party Library Support =========================== Patching Supported Libraries ---------------------------- The SDK supports aioboto3, aiobotocore, boto3, botocore, requests, sqlite3 and mysql-connector. To patch, use code like the following in the main app:: from aws_xray_sdk.core import patch_all patch_all() ``patch_all`` ignores any libraries that are not installed. To patch specific modules:: from aws_xray_sdk.core import patch i_want_to_patch = ('botocore') # a tuple that contains the libs you want to patch patch(i_want_to_patch) The following modules are availble to patch:: SUPPORTED_MODULES = ( 'aioboto3', 'aiobotocore', 'boto3', 'botocore', 'requests', 'sqlite3', 'mysql', ) Patching boto3 and botocore are equivalent since boto3 depends on botocore Patching mysql ---------------------------- For mysql, only the mysql-connector module is supported and you have to use code like the following to generate a subsegment for an SQL query:: def call_mysql(): conn = mysql.connector.connect( host='host', port='some_port', user='some_user', password='your_password', database='your_db_name' ) conn.cursor().execute('SHOW TABLES') Patching aioboto3 and aiobotocore --------------------------------- On top of patching aioboto3 or aiobotocore, the xray_recorder also needs to be configured to use the ``AsyncContext``. The following snippet shows how to set up the X-Ray SDK with an Async Context, bear in mind this requires Python 3.5+:: from aws_xray_sdk.core.async_context import AsyncContext from aws_xray_sdk.core import xray_recorder # Configure X-Ray to use AsyncContext xray_recorder.configure(service='service_name', context=AsyncContext()) See :ref:`Configure Global Recorder ` for more information about configuring the ``xray_recorder``.aws-xray-sdk-python-0.95/images/000077500000000000000000000000001321403223000165535ustar00rootroot00000000000000aws-xray-sdk-python-0.95/images/example_servicemap.png000066400000000000000000004750721321403223000231510ustar00rootroot00000000000000‰PNG  IHDRIW‘ò¸é ¸iCCPICC ProfileH‰•–XSYÇï{éBoÒ;H =A:Ø PBL*6DG`,¨ˆ€¢èPDÁ±2¨ˆ(ìdPQ×Á‚¨¨ì–°³ûíî·ç}çÝßwÞ}çžsß»ß÷€üK H‡åÈàg Ãý½è±qñtÜ 5@æf±EFXX0@lnü«}ÐôxÛ|:׿?ÿ¯&ÏI±€ÂNäˆØŸFü:[ Ì•‹ÄõVe ¦ùŠB¤@„[§9e–{¦9q–%3s"ýþžÌb S O¯EÏf§ yÈt„­øáéuÝÙ\á/ÈÈÈœæv„ÿ)OÊ_r&Js²X)RžíeÆð><‘ µæÿÜŽÿméâ¹5ô's…áÈH›Þ·´Ì )ó…Î133†¹â€¨9f‹¼ãç˜Ãò šcqZcŽYÂùws¸‘1s,Ì —æç§/ –æObJ9Iä1ÇÉÛbÝÆÊÚ€éó8û¹ßß›9g ?ËDút} ¬<K4 y rùó1”«t”°ÅÂìÙzú†D *ÐBþ'cäÄÛà <‘ŠA(ˆq`9`.ÈB° ¬›@(;ÀPªÀaPŽƒ“ ´ƒ‹à ¸n»à!€ð Œ`‚ D¨*¤ @f ä¹C¾P0ÅA P ćÄÐ:h3T•@åÐ!¨ú: ]„®A}Ð}h…ÞA_`L†aMض„`GÂËàx%œçÃÛà2¸>7Ãáð]X¿‚ÇQEBÑP:(s”ÊŠŠG%£„¨ ¨BT)ªÕˆjCu£n£$¨×¨Ïh,šŠ¦£ÍÑ®ètš^‰Þ€.F—£ëÐÍè.ômôz ýCÁh`Ì0.&&“‚Y…)À”bj0g0—1w1#˜X,–†5Â:b°qØTìZl1v?¶ ÛíÃcÇq8œ*Î ç† Å±pY¸Ü>Ü1Ü\?n÷ OÂkãmð~øx<Ÿ‡/ÅÅŸÇ÷ãŸã' r‚ !”À!¬!l'!´nF“Dy¢ÑIL%n"–‰—‰ˆïI$’.É™´˜Ä#å’ÊH'HWIC¤Ïd²)Ù›¼”,&o#×’;È÷Éï)Š!Å“OÉ¢l£ÔS.QžP>ÉPe,d˜2™22Í2ý2od ²² Ùå²9²¥²§doʾ–#ÈÊy˱ä6ÈUÈ•”—§Ê[ˇÊgÈË•¿&ÿB§`¨à«ÀQÈW8¬pIa˜Š¢êQ½©lêfêêeêˆ"VÑH‘©˜ªX¤x\±WqLIAÉN)ZiµR…Ò9% E3¤1ié´í´“´ÚeMe†r’òVåFå~å uO•$•B•&•»*_T骾ªiª;U[T«¡ÕLÕ«­R; vYíµº¢º«:[½Pý¤ú XÃT#\c­ÆaqM-MMæ>ÍKš¯µhZžZ©Z»µÎkjSµÝµyÚ»µ/h¿¤+Ñôtz½‹>¦£¡ #Ö9¤Ó«3©k¤¥›§Û¤ûX¨ç¤—¬·[¯SoL_[?D~ƒþ‚“×`¯A·Á„¡‘aŒáÃÃF*FL£££GÆcã•ÆÕÆwL°&N&i&ûMn™Â¦ö¦\Ó Ó›f°™ƒÏl¿YßÌçüÕ ÍÉæ ólóó! šE°EžE‹ÅK}ËxË–Ý–ß­ì­Ò­ŽX=´V°´Î³n³~gcjö©°¹cK±õ³ÝhÛjûÖÎÌ.Éî€Ý={ª}ˆýûNûoŽB‡F‡QG}ÇÇJÇA'E§0§b§«Îg/çÎíΟ]\²\NºüéjîšæzÔõÅB£…I ,vÓuc¹r“¸ÓÝܺK(ŸBŸ^_ß(ßrß'~º~)~ ~cþöþký;0A;™šL6³ž9è¸>°+ˆTô4Ø4X܇†ì y´È`QK(e†î }f¶2ì×ÅØÅa‹+? ·_ÞAXq4âc¤WäöȇQÆQâ¨ÎhÙè¥ÑõÑ1>1%1’XËØõ±7âÔâxq­ñ¸øèøšøñ%¾Kö,Yj¿´`éÀ2£e«—][®¶<}ù¹²+X+N%`bŽ&|e…²ªYã‰ÌÄÊÄ1¶7{/ûÇ“³›3šä–T’ô<Ù-¹$ùEŠ[Ê®”Q®·”ûšçÍ+ç½M H­JH M«M›JIoÊÀg$dœå+ðÓø]™Z™«3ûf‚d¥ËÊ=+Ç„AÂ$Z&jÍRD„OØXüƒx(Û=»"ûÓªèU§V˯æ¯îYcºfëšç9~9?¯E¯e¯í\§³nÓº¡õŒõ‡6@7tnÔÛ˜¿q$×?·nqSÚ¦ßò¬òJò>lŽÙÜ–¯™Ÿ›?üƒÿ 2‚Á-®[ª~DÿÈû±w«íÖ}[¿r ¯Y•}-f_ÿÉú§²Ÿ¦¶%oëÝî°ýÀìþŽ;ëJäKrJ†w…ìjÞMß]¸ûÞ{®•Ú•Ví%îï•”—µîÓß·cß×rnùÝ ¯Š¦JÊ­•û9ûûxh¬Ò¬*ªúrwðÞ!ÿCÍÕ†Õ¥‡±‡³?;}¤ûg§ŸëkÔjŠj¾Õòk%uáu]õŽõõG5Žno€Ä £Ç–»uÜçxk£yã¡&ZSÑ pB|âå/ ¿ œ :ÙyÊéTãiƒÓ•g¨g ›¡æ5Íc-ÜIk\kßÙÀ³m®mg~µøµ¶]§½âœÒ¹íç‰çóÏO]ȹ0Þ!èx}1åâpçŠÎ‡—b/ÝéZÜÕ{9èòÕ+~W.u3º/\u»Ú~ÍåÚÙëN×[n8Ühî±ï9ó›ýogzz›o:Þl½å|«­oaßù~þ‹·}n_¹Ã¼sã}Q÷—JîqŸ~ÿíƒì“sa>–{\úDãIõï&¿7I$ç†|†zžF<}8Ì~õ‡è¯#ùÏ(ÏJŸk?¯aó¢}ÔoôÖË%/G^ ^M¾.ø›üß*ß¿9ý§çŸ=c±c#o…o§Þ¿W}_ûÁîCçxØø“'' ?©~ªûìô¹ûKÌ—ç“«¾â¾–}3ùÖö=èû£©Œ©)KÈš‘(ÄáädÞÕ":!ê-ˆKfõòŒA³†ÀâYM=cTw êñPÄ«<0D”\ÂŽô°­­Ôÿa¢d[›Ù\¤Dš”NM½GT΀oƒSS“-SSßjb :æã¬NŸ6¹c¬± ð îÿž› þÅþ\• ly´ËòžiTXtXML:com.adobe.xmp 1609 855 éÄt?@IDATxì½ \”×™÷ÿCaPPDÐ1 DAT¢ˆ V¢¨X1›¸1Ù¤>ÙMÚm›Mv7}žm·/Ï6Ùí¶M³»M“¾¤‰5þMþ1&¦$¨Äj4A%¢¨D4 (ò" Œ ÏuîyážafxÉK‰þÎçÃÌ}ßçí:ßsî3z]çœ+¨WtÁëVÃK           ¸z\MÑGô×®x~“ ÀÕB ((š‘DE\†ýõÕÒP¶ƒH€H€H€H€H€H€H€H€H€H€H€ô4#‰Ë(¢¾÷ï߯ç5 \µ‚zzzÄ>Ò ùÆ¥KWmCÙ0           ÐðxCI@ý)$Àyã›@À1äeøcªW,GEÙ‰2œ;wN|˜”ˆÔ™3‘23E«HùH±X.#"Âä]ñ î‡/Û ÿtI†ÄØÛHòéªvçþä“O þª««a4™Ò¤Iî4¼          Ï’€/Éüù󑜜 e$QŠãÆÆFìÞ½mmm¸|ùòÐ %þîü1JþŠàsð 0Ì1ÕÐЀg~ù Ž?îQÜÞ½{µûo¼ÿ¯‡ñëç{~ðƒx¤ÔÍ0eTÙ_p"$½½ŸîEVÖ×7¢¤¤Ä£|ðv¾_^^ž¶…mègýyÇ         èGàСC;H233‘––æN„˜˜¬^½[·nE{{»f(QGsÝsÏ=îtþ.éOí½Ã;¥§­ú8N5ؤÊ̘7ã<4¶þ$ùüž[Úšaí6b„ðϬ’î¶j?ÕŒ½s“¯í…ôÆ7ðáŒ)åçÿüËÿÁ…  Æûä)S’âØ9òqYêêêpðàACí$™={6†SÏpòx·ïóº c%ƒç+7Äm(úF¨mj?ÿùϵsþ|µkdÆŒ¸råŠæéÒ¥KšUVuÒúõëõY_ÛP´÷,öHg·wÊ;u2S&x<÷¾±Ë:„„xåöNõ—¿··žFá0wÙBÄ„üåå¹*%°Û`“›ϡ䳩úqóEõ¾NŸB}ÎÛ«ãýò.Ü”“‰ =žïÝnÇè±±˜97 1ÆÁ Ò^sÃù¦vtŒˆOÁM·¤cÂg2ÆÛq¸ðCŒž³)¡Uƒzì­'%]Ó Þ3} %_ :ãÂx™0Ö1°5ŸÄîË5|9ÚqhLJ›»³¾° ƆÊC{ñÑé —²ËÓã¡ù ãì¨>ôgT…ÍAö¬˜þåÚ›QTpKdÜœó7ž}þyÏÉ}ãwà> MúVzžÓ «û]X}e¼&     ‘E@í"QNÚ]A-Ô6›ÍÚ­R;v “'OFjjªæÀýúë¯w/önmmÅùóç1EÊCýi†g$©ùó/ñ“7¥Ú‰ø×MÏcž§Æ6 8ƒŠì®Æoþå8t%ÿä1$²}´ãø L«ñÒ×cÜ *8Q×¹üðßò%a^Ú¶á3(·üæiüáP nÿçê¤@X¾/4E€1ä-ÇPÇ”Ú)õÌ/~¡HÂÂÂðíoÿ=nºé&b8ˆ§ŸþwÍ@âŠj=*ßpò¸êûÜ¿‡ÀXÉâñÊõ޶|êü¾šš lذóæÍs—¥:GYf•EvÿþýZÜœ9sÜñ.lN¡´¢ ƨh˜tÊm»ÕޱׇÊ Øj°åÅ|Lʽ9ñ#ûEi­<‚ªš`Ì×)N7ޱC#`lj7_Ä~ã"<œç°œúÍï5n¾¾ñªÓ¯lŸcD}ùÔT™q‹¼g¶:Ï÷Îní@‹84+/=‚¬»îÅ,½†ßL•û¶bWY“Ì2QHNƒÑ(-/FMyò\ó§ë–z‘¹aF²­5ƒ{ZÏž’÷ ¿g^ýáÈ׹ß"5JãšXÜâÕö¦Ó‡E® 4+¶ž-“t†å÷îk#J¤ÿ’SÕY‘^•{§ýŒî­5°«¤¦ØDE 0WFu:б¢Jê=Ÿ–ê³T{ëy”ÊoU–t‚Ç»íÝçŸ÷œ¬¿÷Ià6õ5tóœ~L I–¾ÚxE$@$@$@$@$@#@}}=ºººÜb©UòÛ¶mƒÒCž8qB‹knnÖŒ$*ÑøñãÝiÕ…r0‘$þ´Û>¼SzBB£¥ve$ Ç()£û³võ ‹ÍŸ<‹Qkw|—±¶g«ËðÚs¿—t¢Ã%‹ºþ BHôxð¾(ôF΄á3)7µÇBN•B{W¨0{囹ÿ••Þ=î“0fÌgC¹ 4†¼Ëj»Ž-Eii©VÌc=ŽyééãIwìÜ¥;çªKT÷Ãâ7TÙ\õ4ÆJ.#Iï0­Êá‘ë<³+VxHT%jgÉš5kP%JºS§N¡°°P›œTÜ@¡¥N½–&,‘üf‘d |Z¼å":¤‰éÑ#Û@¢d~7¾~FüŽ—Aq‘‰lhj•‘4ÇóÇϧ¨^ãæ é¯:}Êõ¹>´ ¶^~öÌS!õ4kï]4Vܽ®µøÖºÃxe{±:?Fʪ´€cÕZ½O3b3°nU:\›Oæ&áåüRì=Pu ã?U‹¬MµòCmÂÔI²ýÓ<¸÷ç¢üc Q;PÍ^ý¡å‹ÎDŒšƒdWxº¨ñNƱfq_k;dp¦!zûEcDþȾ̃»2&âë_ÿºˆ=¹W…wª¦ªJù…IÆÝ«²¿³ŒÚí¤µÏÞ‚j&1>ÊC$W·ÙÚÔ?:cì9ïzõùçýÞëǯ‡ º›Ú¤Kê¼Â<§Öê¾w©™|B$@$@$@$@$@_ê$ï ”Â.××]wn½õVw¥«ÔuôÖ@!þô³XY¯Êð¥îo+¯lÙŠ£Ÿ4ã²fFÖÝàÞœdÑ~vãÄ»›±©`?jšµX˜gÝŠ¯ó¯an*ÄŸzIû¿²˜ðü÷ŸÄŒE_Ç#+’tMmÛÿþ¤{‚+޾dé¬}ÿþÓw0zFR£êñ§üÃ"SÒWoÀÊÄ:üîç¯AÖ¨"ÌœŽ¿ûÎ717F© -¨¬(Coô$Ø»kðÇŸü7>­ÓÍ7ÆâÃmù¨±Uú ’>CÒW>‡gÞ>…¹ßtÊÚ‰÷ ^=ކܿۀËÛþ›”@Â;¿þ'œš~$á–jlýÍKØqøÓ„ë³ñÀß=à”Á‘>Ðgñ¡øå/(‰ìÎø6-Z0M È@cÈ;ßPÇTÕ™ í8¹„„¤§Ï•âÇ¥‚ÖDÐ>šKwàÍ#ˆµ£Â)›y^.ò(Å´ §‹vâýÒw9™_ÍEZ\8šOH¾$_´5¢¸%ô¼ÅY>³ÅUêÞˆ´¥«™(jÚmׄðû þ@rèo¥4n­<,ÜŠû¸Ínª½Í¥Øôz‘6>P² [íyX3'{ v¢¼I Òo©¸í« qå#ïqsðá›î¾¥{ ßC¹c¹ói+ÏŽš~cUÕù'ÏñxÏBT¼×_^U¯]dÚW 29Ûb’1˜ûUÇqNÖ†“~Ƽ—4¶&œѱeœ9ß;QÚë Æ¸tÜd#I}‹ÆÒ¿éÑŠ#ˤ‚XÜ~[ŸDÕh4ÏǼ¨R”TVÁ"F‹×;’´Tv}%êK¶âä¾÷°·Lý´ªb‘µ,³d¬¶Ô—×xŠÔûÛ÷þøç!F )Æ”§)í-r¼Ø›Å0¦åÊ;Ùg°éß«qYUo¬Bþ+EÐ^ y/2W¯Ašz÷û¸›cµwYköaE½šæ’rù/¯çeJSòksÖ«¨2ÊJùaÓfa³è¶¯"ÅÇ.5½]1ëî^ o°¦j.<]ôžÏ¹ƒß­ØóÊ[¨ßt—cã[b(KÅ9åjm <ÿú›¯>u„ÙÎÂb4ió¥ìZ’=ÃVe<‹Ró¢:òk7þ\✻c“¤þY5WÆv߸Iï*òú½ð|ïýÍ-êgi¸smßøU­ð Ûäg¼E´ú˜ç2'ùëõc¢O–¿už"òŽH€H€H€H€H€F$µÛ_PÇn©ÅÝ*¨t@E…çŽ@ù]åöº.||÷Šìaµ”ßT½^;Iìµïá¡ÿýœ#ŤLCÎ\(Ƕ_ýo„Lþ–µoÁ÷óŽ?MŽ»,~'ʾ†zøõá(o´¸ŠÇY9Ž,hf—Ô¡—5wþä ˜k¥âs{ðü›‡µôš,îœ}ö‹çqô¬k&Ý-xÓSxß}/f1ìüø©ë°é?W£çBö•1)xà®±8r´\ZQŽò£}Tú§žJ’ô·ÁÖ|gÏž…µÑ%kÎÄYYOß(õ9¨A:B‡ì:Š‹èíªÂ/Ö?Ž}®ù¶ÍÇ©À_ü¿˜¥W9éÒè/¿’µUgÎàÍ·ÞÒ?v_ß±jTO~îèA]ôõöÀɇZÏ+WAý©à+ïÔ)fü÷³ÿÕ¯b_iû%òz0œ<ªˆ‘ÆXÉäyxŒrÜèOåð::”ò\”|F#ÂÃý¶èhQèIPŽòa2ph‡Zø18uò¤¶-Nm;Qz5í6X.œÅX ÊË.bîÒ<,ÍHBwKŽ×X=}¦¨h%ÍÈÈú ’ xeÛ^4™’±tåJ,N‹FUq>öIZˆò²ª©U¥%hŠLFÆ¢…ˆÓ­èVÇÆˆ’->+«ó!¶½ù[ö‰Š­û6½*ˆ!!+—fÀP_Š­…'¥bQ4‹òÒÚRÒó¤eÜŒ‰¶n´vºf¹f¼¿K&ó ±™6á´(¸Ã#ÃÑ~º¯î*Á¥è9³’#DΜlêmE¾HbÓ²°re.R#›°Û4¸ŠTíÕ‚£înk*:⤜<Ì‹5 æ˜(¦%¾zÏfìIRÖR¬^¹ÉM(z§Xâ$_µNT¾îDä©¶:P²»ÅmS°4OÒFYQú磒6PÛbøù X¿0ó'w þn=©¸Ãfž‡|³ãÉÙÃ8-jàWY4?J®ÃœéÓ×ý+¶þéOø¯o:K8{š®Ó‘bŒ¨«]õ†º2½Ͼµë…Üõô&lûå½h,|Éa ™´ÿýúÛâû÷¸c’JSŽWþ\é.ÇUž¿ïû¾v?V‰1Ä;¨g*Î_¾Á<×Êô7†¼ŸKâÁ”ù—H£Úñiê1ŒÌ]ÃS럠QªiCãÆÓ2©Ý$ÊÉQd¤ïÃ`êêê´t£GÖœ# X“¥QT¢Zªq`o…c׃3SÚø$ØÏUË]tŸ›Æâ dy¯qÂTÍÁtkbÒgÅáÄ[¿“üÑÈ]v“|ʱ,sÀ\º]¶y]ÀMÁ ÚJlsæjä¥õm5rVåþê¼Ü…ðY)È[;§/ÈZî“¢Ll,I‹—`Îd#ìÁQ¸A­´¯¹ F„‰OTî»;SN”ß—Kpªòœ¨)ãqéðÙvfBî¢ô4iÇ‚-˜d‹k…4)÷å¥;Ñq+Ð|ãzÏc_‰,77g"{nzd²¸áÆy(Ë?†Æv;bÔÖw¥nh(ÍYøzÞ,­œq Ñ(Ñ`Jñ3nFÖÔñ˜?]—[0Ze5Ž–tŽ|†„ExhyŠ<´¡Æ(;o*mX¨µ!4Έr[:¶=EKëÇëb úýÉÝä·¿/⢕ÇÚåŽc Ìqy²Â|*Î4*­¸ì¨‰DqÍ8d¤§‰l6„.ÊÂ,YUn·£½©MÛY0zì7Ѻqcß®¾ Çé½[dœÈ.£ûW9wÅõ§°«ºI”ö€ù¼xÝzŽUUç!¯ñ(ý€,Ìö!oåÙq¢ÆÑº5ˆ7ª‚ã#ã¡mœܻßï˜_hŽ÷¢E{7MˆSç=É{W#±ñšÒÞ#®tª–Ú5£gLß­¥Nã3qlßCÝU—]YT­ýÞ]2ØŽ¡HÆl¢{“â˜Sbn[ŒCÕb`‘#•j”!&u¢èØO:ߟpTxÝl-'4¹”¯”R1Š&-ºKÊ ¯R»îßj ɲË%[ÛåbÃdã~´ÊDÓ>„÷ÀÁX ™bÔ—)Ô ¬]1e¢IOîð;§´µ¨ý#FLŽ6J›ë¼deEEÁy´IMÐÆ‚Vƒ|´BÒ+ÛÛü3íi8¢ñNZ|/r¦;þQw§/lÞºfºü¾®z\ßFÄO“ºŠ*,ÿ Kºr/J?ú+7Ú^ç5Þ]å8¾ýÏ­Ø[ ÿ •cÐîËsÿ·M•/¢,v2ŒÍG±·ª[Æ´'ÅÑž˜¼Å¨Ø´qbh²5qc¸þ÷BÿÞ[ñš“ýÎ-ã‡7×êÆ¯gk[±?@›z·¯yN^eø›ëûÆ„þ].hâxÿÖéÖ xŠË;     a&Lèÿÿ|—ˆêÈ­ÊÊJmñ¶râî+ÊïJHÚÓ£7w¸r ü­ÏÖ#»JÄ•ŠG›¾/üÇlìù ÿý£Ä‘ârù?Ÿ+!N|Áâͳò  ?|àqmrݺ ëþj%bz»p¹«½°‹Ï ›ú‡ìWÜ Û{Ñ+‚y‰¢es·Ò´ËRMèè5`zðA•è“æÜ€pkŒI‰’öCù»‚N›”%WZöõê ½~Î tµ[0a²JÿüUã•^÷Qãj“ƒ«»M®Ë—»ap>ê™­Ö4wèžåèüûwÔª;çìꚊ:Øz¦9ô­ZLàuë¾&'!Û·;v”¬”ÝêÙpûX_[ 1¤O§®‡ZßË/¿„C‡º‹5jžxâŸ0uêT÷3×ÅÏ~öSÍ=Æw¬ÆÒ¥Ë\ý=TÙ¼ )Œ•\F’ža:¸QÛÕ”5Sí(yçwpï½÷z·YŽñ·kŽÛUÄÌ™3qÔ–(xÄç€RÉfÜõÒ'èª+ŠÞ³Â”í>ËZ[%OM0‹Q­(?%™ã§ª•ñ—5¢ÆFÁæ—UfwPÎà[êÎÉ},nòc 1šoFî¼K²›d76É_tò"¬ÌNAíÑ=S.Ú±gº÷=æ)]Ÿv4ª-[â?B³‘Ô*ÿMiïJ¡}‹cë*$ÆíkÔ¹“*ߎ Ø{‡-—ÆÆFÁèãѧn®©’[3æ; $*.xÂtdªï´ŸÔ ¨±rì–øSÐÞÕGþx¨ãïTè‰ Q}sáÝ5ç$µœãè:L½ZEãâ9Çø=èc|ßCÙî9AÉQ·ïl/‹ÄDÑã_¼àNi­Q?ôS0Q†S‹&S2\2IŒÝs:WE;‚p–ÞŜرÄ´f_‘¤JÀN‰Êl—D©Ÿ‰°1²*Åßûçc|·ž­–|±˜(+<.Us…¿rCÐrZñõ3ÿœ/slÚÍ®yNŠ‘yBÙñb§ŒGÝéCZ½7; $*Öni•±í04µ”¹ÆÊæý:ãd«îûíwnî\{ù¢{ü*¹ÜÁ9ûkÓÅ ªM~~s¼æ9b§¸\á–Å8Á÷o]°[@^ ÀÈ& rOž<µ¢Ûð7Þx#”Oåà7¿ùw4 ¢ã™>}z¿çÞëOÝJïlÜ÷deh¿´M‡~‡GZàxnŠAfv6ΈâGÓÿõ rÞzl~6Ûó·£ðÝø`9‹½úü½½ôC$Kš¾Ð¿ü¾8u5˜´Ny££Þ«VÊ‘XÎG½Î¶]6ùº3„hËmumÔîUdfL ‘o;:{Ä Š3x¤Uò(™/á¢Cmâ¼åL „j‡%ÙÑqñ²ãYGLSgâ²Üλ9I4]¸2JŒ5RŽk3Š;s€‹ûî»WNN ÕRÜu×]ò=»…é¢!]BírhuVUÉNœFY4î é²€5..Fî<Ë9räˆSz†ÄÄ„~ñÎì|y–9@bŸÑ#ƒ±—‘$¨Ï¦çSh•³Ü¥K—b›l¥Ú#/¨:r+//O›\TåôhãÆP¾KTÈÍÍÕ¾úh9w^’Ä"¾ŸDËQ)š¢rš:¢Æ´•üâŒV)zmMâ'E^„ÉárS‡y/Ͳ"=Ï©pµ[êp²¢ Sfšqþ™db³ÜJWyÚ·½•§1yA^ G¤œøÛ÷ïÅÎIS0K-)ë×/t*­¨>y ]¦ir8ÞA‰5!AœJ»BøxÙqÓ]‹…•bðHÀ=âD PÍsl²Û™óèP598‚¥æÊÛÆ!ŤKï[—^´½æ4ÎtŒÁ ]$b#¹p^êŽB\”«œvœ«ITF,ˆÈ\yøuˆD°å$~¿i¯¬¬Ž£”Úy F&g>{«c…wj¬súæg¥ÉÚ±@UþÛ.Äý†º@õËYŠþäÔßbWÓq¤ÉÕ^éòšc(—‡©I²§Î¹jÛ<'VRYQòÎ~ÔG¤bõ}7!&±|r±µøªÓ5ÉkW4C³h[iDâ¬ñÆ|¼——ň&Å$ÉÑUS£½wfLq,¾w¦µàð;;4C]Ö-)ýŒQúQ´÷áœl_ÈŒ‹ÓG¡rßM!;/s¦œ)¹WoúwDŸ¸ë²3 t;ªåhª.ñ3¹ã¼$•1>vT¿÷Ç'¹)C¡1÷®ÏÀÑßoBñÓHÏKÑW©]{÷G}¶eÅý#joV>@ ˜#;?pn°ï0®—âÕñ`5ÊœqVÚ¢Í?j$ûœS®C›òó"mqyFÉdœ*ÿp;Ê>*—›$Œï›r´HKcß|ÐuÊ?Ó1ÎñÝ÷O Y»q且‰q!M8&sh¼ŸùÖ%…ë»ñ¼ (q0%¯Ë…@冹æ?×xw•àø4_Ø-eBË€ cûlÿ$’5C BvÍ©S°¶KÌUêéâcr/†&»Ì‰ò/+ç¼ëÝçî9Ùxn±\Psæ0æÚFÕWjüzNvK{À6iŽW|Ži÷˜çÔñŒæz©ß%·E{ßE12Vžôý[—§34¹Xò›H€H€H€H€H€F* àOr|“rØ®.ßÈ®o}œºVÊduúÍ@! þTo¨ Ÿñ—q¡ª‘¢“évúW1˜¢q©\tL‹ÿ ›[(‹ÑkñŒËH"u~òî¯ñÒ¾óH]ýøÝ7͸RùüÖÿÈ §pè“ËH¾¾¯2ÙÔ!îúîû]¹â¥s¥¹ì£,W>WU뙫2]\°:Ù(@|sÝ%GüÅ38(j wЕqñ’ì«é©Ó§NŠeäüÛVhËI/”¼ƒ7?¨Áä…ó¦Ëã.g€‹»Ö(㈄aäudìÿp y'ÿõª±®ÞC°(gtå*.Ư~õ+­&Ÿ8Mvñèâ½Eð{?œ<> û‹3™4õ•K6ÝÆ'×£A/_¾\;ËO9fWÑîÝ»1mÚ4Í¡{µ8ÏÑOLG•þ¡®N|£øÉ¨F›{yº ‚Å*hÆ%}Ý®"âo¢ë”úÓ˜X›+±çOJQmD´ZŽ‹Ì‡‘I•ת4çj5x„ƒŒ­©F&W#¦ÊÊqS‡ÿ¶ÛêãµwNbúò5X`îS`ª2»:ÔÔ‹™Nn[Ëÿýa’#ÔdCÞî"Œ[<¡+PPPª<\cžª_v‡´J|´¬ÞnµÄ8ì¦âŸaŒXæNÀÛêL'ƒô­RèzŒ›`mlh}#ªÖ(©¤»â J“MH2]AÉ{ù²cÇ€,udZËaÿòÙP¸å]X³±*3^að¾êÔGñ€OyE&±ût—ïGiÒ8L3ɸΗq-}cN¿#À˜÷0ÖFyY}?Q>ŨÞ;ƒŒgyïBå½ëê¨Å±â2(sBÖjÌ£\ >FxæŠ\E%Û±‹e'ˆ8*ïjÃÉ{Å@רԥX‚ê½ú[µS&%ÍÊJñÞ¡©øJªkT¢¸7É»“ŠP¥Ø•1ÒŽcÒ}î>òËcŠf¤ˆÎ˜)F†p̘²âè´¦ Q†ˆ>x÷‡2n8Êw¤j­?'±ˆ•°ØÌú2Å"¤ù1kŒu1¢Ø®ÜQ‰Ê{ 9e2ZD§¯äP»„”£wt—áÀéxñ7ŠŠƒïA6§ÉtÙÏØÛäd¥ŒÇÁ˜fÄL‘B˱çð4|%y"ËÞÇ.Ù™± Ñ—t†!I¥ŒúùV{äþ£l•´%Íáˆ>*@¹qÁŽþÓww1rh¾Žˆ3C1>Üs㳓a¯=†ww+ãC´f|ˆš*ïZY¹ÌÏÉÈ”ùù\±ÌÏj7˜yŠŒݼ+9êü½÷ÒÇæ––=^cX7gšk›œsÛŽíltð„Àm 4Þlºy.< ;WýjL\p½Kb¾Üîç·.àûî”_$@$@$@$@$@$0RLš4IsÐþî»ïzè#Õâmå#Y¯£tÉœ‘‘¹sçºn~ÒŸÚõçf,Å3²·×µ‹¢Ïÿë?xF†ßŽ\¤=ë8¸ÏoªEÇá7ðgg*µ3ÆÞ&>8OEù<ŽúÓ·cbÇÚ1Ýj§ÆÜÄ0ØÅàÒ¬¥¯ÂS_¿Óïü~ðW3%x~¹v„¨§ª=š^Ê3‰ÇPŽ4½²KÃ$:¦KÇB]ëï•Ïx—~]=÷Nß)ÏÆ8XÞÿOüõaYrjCˆ38òØÐáÄVø³o hþƒxþ¡\˜þ(Dz—<‡5ߨ‡›#[ñáq¥ÇÖd­Òêqñý 4†¼Sl†\ý§ŽÙê‘sÛžþy9ùé]Ù%u½Œ};NŸ>%º¿:mGÕ”)S°aÃÿ6—¡Ê6”v|Ú´Ca¬êò2’8^¸á¡v“|ë[ß›o¾‰ÂÂBÙÎtYsLì*KuLBB‚vöŸš¤T¸óÎ;]Ñ>¿ÛÕcqN^Pà/+iï¸Q¬ˆrRTjÁÖâÜY";$DÇO‡A±¼ Óú‡pò¥¨ymönU‹WÝ—Þ›‹Hk¶²}†æ°Úåñ)ŠØÜ´Ó((ÝWE­BTB&²äHšpd#óü›(ÚŸ -Ê€Tñs° ÆŽ¢]²%^6t!4T;ÿÏ*>Gn‰w®'·¶õÕ/V’ÜÜyx½ Û7;,ÄFqH¼&7E´›Àm‹’ñúÞlÛ\¢ªäÈËö66‰òò¼gÝV9¶LÛ!"±8ÓBD—ï®W7ie˜¢Ä,cíÀÞƒ§‘&ÎãI}2w4‰i!j¶¶\%ÖŽž2Èq?"zÄ,mAø"èèî@ð¥öõ ætõâ¨_ÇL/·¥¢Q òÓßÁáÈÍËÀëùÅÂMŒ#·»nsø‚Aˆ ±ÒUÅ»ðžá.dß”„òýÅØür±J “ÄuX«pXŽºI?nÖæôõtÀ¬e«Q/»¥Šò_G‘VK”ìÈY© Ze¶_ù.ŸCu‡°g徂ÇXõ¨S¥6b¶_ymÈ^²õ[·‹L¯:d2D#kuž8LÆcÞKÅÙ*ÏÌê¼'{«ø¸‘›nq^PïNi(‹331=Æ1n/T«>¶b´Ûú‡¤­ºÝ;ÞE±OWå®’̈䬕ÈAJÉ^ç5N½Ë ‰›Å©µØ]² Î!„Œ‡*9eæCµþ[H­øÆñWÉáb4^ÿ8dýì>!ïZüõ“w:×}HH0æÍ›‡õs?^zñ%9rgåXtõ§iisððß=Œð韦0}jß×C•Íw)ŸÏÓ¡0V‰¯^eUR>C.^¼P*užßüùó¦Q‘ÍÍÍš1äìÙ³Ú65µ£DHÆŒƒ?üá8pà€V†š¬2” XÙØÄ¹¼]_F0C v¬â>8Äï¬v9cÏf—c]Œ!Újð!•ë3±Ôeé’õŒýå ‡Ï¢|>´Ë›hxUù¢[&=>Úå3«×C_m·ˆÿ”MùqÏëú­jwdÿìê÷ÇU®ïþµkýçìw'Ç£QúLåë‘3Æ ‚6†$×`Ó«ãÞÞzq;Ư| ãWG?1×fµH+B}Ê4ì1ßOˆ¡?°‹\—…-BF!LŽáâ[§U¨Æ˜øû’סÿ»çO¢@<üåù4Ï}½ŸWy–ÊBlÚÕ†»^ƒ ãbð5dú™Ì1>dv¹®÷ÚÏ|5@¹Ž¾£‰ŒŸá— ¾çÿåºòù‘Ý_ÆAµÉ÷oŽÇ<§ÍmCœë¨ÛŸÈ|N$@$@$@$@$@#‘@SSÔ‰6gΜÑVÕëeŒ‰‰Áœ9s””¤ðú£>Ò|žøJTZZ:èÝ(ÞùCeaµr_à+ØdˆEŒ²ÎYê6¨¿m'€ò£¢âººº†Ñ¢x±X¬’Æ éa;%εsF‹w'¦·«]5¾‚Z?v¬c-ºâK—Ô"õþÁW%¿j‡Kµ`^ùˆQzç¶¶6x牌ŒÔ|V·¶¶jmñN¯ŽŠR>°Uäì Œ s:¢¹vPíRת^»]ŽË–ÅÉW®´kîÂ$­U,[e‘é˜HzE÷©„ |¤¥¥ J”áŒ)å3\õµâ©Byy9ÔÉOê¤'µëHíQþÂÕß§ ªƒÝ}õiêNÞ¡0v•ïa$innr=÷ù]WW?(#‰ÏÌ·êÅx饗PTT¤=ùÞ÷¾åøáj!`Á¾WÞÄèEwÈQ[¾'÷«¥¥ƒmG]ÑVì뜃5ÙÓ‡e$l=Lwm¨,|»ªSñàC =üm\­g+I€H€H€H€H€H€H`蔂\)ê•Y-¢SFF‰@¥+#I\œœõí#;v7Ü08å·ìƒzäR|+ýª¯  *R†J£â¿,ÁÑÙùÒë»ÍªªÝÞmRÏTð~®=ü ~=Z*ƹك’à‹SƒÄG"ÕŽÏ{¼û¨vP†ÂØU Çq[AA “+òóøVûÐ&#u`´:cá*"Ž…ëÖ]EíùôM‰Ë\ƒ»?}1,4ã®Ë@†8Ôæž$R$      kŽ€Úá0qâDíïÓ6>þ´'€"ÿÓÖ«ò÷(g‚Ø>ì)õå±h-°Í*•¯6ùzˆÝh y‹ñy)ïú†r?’e cÕf#‰#äPØ *­2”Üu×]ƒJËD$@$@}&LO‡§•¾8^‘ |¾éO{{»>_ Xú—@ 1äݶ‘<¦F²lCa¬˜{IÄéݼ'         ÐПÓq;Á^kŒ!o#yLdÙ†hçð4’ŒàmHÞãƒ÷$@$@$@$@$@$@$@$@$@$ð… ?íéæN’/´/¾¬•CÞMÉcj$Ëæó6o¸º{#IPÐzHW/I€H€H€H€H€H€H€H€H€Hàj'àOªÜ Øí6ÍqúÕ΀í9JcÈ»T•ÖÖÝCp°wÔˆ¸ïíµ9ôBtÛíCbìÊëa$éÀÄ$/»Á#´c\â7 |V”NTéFýéOÃÃÃÑÒÖŠ¨¨¨ÏªJ–s•PãCcÈ»¹¦Z%Ïøñã½£FÄý`ÛñE «x)nC•ÍÃH‚ÞÀV©Ñ£GãâÅ‹˜8qâÙ6ÖE$@$@$@$@$@$@$@$@$@1J'ªt£þô§'NBeå'"_ÆŽË%±žy«$—.]BÃ…$&^ïw yK>)&Ÿ|r ]] £Ñèä/zß;‚¥²Z­hkmÕ8_ýŒA3v +h¯ê(e ½ÐÐèzîó[¥i¹ØŒ©S§j,î(ñ‰‰I€H€H€H€H€H€H€H€H€®Jª $çÎCÔø OرZ¯ ©±ôöÁ9÷UÀ‰MðO (hL¦pDËÆ£qŒÿ„>bl6.µµ¡íR:ÅÀà›Àh1 ;cÇCHHˆïDžzI/HꈲÛ{´±Z;ÑÝÝ=`z&          /#ƒÁ ŠíÑPJØààQ_Æ&Pf x·Ô;ð‹n‡1†0ÂåH€H€H€H€H€H€H€H€H€® #èx¡k‚7I_#‰}$$ö`5$@$@$@$@$@$@$@$@$@$@$@×&#ɨQ<+ïÚl5 \{<Œ$=öàk[L$@$@$@$@$@$@$@$@$@$@$pMð0’q'É59 Øh          ¸ xIÐCïC×à`“I€H€H€H€H€H€H€H€H€H€Hàš$àa$é º&!°Ñ$@$@$@$@$@$@$@$@$@$@$@×·‘$H $½ÜHrí¶˜H€H€H€H€H€H€H€H€H€H€®Qeqý=[ub`³I€H€H€H€H€H€H€H€H€H€H€®5A===½ ßî?u¯þH€H€H€H€H€H€H€H€H€H€H€Hàj#àÚ<âqÜÖ¨Q£´]%4’\mÝÍö ¸¸$êÂÔ5 $.ü&          ¸Z (›ˆ¶“Ä—¡äjm4ÛE$@$@$@$@$@$@$@$@$@$@$@Ê6$;G<œxÝ’ \•Ü>I\­Óï*q=ã7 \mF]m b{H€H€H€H€H€H€H€H€H€H€H€H`0h$ %¦!          ¸êÐHrÕu)D$@$@$@$@$@$@$@$@$@$@$04’ †Ó \uh$¹êº” "           IC‰iH€H€H€H€H€H€H€H€H€H€H€®:4’\u]Ê‘ †$ƒ¡Ä4$@$@$@$@$@$@$@$@$@$@$@WÃÐZdES}ZÚ»%›QãiZLM$@$@$@$@$@$@$@$@$@$@$@#€À $ü?ùÏ-¨÷:6c-¾ÿÏ0=Ò+âs»µ`Ëß®Æoq?¶=¿áŸ[=,˜H€H€H€H€H€H€H€H€H€H€Hàj&0¨ã¶Î¼ý|Ëe ‰œ¼<äd$j\ê‹·àÑ»ŸFÚ\ò…öF©¨² ¶/¨>VC$@$@$@$@$@$@$@$@$@$@$põê•°Yݧñݯ>ŠbI”÷Ý_á±%ÓÝÉ»[?Æ>ô-¶ß~OÝ>Í÷ù]t£éÌ4"Ó§ÅÊ¡_ $@$@$@$@$@$@$@$@$@$@$@$0tÛ:¯à¢³Ü›oê3¨G†È™ø›oæ ð©B K¿•¤ïoy[ß;ŒêF ÂÃ'bÖ²µØ°~ ¢UFëiüꇿBCÒJü]N6?û"òǃkÇáÐ +Öüó¿à+±:Ñ,ãÿ÷4ĬÀ¿<–ÒÂMØÛµÿøhŸ‘¤¾´Øü.N”WÃމÑfd­Xu¹3u†”äR²ùMmÁ¿½ð1V|c-°ïe<ÿF9!u$ߌo<þbªóñËÞ@¹³­I7K»¾}M°à£·_Çÿ_¸5M"˜V>ˆ‡oOÓäkýømüû3ÅXðµ°¿‰·—ÃbÂ'&cÙïvø’H€H€H€H€H€H€H€H€H€H€H€†D`à$ÖñØÊoḑ˜‡Ç¾³éÓ¦!ܯ¿özüöþû±Åé¼$"6íõ²ÕD…Äûñšø‰´”âoW?ŽJÇSçg,Ö-³à•í’ìY<¿~¦;¶þ½§q¿b"r¾­O¦ãåûWc£ø$ym£”%©j$þAe¨ÑBb#Úá®ríÓx~Ã|‰„\μ¿Jû·x|‹NÚˆ ÝÙ&]âXik½«âœïâ'—ˆ¤ï=}žRÛmTˆˆED{=\¹³žx?È5£_²SFߎØ;Œf:Êà' |™Ø-¨®¬Å˜É‰ˆ öÜni@em¦N7ïÊÑ#Gÿcg¨@IDATKC5j¯ŒAb| Ú+óñ£oåk1±‰¸9+ 7Ýœ¦÷íè8½õ—Ilžý¯¿ÇÌH¬õEøáýßCqåFüú½åxò ÌU>bqÿwÿËÓ’ÄxP F’çP¹m7êÅH«¥±¢x§Ã²ö®òÄŽÑ*óe8^1âüÌi ÉzägxrMš6™Öô2îr#*·<ƒî݈±ƒk‰£F·h΋P—´Øð³°6-Ý5ïá>¥zb³ÁOŸ\ƒXa\óþ¯ðàÞNT¢KÄ t/j’D|ÿÅŸã+fåj¾¥[¾‡Ç[ŒŠsjŸŽ}u9ß~OÜîØsæýßâámAýßÃÛ_ÝŽÛ§ùîHo™yO$@$@$@$@$@$@$@$@#†€í wï’CñPv¼‡X¶¦cؽ»K§>ŒÄaª¾š>.ÄîòxL~8GÎp l ¥xíí*,Z· æ/ ¾ÀÒ [sè-ä—Ôëš¹z ҜƊ擅x}o….Þ(ñ÷J¼^£mÃáüWQÜ”„û\Œ¬ÕØ´i—._ߥ!i)Êqø¤VO­Õ‡QRUƒÅY}ÀZOîÆ¶½UXº^úÍma°¢Tê)ªÑ_Lɸg]¶¶ØÝUƒ¾¼ä¯.ÚŠ‚Ò&WVùŽÂ¢{Ö %Ò]©..pýö†Ãxy[‰.½ãr±´az_ÓÜñekž×^E™žÜ!*÷¬YŸ¢¹É…å4^}u7¬Âùa'g‹<¶”ˆæ¸/$d­ÆòY1}ôW¶:¼+éƒ3VÃãòÀbÃñÂÝ(ïNÆ_åvc—¼gþƒclוâÕíEpô AÛPQ^†âý¾Æ•ÿÒ´K%¶ÊÓ÷^ô¼<¬Y`–1µ›vù–'9w=²ã½;$pß"@]ãÃÚ°mû^L{x¹lMè6’Hó’ǰ=uò·¾‰‚]…¨”mõ•ÅxCým”±9øÙ=‰´H >,PÞK€oÿèÍ@ÒÝÝ cl&þù鵸ûÉ-(Üþ¾sËõZõqÿÏ~õiΩ+zîŒxo´¿Î<„5Ê `9íªÈˆ÷±Y˜nCué|RuŽ=˜·Ÿ–ºÅ@º¹7MEËÇ{‘_TŽEI¸;Óì,SW^p5^ $óaÍ@bJÈDÞ¢@Kvnß½ù‡´.³ßŽ’ê¿Ü¦,&d­ÌEl°Ý¹£d &xë㕤°­/|K3$eæ!kÆ8Ô{»Jʰójkœ³­¾¾,Ø÷¦H$Ê4ÚiÑ°Õ _HŒ È]•…IÁ-Ø¿=ûßÅé„õ½9uí–(±¸+]Œ(5þߡѪmÝÁ}öúõYrŒ`[^Û¼ÝI‹qï­ °ÛìÚs£´ùÍ@E«s‘㨸½îÞîEÛvbÊ×ó0ÁÃÓ_6Ç1Ì(‰órWaî$;ŽìøJJòQšøuÌš<y¹b'pŽ¿ÐàìCF‹l(˜1©‡îÛIëJKÏFtÉ6ì.mƪ´ ý”‘Då2ÆÎÄšGÕß“°¶6á\ÅÇøpçVl,”ƒ¸ê ñøC£±ñ•”8O¥úåÃ+ñË~ÕɃ¦‹N+”\G܉<—DK¯~-o~yŽ\‡´…f˜ÏU\MÑkÂÀ„Å÷åAŠåÉ/:ðØšo°®ùÉFEƤnt«ªû¦íÎÇÇ™‚_á…?ŸÃÔ[Æ£¹Ó´ÆÈhLŸÿíïžµocÝÿ'qþRŽ:Ëb1;ÚuL•z$¯Þ‚q±}¾‰&Œq¤vN[rbÅHR™_ˆV1’ÍÏ—¸¬ÌžîN£¿°µ;jlt~ëãÜ×2؆$—;£çE—ÍÛâŒwfÏÔÎ;9ì»âÓű¿F ®³3Ä2Ñ ±eÿ£ÇiS…g„sìz>ä \mºÐÚaEGG•,ÚNF†9UGÊPºûu`ÌzdšCp2‹ìVè†ÉœŒ”Ø`T)E©Rþ›,š¿#+úëaÒò‡¡¾âjÊvラS138 ‘Q&4‰ÿŽnc$Âd÷„­î6Ë*~©©iãqñTÊönƒ}Ì}Ú‘?Õçݧ _ÅnQ ¢ÌH›Žs¥å(ù/Kyr.UWG *ŠE1h2c^æD4/‘û|L”Õõi^ç4Mºa ²dSÇd·~ІºZ¥NÆyÖtî¼Öðq:­oð¥“íÆ…‹¤Ä'"ïnDZYuE¯`{™·´®{ÙÝð§"aŒìY.c”#®®ìˆ”–€œg“¥ä¬3‚è÷ëöáwÛõ »íè¼"±±ºÅìÁˆ‰“Ž)»ˆK¢CU.6ôå5í@þY·a}RŒÒVk{+jO± À”<µŸ¢[Y×/>TÄœÐQŠ_(u4Î`¼[WbA¢hý­b´’qah×l-Ùö`æÒ,L“à(G}ZÏ¡RŠ7ÆÓžÙíí²cÉ «øRq[Ã!ä—¶`ÞÊ»Ç\ÎïÎ.½¢Ù±]ãÊåþ~;lâƒGìH5G{•0´[½ÍL™ZªÎÊØ’1¬3¸JŒLÉÁêã‡TùñSu2'!^Œ™Õb ò­'4.QóVºwÍxò¨o§Ê \×ä$yÊOá\;âeäqÛW=„ÒÝDŒëDqq1ÊÇ-sItÑ0NËÀW¤Ð|)á×aA¬AUŸˆ§ó<æõ)[ñÑûÒíÑÊŠ(ÖK!r.îœ ãpñ\£Äqæ>Iw=^~Öi 3¨.´çÿ¶®ø½»h*³²³F…›/˜.9/I€H€H€H€H€H€H€H€Hàª#4+©¯MÆx,H0`wM;Ú/\ÖÑI) ñó“ ØUíxdμ÷&VãXÑÔÔžG}“l=Їr¿[ü0ÈC9Žê²¶öº ;_{ê ™9ÎIâ —¥¾ÎëÓ kS#ý줾ÓtBÌHO5!¿¬-J×+Êqc|rŸ#sãQw¶ÖJì|k”?tSÒ"¬vú¼°w*A 1î`kjÜñKî>X%íMƼxOå±µæ8j¤ü¥©ns€;×Ð.ïò-ˆÃ€a¹X‰=â{º,;¼²±hAê5 órå¤#3b"°$Å…áÅí%8|´)rüSx„Û"…ÁÊÖprÞÞ[®4Ò–Þ‹4í\4%Vˆ®<9ªàmÔ# ÷jŽÚmN»³ã§#;õ ÊʱíårÏ6éúÖam»$—†Ï̘á*7€M U4_²aì„Ⱦ±­e´¢è•—µ>®rLɹXw‹6Ô1ÚãH3‡1ÕNûn=…¢& *-]ŒhúO~úÏkÇØRŸê ;ÞµÙÌ3»Üéíý"Õã´5xzmžÜR‰ÂçGác‘‘6 ãŒVœ9¸_sâ®Òe<ò(ÒÔÛ¼îH|ã{¨,üO¬>¸Éa°^lÂñÊz• OˆÏØËÇ_ÉÖmÒbú>¢3–A<“8¨š¼›Ì}‘^WqÿÍœçeÇH;žûÖÝx?+×_è0,Dä­ÅÌÁÊåUö@·Ê^§‚¯68bĪ9ï@ØaÿS¸ÿ»‘#çæï—Â*7þ/Ïø)¸2ÀÑŽ‚¬Ü0® »ò‹å‰„صXçµÃÄ…$@$@$@$@$@$@$@$@$0’ 8O×1x)L•Èv#†ŠUoíoèh¥Æì[áî±8]bñª49Êgßk¢l–-â[$AŽØÉLèD‘ìð4©hXÇ—Ó]œrމ?ð“¤FÑÞJTŸ¾\Wó¼Ó;´°âG£Ç•ÚY‘óÖ¡æuÅy~·žÞƒWw+ùMÈÈÍCºÎCh˜IžwxâÒvèiy–ç}go­@¹ØZ¢Ò’¼ª‹ƒuÙ½£ünxÙN¼‹pÞ;Úd=_'Fšx·á§«Sµn &û—7ü–šrR— éiñšÁ!<"+—¶ãå]娹`…Ùy˜C€êã—q’YŒ`}šø³\V‚Ž~&cj@¶6œØ±û«dçPt2Væ.D\x_Ùd‡ÑAÙT`û+Ð-ÖåF§»|~ÞŒÛ×.Gü»qߌJ”W6 +4ÓCñÞ«»|ꞃCÕ¨õ5V´Y…Úyã#xŽ8 diMCßQxÇ{ ,çöãõ]UHÎ]¯C×W‚©&’EY®j¶ ËhÙl{“6ì-r|ž2äiÁÞ‚Zu« u'eŒIHž§{ê}9Pß: Õå]ªë~”ë"Ð÷ü ÿƒgŸX u4ÚëQ¼¿……NID"îÿþ xjÍtG‘™øŸ?FŽJÜ.N}Š; $³ñíŸmD®Y:0x Æ«Ôf“ #¯ëÓ0wÜé8+/"g%R= ˜ŽTîÍ(,yr#¾»VŒ/ŽïÏwH²îÿ>~ÿX¦#ý`är¤ôûépgãŠƤ8Dí q=ëÿ9ÿ|W&¡¾¸ùšd66<ñˆìqÑžbÛáóÚ•úÈÙðò„]¥0~Ãi IÌÚ€³¡ÏºìNÍ         øGÑJMÚq¶qí¨8^!O .û„¦X=_uA—ªåJ‹/ŒÖŽ%_µã…#Q+ÊÔYQšµÃ‚s§Ä@’°?t7–gg"-y’åé¤[T”z%pÔtää,Çò县9┼¡0 XŸNL¹ 1Ejuµ]ÐËgAÅ©‘?Ñú:=³ú¼³‹¯e 1ÄÎÃ}¯ó0¨ Qqj‡G>©í£ÚT£N¶‘£˜&9Ó*a€Ð|ú-69ÅKI-Š}Íaû3<0~Ë G´ò5ÐQëØ1£¥kÇ'ÕÒ7¦p„ø(o ù›Ê ¤è¨c¹³^›:NM—EÊq'Ÿëï©Ù‡_ü +ûX¡ý"Z%§Á‡~w Ùêm× $ ™«ñКlÿ%Ÿq"æ&'!Iþ&M¿SL‡)æh9ª³oýþe뚌ôÌLd¦§`ìÙy$YÄ)W3Z{â¶í8/ÊO:O5r¥·5¡V† ƒÙu‚ä9I’¸û?P'4éƒÇ>ª’&$LöW!H\°Ù b¡üeggcVœ(ôCï|MEŸî­šÙ12Êå©ÜŠêJeÐLÆ4~Pú$Ü·!ƒ¨ËniÓvyõ•Ùw¥M}·þ® ˜™»Ïçþ ,MMhig3!c`ŠŠ”cò¼¡H›ä̽'ŸßŠïXZÑqE,Z†1ˆÖŸ§gœŽ§vîôW™ö<óÑç±óQ_I±öùXëŽ%žÂ’¿±¢µUœëHÜq.îÕºåò(³ïfæú_`çú¾{Ç•¹?Ø Ù åŒiø…GÛD¶GEYÄáÔqü3‘ÂB‰¶6;MòÌ$²žyåZ9“Óò°~í<âdg‰5ÒèÕÏÿ{÷e•Ç üÇ}`D®**ÞP¼"&^[o©™Êj©ÕòÖŠeËêjïj®µa¥™í®n«™µišeIåKnjj˜¡æ%Ò ´HEEÁ *ÊE‡û{Î3 ÷‡›2Ìïøæ™ç9Ϲ|Š™ù?ç¾¢(@ P€ (@ P€ @ã°k-¾$Ö`Ï™3ßœ‡a»ÁÑ>×~‹Âñdñmžgw´“_3Š;Øeáæ©ÝøÎi$èÐ úÄ>/¿Ðh›£‡püŒñ¸#ŽìSŽË/Üòå×ùò«´¤³8ä—œ$ü°'Zù¾0ýÚuä{‹¥”/”õ8sì$lºwG÷oÄ<ŽÏ#ò1ª¿r’Þ#çÅÚ`£íXM}¢.ÓäܽÝâ¸hÿv A@GG\8º_©áÙ¿kÅ7Œ›ž_f;éįʞqõôiä¿·wGwqç½]K±’C,Îï ¿Ž€û³øN, .1x–)O •Qpë¶üÝm•)¨JN¸q*F¸u„_©Ñ%Ç+ÚêØ³+^8…]ßü€Ñ:â֩ø .¯—Ÿ/nŸÚW®¼êÚ¯o/®×ùóؾýŒÜùÉqØ䂨Ú¾­ËGœªªßÅ;GŒ9#¬¾†vøtp¼#Öýˆm²E?oA#f'út’4=1íé¡ÐTi›†Èã"¦nŽ·púdJ±«½K{øz;C/‚2#NÁS,F>e@kømB–‡+álû<"¦ù‚ºˆñ7±b1û‡‡ŠµrR°g·\\¾#ºWà¯Á± 9.§êá#¾Ÿ×x÷"btMl¶ç F@ç°ÉIÇñQJP¢c@/U¿{ZßèvXŒ,:³_èü1¸¯šåßBÜÑC8#~­šõnåÙMúe²iÓ=;ڊ߇(|÷³=z·ÎÇñ}GD zt–Á=‘Dç²øõÓtëXnÍ›²~U][GµuåÞÉÍ¥‚x†rDõ[h=½ÄCÝ ­«ø—º¼õ’ËVW‹ÕÜóv‰ÎÙ ײâµg™}9¹"²*~QîGëå° P€ (@ P€ (@ T à3âq6Ø‹¨3çñýn9zÄšyû#pl@ñ:"N"¾sCR´ŽDòxö‰ñþÊ‹“&!kÛNœ9.޻ĴZnbi€›âËn±Ì·X3¹'Î~ #¶)ùÝ:v„[Ò$ß‹„ÞÏÁ×½ }VNñ±’ÃÔVÆ —3hü¤íŠBôž†e(l½ðð“£+öwm/V7‰ÂÅËbÄUHmûÔXìÿöΟ:Rü{'ûÒ¹ÿ#x¨»œ©tRb¥®¹<îŒüœvGâ¸øoaOÑòˆ·ÿÃ3ذ~¹|­&uý®oùºä¿UÑžž#…oqÌ _™N¬¢iøÊúUwm««+õÂ%Ñd/x• Ê~XФ¦CÌÓ°±ëƒ1?<“—mÆì~% 5l­, (@ P€ (@ P€÷X@Ü$œ©Ó+wÞÛiœ¡5]C!÷"6nØ Ÿ‰Ó1´µ5ô"Ÿ¸É¸¢éîóÅ*ër‰k±ÖD¹ïzŗϹÊQÃùzèrmD]UÜ/Vrϧäç[CcÚ¦"ž*ë«€0_ôS/Ö[±¯¤ýœR‡]ùŠU¾˜Ö¬Ê>Ö ]flœÄMðåpU¢XÊQ+b¢‹WuyÕ´_”§×‹`ƒ½¸¹¼¢_†²Mª ~Ó,¹âÚÈ¥p4MùßÓŒÊv5m+—¿n;ôz2ØI£ÕVÙ¶´c[ðU´-Ÿñh©€Q®.Sü®ËñB"dèì\»ë'Ζ¿¿Yʧu+Gjäê ÞöbÔKÿʬէê®m…u¥aûº¯pÇ¢X÷»Ì´r¢ÆÒ¡¾ê›À $Ðràt„Øg KÛòÃ}¨JK P€ (@ P€ (@{/`§³k%ßÉïvEʾ+ï¾×ŠÙVŠo9Wö›þ°©(8bÌ Ö«°3]³ÂFSf¤„1£É³X¤D~ÿ.Š­0UY_g؈~j+)«‚ìuÜ%¾È®Âª6…këp#·b)N·eá3(@ P€ (@ P€ @ƒ ˜9ŒÛòÙø(õ›6¦¢}¦Ç¹M P€¨HÀ 1=fÜgmm­üªv$ILLŒéùܦ(@ P€ (@ P€ @LƒÆm‘ÛòË*ãU5*”™)@ P€uäß"+ñǨ°åðT P€ (@ P€ (@ T(`úµ“q[>Ë/¥äãÊ•+puu¯ð\î¤(@ Ü jG’Ü‹F° P€ (@ P€ (@¦+` ‘ÛÆ@ÉÍ›7áêæÚt;ΞQ€ @£`¤Ñ_"6 (@ P€ (@ ˜¿€ipÄ8’$//O,Fbþ}c(@ PÀ|$1ßkÇ–S€ (@ P€ (@F+PÑ葲ùÚÊŠ3Á7ڋȆQ€°I,à"³‹ (@ P€ (@ P 1È ˆqI~~¾Ò$±|{chÛ@ P€*À ‰…^xv› (@ P€ (@ 4´@u£I”ú mº,Ÿ (P©ƒ$•Òð(@ P€ (@ P€ @]* ”ÈÑ$Æý…\”¤®Æ<Ÿ j/À Iííx&(@ P€ (@ P€ @ƈññµ|6>d DIV ’øL P€÷^€A’{oÎ)@ P€ (@ P€ €Å T(‘Ö…ÖgÁS€ @ã`¤ñ\ ¶„ (@ P€ (@ 49Š‚#rŸq$I>§Ûjrל¢(`N ’˜ÓÕb[)@ P€ (@ P€ € T(‘]±¶*4ñɠ(ÐT$i*W’ý (@ P€ (@ P€H@FŒÁ‘²Í2=–_Àé¶Êúð5(@ Ü;Iî5k¢(@ P€ (@ P€+` ˜˜H$†¸p{Sø¥×õöíÛHOOW²OîîîÊ£yóæ°²²j Ýd(@&(À I¼¨ì(@ P€ (@ P€hŒÆ@I©¶‰/×Õ¤ôÄXÄž¾€k:h[´†w‡^ðm¡æÔZå¹}9çR2áØ¢ z´k^«2jtRþmÄýzwmÑ¥W4·©ÑÙª2—î“—ãN"å.ТK/´«C…z½?GG#--­T;._¾¬¼öððÀ€þý¡ÑhJç P€A€A’ÆpØ P€ (@ P€ (`±Õ0ÈFÔúEX½?©œ¶ß³xgÞ8•;R÷Wö-ÇŠHðÐËø<Ä¿îVWBöE|ðöÛHùyu žéQ]`&G?}ŸÏÀ¸¹¯c|§êJõ)ØËÿù–¡¾¿‹úü««¯âܽ{„|–ÉÁÁžž°w°Gjj*233•àɃ1|Øp8::V\÷R€¸O ’Ü'xVK P€ (@ P€ ,E Â$E/¨f IVüŽâ‰ïˆÉáë‚‹Gv"òD*t1ŸàËØ~˜ÖÛ½Þ)Ýû>IÚTxölêÚX/•Û;ÃM$ƒ$¶ÂDMWãNˆ@p'×FUþÒ}J-©O|C¨¦¾Šú)G$mÚ´E@@?X[—¬3sêÔIœ;¯ä‘y‡VQ1ÜG Pྠ0HrßèY1(@ P€ (@ P€°<²+«ª×$±²2FQ<1þñ)(ã! EË•‹±ûb:®]H•¿+uÛ7|}± cÜ: ÁÓú#ü[Ø ûò,{ÿ[xöŸˆažññúhzõrÒЪ듘óTäìVñ»V㣗ÑnÄ <ÚüŠørÿr=Àª§lc>â÷‰/¶íÇUQ££;|‡NFðaƒ‘Ø=áøbgn*ÇÛbÔÓÓñèÀvâÜ R~:ölø›NÝá/ú_”MšÈÇíøöÅfÄ^ÍGœÐý¡'ñ§§~‡æù)Øþî¿°¹hpMäÇ‹p®Ë8Ì Œs{¶`랣¸zÓpŽ[_LyîY lç„ìL“>u.q7Ô—‹¸=_âK“sÛtŠi3GeƒTäH‘ôtÃ[20òÀþ°7n\GaaZµj…^bê°ëד•%2oZÚ xzzVÂ] îƒ$÷ǵR€ (@ P€ (@ ¨(Yš#ïÎ †·ßƒøXߢïÔ…ߢhŠ©üËxÆ+8lRžîD$Þšw¯®[„úTÄ'%‰Çšâ<ŽÙ¸Ÿ„¤øH\úC:Ù¤à»ÏCƺx¸@)'N$!¹Ã<%öÅo_Ž7¾Ã[³RÎÖÞŒSÄJ€D‹§­õ†WžôSŽG~°7ÊöOwÛŠ$O¿®ä_ò ’_þ(,Ô#ò#C€Ä{Œ¡¾õÿzÖ°öJÌ&Î膥a«1Éо"ê\:×ã2à$Fi<øô›X¹HôqZ¿¢2/#E8•ê“h“1IÿäÓqE/½ñô ¯cåûÇ??øµÓyåU|}îܹc,By¾};2@âìÜÇÓnÙ";;))% ºËsªºÞDHAIbÚ«¼kñb¤†LÞxjRX‹6{ ™†wºN@¾Úëߊq2b÷ÇÿÂn¹)Fw()ë7\»›O“uÕ³.QÖðøXHƒŽýëã˜<)ï"ŒK’"?Åkq_úô¢:t¸˜z…mlag¨çZÙbøß– SìO8z<Ë–$§’Ñ)62IŸdŒÄ¨"ÏoÙ½;!ó'á­ç§Zo þÝ0<9y¼lå¹¥ò{­¬¬0èÁÊíié©8~ì8rr²MN­õ1Q€h, ’4–+ÁvP€ (@ P€ (@ °*¨j¢“,ìYý~LÍFÇq³0mìÓ"ùbý‘mËÿmçDhâB:R å*$"éÒàØÚwõ€_@Ø#9bTFNAѨ ïþ袱†ˆ{ˆäˆ~A’Hú. : #†÷­h“•q¤…xι}[ž ’#쬌çŠm;Q–5 ¬£2²à¨i G½hO?thnÌlg8ÊòJ–ÛÆü&å9ÁYRd´%×x\Öi%ÊtÄ]Qn@{gÑ–´ÑÚ—*Ͼ@¾.ÄÑÕÿï1„kœ¼ýðà`G>bXéD¶Ó´O²=ŵˆc<ƒ÷ßèÈ}‘øá€˜¼K—„#‘›Ä#‹>\_Ù–Ò©™Ö7Rj§­­-nݼ­<~ûí7èõâb˜$yNÕ×Ü$37)@ ÜIî2« (@†HÕßÂ] ®f¥ºTq§V®Šç«Yi¸&ö§è3àîÐmœ<•Gk'ñÁÂd»h¿—SÙ¹‚¶í, (@ P€/`=(‡a§œÄ‹`HüßF÷·^ÅÀÖN°_´»VKßôg£uç6À±s"22 ¯¾åƒÄÌ¢¡õÕ”,Ï“œ©4g[m Œj€‘â!Ÿ;4óª4/P€ (@ P€u(µ^H¹âlÐç÷""ƒIxïï³'ñe}VI€ ×#ƒÐ§_{8m>‡¬ñì‚ v¹…#' #CþXÉš$"î!ë3†?àè‹Q½Ä”['‹*ŽNöEyŠ3‰×Ž06øHÌ…uà½EH è„ÄcE'õz=»öÅcÞÿÃ×bŠ­·fþ ƒ;àô‘cÊ §ø?¦uŠª¬½à1øZÌñÖ+¸hZžlJ¡y¢ˆ†^ó". €Kò1œTf À#OË6æAW4€æÀ{/âç¾ Wn‘½þyÇ´½®ÅÁ/vÉÒDÒúmÚ'Ó6‰íÜ ŒÓsÅÇ/Búcp¿~Di¿ õéäTbh(Pùéááwww¤§§ï•#Iä~™ävNNNñ1™W«úšgç(@{"À É=af% (PW„Ì«ø_Â~ü/q?¢SN‹5&ïîëZ¸ÉùrDJXünå!w÷v÷ÁFŠÇCèéÖÑ$'7)@ P€ (@ú‹W•»<Šå/ÚãƒÕ_@ήU qÂð§žÇ³rMøàß îâßK7Š¥@NâˆǧÍÇ”..ȃL”$ÖbW?.z)¾ÆGÀ˜1"H©ìyx¸?¬E{L?mhÄÂ…… Ÿû²Ö,Ç&QøÉ¢‰GÀcøëŸçS^}9+ÞÅ®si8vİfŠwÀ“xᙀ ‚.˜²x®/^Š#iYJyNÞ]à‘tòL1‰¼G¿€n­Á;_CÒIñ-ôè…™ófÀ[LïUXè"úˆýÿP‚Y—l0ù…ÇÿÎ×¢¿ñ…資·V$ÉoâzFÚÊ2D2ôɰ-º8ء˔1íæ±ñà9ùZ¥dòÆc/üÝíe}†=eö퀌ÂÝ»†ˆMvv6Μ9«d“ÛÆäèè™·ºëmÌÏg P€÷JÀªP¤{Uë¡(@ ÔD@B¶_ŒÂª›±ÿêqñêþþÉØ¢æú=‰?øŒ‚­µMMºÂ¼ (@ P€°(ùu“é#??rê%ùœ››‹¼¼<åùÒ¥Kèбj›l1‚D§×‰üpqo®L«Uúä|dÝÖAŸ'ÖÇ+XF£töZ¼ÊÏÎÂ-]6l5ÍÑÜ©üç‚ü,±‡>ms89”?^¶Ê¬Û7¡-uo^~:+%o~6nßÑ![4¯$O¾È#Mldu2¿lŸƒVUýeÛ#ÏÏçç‰Q Z1j§úˆ5åź#11Ç‘žV2¢Ä´\wwôëט&Œ‰ @#`¤‘]6‡ Ä]ZâßÖ ñFÌĦŸot$]š{ãµ¾ÓðÇ.0XÒè®D P€ (ÐL$r»ª I»víC“Ù†: È뜙™‰›7oЇ!Xâææ7778;‹ÅÚÅZ%L £ƒ$ñª°M ,X໤£XðóZK5 ÏnÌÝ];àýÿŒ)†‹Áð|Ãߘ¯ÛF P€ (poj$ißNýH’{Û ÖF P€– À ‰%\eö‘ €Äe\Äߎ¼‡o/ÿd­-ÝÄ!­zãç ‹î¥ð(@ P€ ,T &A’ví¼-T‰Ý¦(@Æ À Ic¸ l(@ ÈÊÓcɱOðöoáÈ-›i²¶²ÆŸ»OÄÒ3áæàl¦½`³)@ P€ (P?5 ’xs$Iý ³ P€¨•ƒ$µbãI (P¯ýŠç.ÅùÛW꣸FQFk'¬ú&vÒ(ÚÃFP€ (@ Pà~Ô$HÒΛÓmÝkÄ:)@ PÀ À (@ Pàž ääcQÌGø÷¯Ÿ£ °àž×/*œÙã1¬üW8Ú:Ü‹êX(@ P€ •@M‚$mÛ¶iTmgc(@ PÀ²l-«»ì-(@ Üo}žÜ³®ýR¯Mi¡qE¿ÝÐÖ©Úh=ÑÚÑCyn#Fv´vò„<žž}W³RqU—ŠkwÓ”g嵨w<5^9V_Z÷5ŽÞ8…­ü šyÕW±,‡ (@ P€MNÀÚÚºÉõ‰¢(@óàHó¹Vl)(@³ˆ¿•„q»æ#!ójûbkmƒßµòÃ8ïA×n0ðè+ñ¯.éÔÍ ø6é'eñøƒÉ¿ ;?·.Å)ç¶rtGĸÿ À³kËb (@ P€0šŒ$iÓ– ·›Ëue;)@ 4EIšâUeŸ(@ 4BÓ1ò›9HÎJ¯S뺺´ÃŒîbZ×qÊè:VÅÉ·rtøüÜwXwz;~M;WEÎê¹Øk±{üÛÔ²gõ™™ƒ (@ P€M@ &A’¶mÛ6³  (`® ’˜ë•c»)@ ˜‘À¥;×1dû,$éRjÝêžn±(àY<Ùi$¬­îípüo.ÆÇ6à甸Z·ßÝ¡9L|~n>µ.ƒ'R€ (@ PÀ\ÔIrrrpùòe´iÍ ‰¹\W¶“ @S`¤)^Uö‰ @#k Ý>go]®U«Úi[bIÿLó{σ#¦ .D!¶^8ˆG×Öº/mÄÚ(?<ºœ[›Ím P€ (@ 49šIÚ¶áûã&÷ ÀQ€0#IÌèb±© ÌM +O‘;çàh-F`hlìñRŸ§Úçp²Õ4š®çäáÝÿSF–dæfÕ¸]rº°]KÏå  (@ P€05A’ÜÜ\\ºt mÚ2Hb.וí¤(Ð$iŠW•}¢(Ð ðÄž…Ê苚6gH«ÞøhD(º¹´¯é©÷,ÿeÝ Ì<ôìº|¤Æuóêƒ=¿_ {k»ŸË(@ P€ (`5 ’´æt[æpIÙF P€MV€A’&{iÙ1 P€÷Wàõ˜°äØ'5j„XkdQÀt¼Úwä¶9¤÷OmÁ‹GÞ‡>?§FÍýs÷‰X7ìï5:‡™)@ P€ (`.ÕIòóóa\“$;;Û\ºÅvR€ @`¤ ^Tv‰ Àý OÜý²XÅ£PuS\ìµ½c½ª>§±düéÆ)LŠ\€ä¬ô5é£á¡x®Ûïkt3S€ (@ PÀª ’äååA>ät[ráöx0žcýc)@ PÀ¼¬¬¬ ÖÖÖ`ļ¯%[O P Ñ \Ñ¥ Ï–éHÓßRݶW|ø6ððU}Nc˘y£¿™‹ ™Éª›&×ZùyÒ‡èéÖQõ9ÌH P€ (@s0<ŒÏräˆ „ÈgcD>Ë Iß¾}‹$2?(@ P ¡ŒAåYüñá_Ÿ†gù ,D@Žy$âoØs%Zuå’ï'¼‡¾f 1vVJ†nŸkYiÆ]Õ>ËÀÐO“Ör}’j¥˜ (@ PÀœŒÁãsÙ ‰|-G’$%%!  8HbN}d[)@ PÀ¼Œ[óî[O P€I`]ÜöH46öØþÈ¿›D€D^ç6Ø=þm ßñ<2r4¿¤Åcé/Ÿáu± (@ P€ ,I@~9%“ñK*ÞÇkIWŸ}¥(pÿŠÿ‰?@Irÿ¯[@ PÀì®ßMG÷/ÿ¨:8`-fÿ£ãIŸ‘fß÷²Øí8ÆFÌGNAnÙC¾v°±Ã¯‚n.í+<Π(@ P€07ããsE#Iät[r$I¿~ý”‘$æÖG¶— Ì_@J8’Äü¯#{@ P Q,8ºVu€D6x±9Ñ$²oµî‹ÿ™‡‡Þ’/«MÙù¹xáð»Ø5nyµy™ (@ P€MQÀx7oSìûD P€[Àºq7­£(@sˆM?Oã¿UÝÔ í‡×žQß3þ¹ûD„tŸ ºéß^þ©FS•©.˜)@ P€ (@ P€ *`¤R (@µ‹c>FAaªìmµ-ðɈW`%þ5õôîƒ/ §[GÕÝ\ó‘ê¼ÌH P€ (@ P€ (PwIênÈ(@ X´@\ÆEl»pHµÁúa/ÃCã¢:¿9gt´uÀƇ^ƒ­µªn¾~r=& P€ (@ P€ (@{#À ɽqf- š¬À»'þ‡BñOMúc—G0®Ý 5Y›Lž~žÝð‚ßT÷gՉͪó2#(@ P€ (@ P€ @Ý$©›Ϧ(`Ñ™¹YøüÜwª šÙ9â­A³Tåmj™< /'wUÝÚyéG$éRTåe& P€ (@ P€ (@º 0HR7?žM PÀ¢¾LØ(Q“ähŠ6Nžj²6¹<ÎvNXØ÷YUýÊ+Èǧgw©ÊËL (@ P€ (@ P€u`¤n~<› €E „ÅïVÕ9Šd^oõSN©*ÔÌ2ý©ÛÕA¢ñßšYïØ\ P€ (@ P€ (`ž ’˜çuc«)@ Üw+bJ¨CɱªÚñŒïx¸;4W•·©fr°±Ã¬ž“Tuïì­Ë8–zVU^f¢(@ P€ (@ P€¨½ƒ$µ·ã™ ,Z`ÛÅC((,Pe0³Çcªò5õLr4‰µ­ªnn½pPU>f¢(@ P€ €åès1qÉ府—‘ˆ#Gâ +w„;Ê ”²ÊË@ì‘X¤æ•ÍÅ× ,G€A˹Öì)(@zøæÒaUå hѽÝ}Tåmê™Z;y`œ÷ UÝ” ¸3Q€ (@ P€&º“Xº¡sfcwRéoõ³/íÂÂ…spšQ°Š7KYeŸÇü…óökFÅ™¹— €0Hb™]¤(PßÙù¹8píUÅ>ÕùaUùîE&½^¼ÒŸ¥Š«ÍÓWr 8Gýl<ÝeŒª‚~M;‡ëwÓUåe& P€ (@ X„€˜ÂÖSéh&–¿ù RM;m×L¼ò‚é>nW(ààhbåÐ+Þ\†Iå>& P€–) nÎË´a¯)@ P £)§•§¯ähéÝS: /½ã~¼ÊˆÅ²çæco¦¡òÑsßEè„ :@IDAT%-IÞ‡ñÁ°bGü5%»bë÷í„\ŸDšªJ…(ÄÁk¿âIŸ‘Ueã1 P€ (@ X¦@B8Vl¥S|+ìjÜnüwÝgˆ:!§ær†Oÿ‡ñÂ˳ÑÃU‡‹_C¼ÿ8xÄ~†°¨d8;ûaꫳÑâäçX¥ä÷ œ‰×ç…«RºGÂ×àýð$‹Ï^~¢¬¿¢Ÿ—üZ-á‹_Å>LÄÛ‹'@«ä¯þGâî•X¶ßSˆ€Ïšq‚3†ÏÃ{¥`ù?× AÖã3/üóEôÑÄKs6`ðâ÷äoh‘¬wã¼yHö O1ùlSAÕI‡„ÕºpœweNpKär2äÌKÁÞ ŸÁ÷Åîèä*ú£KDø{ë¾7ò㓳—°yéULñ÷„ÒæÃ¢ iXZÔf¿ÑSñò‹S P ûÖ¿ƒÿ†G)çöœ 9Á°E-ºN©±;±âýpD'ˆëâì…Ñ“fà¯Ó†©pÓ#fËÇX·uäEç™ôo ƒã÷Ë0{Õ^Cfgñ¤ ƶ°i•Ü”Œ$ è• ð´åô…!Ãåôlqó†=AS."tÉÎâ5ºØ~?-¢!ï1‚ÏŒvÅÞhy/0ùÍ0ÌìŒC«_Ã’­EvwÃBÑCÅ­c£ÛôÞ+¦L”bËýˆN=]nwP€ (@ PÀÒ²²›cÔŒW°cë,}ý+ Z]ÊY$Ão¾·ƒ•#‘¹gåŸÄ{ü lZ¢LßÕç1cͼ¸á}ŒõŸíŒøñspòÌ`À ¬ O€Oð ¬æ¯”4ª'ÆL_ŽÏLÂâQ}ðñ¦0äÙº:)ªDÕ“³¨sƒ¡ÎQmpòÉ…põ,-ª§Mæ,<–\[_<äƒèðoqJ?Vý~v¯ˆO6ãû& «¸:=ö}.õÇ».FØœ0íÿË候²IÜD%>Þ QFÞËÀöç1}ÕyÈONE£Of­þS|ea#‘}h¶eŠ ˆÍñY„Á¸kç SÎ×× ãg¬R¶ef÷ºõbÛ+¶­„¿üÜô8ú¯œŽ%ÿÅa1¢cX]ÉÔ‰Ñ/>!Xµ8J7F¶ÁÑI E!%¡)çÑ :J©oÂÈ0sÒ|¬ßø=[:Ê^áðÅÒb¯Åã±jk8âžlاäá PÀ8’į:ûL P Ž±içU•0¤UoUù2¢Å0÷‹A؉ȫÑ_ ßyÖA~ÀgßÉ»’€¤Ÿ¾F¦Ïdt½»O ¯Ø„ÈÈmxq 8XåâŒZ…E"¤(z·å}D‰ £{Šwå⃌ÿàÁè÷ ¿lm’ì€Ì„3˜¸z"7¯€_B;ÿ»DûÞ ñÃÖ·¾#^Nc¥ÌZ·CiǬ{þƒˆÆ¨HC¼Ô¹ü–~^LºU¨¢Df¡(@ P€ €ådåˆ/åm{àåE@Âzüg_2l5†¯Á¥‚¶Ç4ìÚ&¾@ÿi V.[€à1“.îw*š` ÃQ¼† )ZßhÝCNW¥EkO ‘ɶ=Ä÷û"Ù‰Ù§~QnœÊŠÚ€y3gbæÌy˜÷æZyçO_?máêéOWùÕ}M’h„ó$ •A™4†Ä€:^‹Ÿ.b”ˆø¬#>ž ç„)âç ìý9U<'cox2¼‚¦ SÑébg)wDvçÀ'L‚¶âF1á&Ê&M,Ÿ±þØú,6®^&ú;EHäÍ]NEk½Ès&c¸ ):Y|¬’ñýå8Å)ptÉr¶†"XÜÔf¨©¨-“§$Êé¶0a‚ØÊÄ™‹EVüÔ#h%6…öÀžðõX¼`&ƈ‰á¶’üƒJ^hý1=PT~&Y„g€lÉ=¹´Ãȉ“Å‘ˆ½\åÊ’2¹E 4YIšì¥eÇ(@ 4Œ@nAÎgŠ;ªT¤þ-º«È¸~ëûa߯ÕX0÷eÈ·á€5†N DrØ÷âsAŽ~ÑS‡#ÿª]„ÇÅœ¸òíøØgçŠç“û‡ÄËŠ’˜[wã¼1˜³& /®{­ô‡‰¼’» §æ(Xöo÷]»¢¯xo=`HoññèäßK¼‡ÇZáañÁiÍŒ‰˜·l´A«:Öp·UEÕ›îëëÑÖVÕÿ ¾•£Ãµ¬4ÓS¹M P€ (@  x ›…Y~bÚ¤¥ÿÆÞÄ’¯Ìuq[0~Òt,\¾7ÐA/†ˆq%Æ/ë‹NÎ)ù o-Þ0îÏEÙ’S4ðíÞÝ»·Gûî#hòd jk’±î›†`BI»L[lë%¢±ÈHþ¢º q=ÕUZ?RòÛÀ¾’ó’±:x’˜òk)"/½FÍÄ‹!CLòÊHC38šì1næßMW6íeDÇ$‰ONêR™óÊž»qžš1«ÂOÂ¥ó(¼87¨TÅ®LNÎ2|c_àŸË6ÎÎà©L5Vª8¾ ,L úoh, „Ý¥(@ª.Ý¹Ž¼‚Š>=”>Ok«A'çÖ¥wVò*qç˜1ÿ}¤»÷Æs ÿ y_“|3íÚg’Œ½ßÇÄîd?<6 üøë¼»Õßõ“—|ÁbAÀÈVsź$aÛI¾Y®&‰@ˆ¡—ùJ[rr VJzî…v lõ ŒöLÅò…³ñçõ1Õj8ÜÌÎU®Õ’˜yMU™ÌD P€ (@ËÐ`Ê«oŠQá'°|©œVÊðyá|”œŠJL­´#KÅZ#ÆNÀqãÊ~I®LÛ¹—2ê|È̹˜-JŸ§<ž†ûíÛbˆG•Ã8T”^“,Z<2u4½ï¬#Y¼‚JF¡TQŒƒ>“¹ç {R’ÎFE‰â@™¤‹ý[EÆÉonFØÒP„EW;¹È»H¦ó™ö”ú©mÓM,=/VQç‹÷ç%ý1WqM†¶üTª-—ûEÉïå\ÕHœ ü²í ¦òŠÜ²óB‚0v¨¡>{;CdDöæçŸKê6޶Œ#“¬;9bo©ºÏÿò³8â…¾•9Ù”|üA X¦ƒ$–yÝÙk P€µHÒ¥¨:·só¶°ÿÔ¤ÌKbìˆßT<;aÜÒã”»¢”{zl;aªXqÕü¥H œ¢ ×¶”£S±éH’Xè= _,óÚj w奯bËÎ#efßÒá«—– 9p>‡fÙ:dˆØóT4ÌxWE÷Yå%oÇøñO#Þ¹+&„„âÍÉÎâM÷É2uW^Iu£N’t7*/„G(@ P€ (`鞃±d®q´ƒa,†$߈Abb,6.V¦ÛJÁ$9÷R%ÉøþßxX¹KÛ “åh•åÁX½3IIqزìe¬Ù»:Çfb½òD,›2cfnTýYÀX~MŸ½<&B?Ɉk%™:NÅ(ZŒ•̼´X¬½‘˜ˆ˜+1_¬±RQ²q4 9ùuÿ>%F'Ä„Y—«ž ž0U8%„ÏÇÊ-‡{d'N_e˜Kù@elËVlZ#qq8.Öš\#> еFFvª*H"!ò>·C{q(.ñ1;1ïÉ%JÙ qç•Ïv2H’&ꟓâÅ5z]ù\9dÊ(åT¥¿Âa¶pˆ•»Wc~˜pð›Œî*î¡SÎç P É ÜËw“EdÇ(@ X’À»7Uu×[ÛRU>™©ýÐ8o]Žñc–‹áÛ>ÊZáÿÙŽ§Äbì}Æ‹aÔ[W!hBÑܶ^£öff/œ.B%âýt1æ<«hº­«G±fU:ŒÜ‚~Æ7ºúó8"î†BÄRLCòÂ2±|qž¢½ÅOòÓ‘—³²  q_³¢;””×â)[ïGñfÐ!, žX”ÅGLãõxÉp㉕<·Sésãn5F*)Ÿ»)@ P€ (ÐT:‹÷ꦩӄ¼7 aòû|‘üŸx C"æcëòP1.]$ŸÑ˜<ä$¶F…cóÉñðûÊ%O8•-ÑÁ]N,%F«,]‡ô7!|UQyboàÜx\®Í¡Ë„2ÓW‹2sZ•+¿ìñµ¾ñ3KÑ!ùEéT&X3ä1qYôV'Lªî¦+¯Q/bYŠ¡ë×cN”¸ÁL$??gœ(²’¯õj|ÂÀ}X±söÊ#^Ø{#¢±jÎøgp™öÈ,ÅI8­Ü,{kÖ,Q>£á·7¢xõÙ–âãÍü5áX8G~šÿü‚ðï•¥(.Õ°!FÑ<Œm ðdŽ#ú(Ê>¨5‹ýÐbC^~8´j¡R·ÌÓ?øÍ¢)‘‹fðénç×cþ ƒƒ³üûSª©[–ÄD 4u«B‘šz'Ù? P€¨?OïÀŒCoU[à3]Ç㓯T›¯8Cžºlñ9A+ï Ûz[h5ÄòuqX¿.ãfÁ[dMÚ· Ó—zb[dÈýys[ªÝŽ©vcÁÏk±ì—Ϫͷ¤ßŸ°0àÙjó1(@ P€ “€üºÉô‘ŸŸ‚‚Èç¼¼¼â礤$ôëׯAš.ëÉË΃FùŒ‘]F6\µÊZƒµ©0O¯Ã}.4Í\QÑG•Ú”Y³stØ(Ö ëü"v-«ô#CŒl9ÝtMI‰vÍ;Àß×Ó°C|nɸ£/ßv} ‚'†âÁ›1Ûß0픡Ÿ@³"«<½^ŒÖ°…¦ªN‹Qþ[>ûÚ“0VY?RT«‹ÁÌI¡@ð»X;­GIÃäg(ÑјâÏ|ªú!®§^<Ä-k†¶ä‰Y²màjsVôa>ƬÞi¾¶â:ßM©kdp‹ó.ÂD;ô¢?â×°è³gI³¸E X®@ß>Y.{N P€Õ d‰7´jRs;ãýHjr‹X‡fŸ~Š­á˕۽ü±hÎ +ÌRU+ëÔGø A@‡’@OÙºZ ôÃ@/9}(@òuý?`ù¹‡ (p\½¼á*F’0Q€ (@ P€hê¾AK)–k¬Ï¤é4+׎­¿"µ0eöbñ¨¿"U•¤ñż•‹«ÈªÅØÐ•¨ÇžVQQ€æ(`mŽf›)@ Pàþ ØZ—YͰ’¦äȹb™*ÈQé£Ö»²z¸Ÿ (@ P€ (@ P rI*·á P€¨@ÀIÌ«&Ýέxñ@5çZBžL•>N6–ÀÁ>R€ (@ P€ (@û"À É}ag¥ ÌWÀÕ¾òy^M{•r7Ãô%·Ë¨õqup.s&_R€ (@ P€ (@ Ô—ƒ$õ%Ér(@ Xˆ@KG7U=MÒÝP•¯®™ôz} ŠÐ!>. ê&Ó##CWƒ²k–U­O kÍ fn P€ (@ P€ (@Õ ’¨¦bF P€ÞÚª Îß¾‚Bñ¯.).|ÆŒSü˜Wª¸ÔC+1qâǨhÌJò‘X½3¾TþŒ˜u˜ýj”ª ‰.v ž|ò½ Ë.Uh-_ÄßJRu¦ZoU…1(@ P€ (@ P€ @)ÛR¯ø‚ (P@ûf­ Ï+ȯ2§.OÄÌkðqnSe¾ÊêðóÎ|3 óú»B—-GŒ˜Lõ•ƒyK"¯`ØUPÈcÛ°5»fÓcߺ¾ºÅ«ªèuÐåÛ@«•{ò ÓeÃA«…üã¨íüV¯³…é8½.ù6Í ÕÔíÏçÜ»¸p'¹¸eUmø4¯­_U¥ò(@ P€ (@ P€ €¨Û·<4¤(@‹°³¶Egç¶8sëRµ}I9S‡ É $$;£‡Ý 9|­zôB'OãŸ-6þ5½f…Àsw&rË´$výLÌßš)ö†bÌél[mêX“0ú¹"vã<¼§ABt´r¦Ï@¸ÇF Zž¼¹é}ôNù³ÿå€maðÕÌ¿ ¡µ¢¢”üýg½‹¥Sz(Ûµùq<í, ª=ÕÅ^‹ÖNÕæc P€ (@ P€ (@Ú pº­Ú¹ñ, P€-ÐÛÝGUÿ£®ÿ¦*_…™2®# ™Xú>¾þ,3žšˆ•‡’”¬‰[^C˜ç„>ꬔœr§û‡¬Â›“á¸ÛÞZ‘#.b¼‚‚à-¶í%$.X½-›W„ !*ÿ†ÈÈÍñKÀûÛä4]¢Üd%jd%#*{,6GFbÓŠ`D¯ÙˆDu ›”k›Ü•¬Î¥·{gX‰L (@ P€ (@ P€ #`¼%·aJg© (Ð$ú·èŽÿ%o{¯ÄT›§² yvÞX¶l5zõóU¦ÇŠß2³—|‹§7xaÆš,¼»c”˜!+F„;*JÈQ€ ´¶òO]ÂÃt˜¾É0ú#7' ÎAÁWféÚ^âßÀ¼”‚üûz!üNé±)9býöÉÓÆ¦Þê:@äÞ†ôl S-ÿŠî½ªÎ¥¿gw¥MüA P€ (@ P€ (Ð0µüz§aÃR)@ PÀ<~×ÊOUCOÜLÀE±öF‡f†„ª“Œ™rsa×Ü­xýN½ûŠ#wpnûÇâ9s&Ž1æÄ“câEÐd%z/6‚RSpéb·#Êk*B=‹OEJBr‹–XÉ-?0Åp’hOцx’ѕڥÌÜ,JþUÕɶê¥*3Q€ (@ P€ (@ ÔN€ÓmÕÎgQ€°h-zÂÉÖ$"Q…ÆÖ «8Zù¡ìK»0ö ìKÃ8òRñíÇa€_{ôy. ›7omؼa¼œ±nóRø–mŽþ‘y;:± û÷¶"ðùQÅ—Êkmø#ß\:Œì|cÀ¥òúä4[#Z?Py¡(@ P€ (@ P€¨³G’Ô™P€°<; oÝß^þ©ÚÎqn^ðûCµùÊfÐú?‡EAñX2}–ʃΣ±âã h&ƒ!Æ€ˆÆNZg´tÕ ì´öCƒ€ùK1élp¹?Ö­t-®Â®x˸á'Ãv©cΆ}öâ©ô“’üÆÔ>o:¿GUÖ>]ÐÊÑ]U^f¢(@ P€ €¥ äe$â×Óér ’¥Þþ¾¦CË+ÈÆ] (`‘V…"YdÏÙi P€¨“Àû§¶àÿF­TUÆoO| ?7u‹½—+0OXÿC«5FFÊå¨z‡^‡ ½ \E å~§ä¬t´ßô8r ª_õýÕ¾Óðþ¾ßMfý (@ P€¨•€üºÉô‘ŸŸ‚‚Èç¼¼¼â礤$ôëׯVu˜ž¤‹]Ió·šî*³=›#gÖ,s„/)@ PÀ²ÊÞxkÙì=(@ ¨˜Ôaæü¸ …ÕžóÁ©¯ñß!óªÍWa1­—¶.­4Z4‚øˆÒµÎìT ‘™§tQ!wR€ (@ P€å´=ŸÃ¶mψ6°¹û æ>µY“ßć3@~¶\€Ð¦+ –¯{(@ P épM’¦s-Ù P€÷T ­¶†zõVUç§ñ»ž}[UÞ¦šI®C²úTUw¶•ô¼«K;xv-ÙÁ- P€ (@ P jys•V«Œ@×4sRò:Ù;Ac{;ÿñ¼¶é±ZaIŠÛ² Sf®Dâ­x,›Œ+WbAðŒ3S‚ç!üHRIfèq$|%‚§ŒQŽÏ[‰˜dci©_<3ïë!2Q€ €9 0HbŽWm¦(ÐH¦ùŽSÕ’;¹wñΉͪò6ÕLrÉÕ¬TUÝ ö«*3Q€ (@ P€• d)‡\ÑÆE‡ááˆ×ó&ãÛ5{‘)ÖYìäp‰ ÉˆŽˆ@Rç@ÌŒnºX¿p:Âã a˜õs±p}œ!$8º ^h(OwáQ HˆMF®±x>S€ €Y 0HbV—‹¥(иþà3 Îv†»´ªkÙÊßÂq-+­ºlMòxfnÞ<þ‰ª¾ÙXYãÙ®ªò2(@ P€ (P€-M™NàÛ_ 7-éD„Ø4¨ødçÑ ¶8&LÃÒ°+*®ßø=ôúX¬ O€Oð ¬ AдyزáEq4Ÿÿ(F›hûàãMaØôñT®wR¬É P€æ%P—YÞÍ«§l-(@ Ô»€ ü±Ë#ø n[µeËÑ$ÿi5ÂF.¬6oS˰äØ'‹¶«IÚÿÍópôèQ±Ø¼+\\\”‡FsÿžWÓ~æ¡(@ P€ @cÐtŽ ç5ÿúæ ž€³{å4¸£ñH-ÄLZJ , ˜@ëéÎXx(i‰éH9¼¢6`^T²à$þ]TÎ9ú:0Ê®ž^†BTÿÔ!|æ$¬—%Ÿ „±œyç‹pŽ1ù`ŶµðÍd¢(@†`¤álY2(@‹øk¯Ç±6îkŠÕ¥ÏÏEbª˜Jj¬÷Àê²6™ã1©gÄTc_ªîÏ\¿'ѼysøùùáÖ­[¸yó&.\¸+++%h"'òaoo¯ºLf¤(@ P€ €e xbÜsC¾jâtýñCx²ò Œ¡ eZ.»ÒBNÎ22a»¢9´’S4xpX{d+Ù|Ñ«œ j[ú$Õ¯lÐel0‚îÈ€œœøŒhˆõS†¡oÑ{ýœœfp·Q](3R€ @-$©%O£(@ƒ@O·Ž˜Ôq¶^8X-‰ ¤üéà2Ä>þ ÜšW›ßÜ3ÜÍËÆ3ûÿ‰¼‚|U]ܲF¶ Pò:9‰;ÔÄ£uëÖÊ묬,%h’––†„„ØÚÚ– šÈ×L (@ P€ @ÅÞ#'ÂgU(Öýg•©áŒô(Î('þùçóéá_´/{E Eι¥íÜK ¦tž9³ÇÃ*زìÀ®¶ïÁ5è7eú·À¸áŠ)!!Æ|¦(@{$À5Iî4«¡(Д÷{Öb- 5éŠ.Ïîÿ—ª‘'jÊkÌyæ^…“7U7qIÿ?Uš×0éÑ£ ùìèèˆëׯ+Ss?~\q’‘‘‚‚‚JËá P€ (@ X¤€X;$h´3NDE~AdŒw $I›•; 1)^@^WÖ,2e´Ú^˜ìD-Æê1HJŠÇ_Æš½{¡slä%bÙ”13s# ˼[¤.;M PÀ¬jò6ëN³ñ (P¿þî1ML£õÉÙ]ª Þq) ÿ<¾¯õ}FU~sÌ´þôN|xz‡ê¦k7cÚP_«ÕŠlZ´mÛ………ÈÌÌ,žšKŽ:‘Sv¹¹¹)`a¢(@ P€ €% (ï€KÍPk‹ACD8eL—ùP¦ÛòòáU •àˆtêü&BÇz+dS–®Cú‹Ät]¡«™È8w÷ëê2‘˜)v´pPöó(@ ˜Ÿ€•øb¥úIäͯ_l1(@ Üc¹0y÷ÍOãVŽºû§äÈ“ðÑoà‰NÝã–6|uû¯Ç؈ùÈ)(šÀ¸š*í­íûÄ'èæ"æ!®‡”——9¢D>äš&òO½1`"×3áÔ\õ€Ì"(@ P€ ªïAMùùùʈgù,߯Ÿ“’’Я_ù‰§ª,¼–·,ÀŒ5z¼»c%zˆø†’ô±‹¥Y½CÜøe ]ÆØ4s…¦‚ÛŠóô:ÜÑçBSÉñZ6‹§Q€ À}¨àù÷¹E¬ž ÌRÀËÉËþ³~X¡ªý…˜¶ÿhåè†a^bÕÃ&’~KOÀ”ÈWUHd·<0µÞ$²<ñôôTòõÝ»w•`Irr2Ξ=‹fÍÄîîʃ£L¤(@ P€ @SÈKÁ_FbÛÖh8O^V ‘.Z>ðÎm½xá ­¸©¨²d«ÑÂÕ\©,÷S€ €Ù ¨›@ÞìºÅS€ Àý˜Ùã1<ܶ¿êªåÂæîÅ/iñªÏiÌ3¯aÜ®ù¸™-ÇÛ«Kxøâ•¾Áê2×2—\»¤M›6ðóóSÖ3ñööFvv6Nž<©¬grîÜ9%ˆÂµLj ÌÓ(@ P€ µ@ÞÍ‹"@²-†ãÝeF­Ø8"Ào:ˆõE˜(@ PÀ"8Ý–E^vvš @à $‰…ÙØ2iú[ª+i¡qEdàJôñè¢úœÆ–QHF}32“U7ÍÑÖ?Oú½Ü:©>§¾3ÊõKÒÓÓ•Ç;w §ãòððPF™ØÙÙÕwu, (@ PÀBL§Ú’Ûaº- ¡g7)@ P † ’ÔŒÙ)@ P z—~TFˆˆYˆ«Ï\”ÃÕ¾™²FÉ#ÞUŸÓX2M‰Ã¤ïàZVZš´~øËøS· 5:§!3˹¡eÀ$--MY"§å’ùÐh8¯@CÚ³l P€ (ÐÔ$ijW”ý¡(Ðtl‹Ôt»ÇžQ€ ÀýèêÒNLí[€ƒ×~Q]½>?›Îï\Ð}¨—?¬¬¬TŸ{?3®>µOí[ŒŒœ;5jFH÷ x=à¹ÓЙ­­­¡ÕjÑ¢E ´mÛööö¸uë‘’’¢,°)÷q„IC_ –O P€ šž€iÐDNó*_ËçÛ·o+SÃ6½³G (`.Ib.WŠí¤(`fraöÇ÷¼†mÕ¸å2HòÑðPÈ`KcMrZ±™‡þƒˆË‡kÜD¹P½œ^ÌÁÆ<¦³’`e°$55Ue"ƒ$raxL‘ë0Q€ (@ P ¬€iPDnsº­²B|M P€E€A’Ær%Ø P€MP@—§ÇÈs𳘎ª¦Icc¿÷y¡}¦B®ÝÑXRnAÞ;ùÞ8¶·st5n– üD=ºž—ŸÛXNwûÉ‘%2h"&2X"œ’«±\!¶ƒ (@ ÜIîÿ5` (@ P@ƒ$ꜘ‹ j)pãîM Ûñ<ÎÞº\«Ú7k…%ýþ„`ß±ÊT\µ*¤žNÚzá ]‹3·.ÕªÄÖNøáÑÕðqnS«óãIr„‰1`"ƒ$Æ€‰œ–‹‰ (@ PÀr$±ÜkÏžS€07IÌ튱½ ÌPàâd Ý>rŠªÚ¦^n°(àY<Ñé¡{,‘Sj½³röÚ&7g˜ð_ôv÷©mú<ù!XLnܸ¡LÉe\ÛDMlmmuÛÙ8 P€ (@ú`¤þMY"(@ 4Œƒ$ ãÊR)@ P ŒÀ錋ùÍ$g¥—9R³—Ý\ÚcFG1Íw\ƒNYuKL¥µé|$ÖÅmÇñ´øš5²LîæöZ|7þm jٳ̑¦ùR.ÀyóæMe„Izz:\\\вeKxxx@.ÏD P€ (Ðô$iúט=¤(ÐT$i*W’ý (`ñ·’0v×ߘy­Î­µ³¶ÅïZùaœ÷ Œo7þa%þÕ%É@ήË?áÛ¤ŸpðÚ/ÐççÔ¥8åÜVŽîøfÜ[èçÙ­Îe™crδ´4e„Iff¦(‘8±²ªÛõ2G¶™ (@ XŠƒ$–r¥ÙO P€æ/À ‰ù_Cö€ €Y È5JžÜ»P!~­×v·ttSmµžhãdxÈ5@”m±¯…ÆéÙ·q5+ Wu©âÙð¸&^_Ó€ÉÑ"ò¹>Ó¾Ø:æ_èèìUŸÅšmY999Êè9%—Ü–Á’V­ZÁÉÉÉlûĆS€ (@ T,À IÅ.ÜK P€O€A’ÆwMØ" P€M^ · ‹b>Â[¿~‚‚&ÙßÝÅÊÿ '[M“ì_];•••¥Œ.‘;;;%X"×/‘ÛL (@ P€æ/À ‰ù_Cö€ €¥0Hb)Wšý¤(Ј)­ž;° ™Waëj×$/'w¬úí0´vXàY¸~ý:Œë—xyyÁÍÍÓqYàï»L P€ @Ó`¤é\Kö„ @S`¤©_aö @#ÐåéñFÌÇxçÄfÈ&暬­¬Òm– ü ܜ͵÷µÝrý’””%`¢×ë9×}½¬œ (@ ÔM€A’ºùñl P€¸w ’Ü;kÖD P€Uœºy;òv'­"Wã<$çÁ9ТGãl ¶êîÝ»HNNV¦äÒh4£Kät\ÖÖÖfØ6™ (@ Xžƒ$–wÍÙc P€æ*À ‰¹^9¶› @A’G?PRoì]ìæÒÿðg<Þi¬Ä?¦ú®å4\2`rûöm%PÒºukhµÚú¯Œ%R€ (@ Ô›ƒ$õFÉ‚(@ P $i``O P€5(D!¾J<€%Ç6à·ô„šÐÀgtnÞ¯õ}S»<[k›®Å²³³•©¸dÀÄÞÞ2XÂÑ%F>S€ (@Æ%À Iãºl (@ T.À Iå6Ö~n>xÂç!<Ñé!ôrëÔU±ìZÈÞåÈùSpµiÓîîî°²âôgµàä) (@ P Þ$©7JD P€ ,À I³x P€¨«Y©øþê1ìS1¸™\o•´qòĨ6)£ÚôCGg¯z+›5œ@AARSSqõêUäää(Á¹Ø»­­mÃUÊ’)@ P€ *¨s$/±Ñ§‘kgW®Žæ­:Ã×ÛµÜ~‹ÝQd•eç„î}üÑìN"~=}péˆ~=êéóŒ±Ø¡U÷>h‡dQGàä>þÞ¨¯w݉q8}ýœZu‡'^c‹ýfÇ)p$¹Ç଎ ê_àÆÝ›¸"'×Äãª. 2ˆ"ײжu©HÕß‚»Æ­=ÐFë  ‘ÖNâµÜ.Ú'_3™·À;w”`IZZš²f‰]âäädÞbë)@ P€ €™ Ô9H¢‹Að¤PñU|ÅÉ+p>œ7 šŠ[Ö^«É+6ã|Ió· ƒÉØ9U‡ô8´ú ¬;œŽÉ‹ÞÆ_mÅv¢Ž)âzdŠ£Ë6c†ÝÿDá€s6o ©¦ŽŠ‹Ê×»z æoµ.Cä¼~•Èý êU ¾½õÚ(F P€¨‰@nzú¶õE_ßšœÆ¼MT Y³fèÚµ«2¢D®[ ggg´mÛ®®UDl¢$ì(@ P€0?;xŠV+AŸ!˜Ü§¥x•ø=8!¾COŽXН' BPe_ê›_kßb‡æÅVö¢‡–ƒÜ pï¥*ˆtõ×h1}-p'Ϧò68´Âs!ÁHÏn†;‹²¯ÉšDjᬪCæò?ËÖÝrÐL7»ŽV´-Ÿ™{(@ 4ƒ$ Ëb)@ P áäKû÷ïGJJ ‚‚‚¾BÖ`VöööèСÚµk‡7nàüùóÊZ%ÞÞÞÊ®[bV—“¥(@ üöÎ.ªëÚÿ?Á‰¨¨ã;@P ˆ/4ZbŒ«RL„ÄNíulñR¤‘pÑà&&D“BZ-ÁÜ@ôrcëØ¦SS0•)bš*QÐH0¨ˆJtTDÌí330À¢®íg˜óص¿ûœñœ½öZ‹ <À”"±ÊÝh3²Úk—F „xTݬ3PÑ–BýîN¤9 -6ñ)¬ [ƒr1í¥G~ê°kÿg8{EœuÄxï…ݸ îveHÚ´ '1Ó<Bö¾ƒ¸b,ÿBÄx;§ÍteHþ )ó¥ú‡MDÀÊ5PÌ5Ä,,MClòe,zWÿ•Œƒ§©ÇaðŽDÈ‘§ $à òR?ÀN5µO"::ŽGÀšõ õSM“¾©ïnÃY§éø0Ìš5MrÔ ´¤´cP/Ž ùßCÊ1âCÊGù0LƺgÆÇonD’€Iiß;a(œ„Ë€-±Éá«ÀÂ%xg«ÎÊ70÷ê”\ÖaÄœEe(Ôâ¯Idõ‘v„Ø8ÂÛ?CÐ`àr “–n@x€XÐ&,Gb±çÄu, Ú¿¼Þ¢í•SΣ°°·F̼E#z¦ÿ;÷Ðøiêï<ž§6Äò']i:Þˆ=»)³áÝ¿)i†1š8w5^褅‹h—`V’DèlaOcžtHsâ³MǪ™iÔrÕ^.Ef6É"÷Àó¡:¤F#QhQd® (((AZÒ&œ»ýj²;VE}ÈÅ%è wm”Ä4pÕVhP“–))§Æ¬x šÄ4ÙP‘×-CªB¶:ÅUÈŒ.5š›Æóõ(=œjRsý"ŽZh{ùУÈÍ-A™Û „P©BõF¬O2SUQ©Ì“•ØDZ’KÈ-¡óôi@BårÓâñ¶»'b Š,£üذH ¯Å£| 0&À˜@7& ÓéðüCšìb …‰^¯ïƳhÝ…ÀàÁƒ1yòd<úè£ÐÒJ´£GJ/´µµµÝED–ƒ 0&À˜`L uÄr,\¸Pú¬ˆH’âbÀ' +¼Qz`§a"_îû3h!• R¬òìJ/Â…‚ÆÚ\±æõ8¨öÆÂßÇ>ndhñÅ7ž•ù†a-ÂÚ»}dÒ±Ld~S‰ò,•QA"úí{©þýˆòw5äØš„"é5ÄX ɰ/ûw†ëРà\e›2\ÿþ~+)HLõg`[ˆTÊoU-c²hObQà»n»´p,aƒŸ±Æ/“ƒvmÉ–¤¢ÜpÜõ©•ˆ‰Û]Q øøxaœlvdì…ÒÐ((žIÆŽU’HC…®Ø²m;¶¯pi`ÕpθµKâòZ ¡"zò…^ËXÂÄX±3í ð´Øöó2ú"ì4*H¼”±ø„ÆGµÅèA  ©ER#¢ZJ2„lûŸ¨ 0ö§B#"¨pbL€ ´O oûY8`L€ 0îC@åþûßÿ.¹O2—ª¦¦Æ|—·™@›DÜ’‰'búôéR¾cÇŽáÌ™3’â¤Í‚|’ 0&À˜`L {°³…ˆ qé\™Aí1Ć)±víÆ«³gÎc„×£¼%ˆ\NŠ–Õ;Q;v&V¿´ cü«È°6d±¤pòðÇJ²¶éèWçp¡ô”aÇg5žöžì± x%$= .â†ÙZ×…‹!B¤Ø»Ì†¿!ƒTvT28_-3(}(gÊ;/C¹v-~—DV "UÃ÷ÂC˜YÒŸ4*N|ñ‹§=¤3î‹ð5ËÓt³FBH¤’”ÍXJʦ¨ýeðð]‰5’ ,[ØN[øëШ߇b¶·Æ ´-$Y ~º` °ÇÜŸ¹Ô Nòóe!“CmµMFBçO݈ɡxvÄpÉg¯€Òȶô¼Qû#ê”=?ogÀFŽy Œؤ-ÞaL€ ´N€•$­³á3L€ 0&ÐÍEÈÇŒÊÊÊ’±’¤>`;;;¸ººbæÌ™0`¹(ÀÉ“'qýúu+Js&À˜`L€ 0{A@KdE ¬8¶­3ª²wàß=®_6jª4pp Ir aîãë __LD ÎÅÞ„-Púû&ð«J™’ˆÐåá(lPp8bÐ@“ÆÄN# ¶$5·„%†ÁôA6|4I/õ×~e«Š[Æ)™ÉbBHÔ† §o›V‘üRvg/’ß^>Ó-·c¶&‘©½A‘M•™}ÛcQô~lZ?£¥G¹ÈŠÄ²¨TŠÒ˜ú7øÐ2sõÅ$)fJcž[Ž©uc2sƒlfÌc¼“ÖW¯˜27ýnÑ6Öƒ@JM5E&k”ë•&7^”iX£ú†q5ç/&À˜@;XIÒ >͘`݇€Ôîææc¢yºyÓì¹ùIi_ƒÝáA’‰~B~K%‹Å"|ð!`cc#xÊ’¡C‡¢¨¨'Nœ@EEÅÀ;ʘ`L€ 0nK A`OÁƒ`˜ê¯¢ âÕç1Þ 6¹ßú]\vìˆCØR 4¾þ³q!5o$¨q‹Î«HѲ/Áä ‹bX™Þ!4P§çêÑ~ƒý™7Mž‡Ã~È éxUZ2òsõ¥“¥ØâDe€¡†‹ÚáÈ÷¦ìäìÍ8ÄíØ¸×WÁ•”2ž3Ÿ,SL9Ä·lˆq7ò –ÚÂÏÖšg)=§ŠÂÎ žy})š>A¬ÒàÎ eM”$W¯7«Äc}Í>|Zjp}\úEFƒ{0“ºH{å‚qRy7kÂØ´h›Nؤa¬}mŒ¢9†Tc†ðñ@ÞT3&À:N AçÜñ¢\‚ 0&À˜À½% \$Íž=›üçúP0¿\)&‰i»]K’ÒÏ *6$¨ðøØ÷Vpn­Ç0y1b®^½Š³gÏ¢´´TR  6 }úôé1}aA™`L€ 0&Ð[ ˜iÀcÞRÈ(nEUn<–¬Í†ß dRào‘”‹}Å))PyAA(Ê• 8_8dtoåŠYîpÒ© )ÊC¤~¹Rbœè—ãÉÉr¸Ø+àEQÒ)´;"”káë dgê—®…EA¡±ŽÖ¾Ú’áG~OÃáï*¨(Pzä2%|ýÜŸ™-É(óŸ#/7¯×Æåqr7é Ô‘kPì;§EöÖ’=n&é)üú_‡2À%§ ¹ìHÉS‡*£‚!m³Y›å­Uféxâ×#Û×¹ÙôÂ%’ÏRx—Rê27cá2u¡ ëMS˶_7hÀ ÙEmÍ&Wd›”("ëšòìlãøøaðmVÔ´FÞcL€ t†@Ë¥¸©…Ë0&À˜è"š¼td!K‡¨èX¨¡²¼»ãb—„C…ܾq_ü+3`ÅŠ˜5k®ŸÎAjN™E)ôš|ìVçHçŽþ†^8Ê‘®V#Oc2.×£ðPª´¯/ÏGjzŠò’¸¤dU6…×äBBl4¢cžgzì·Ø,ìÁ„2ÄÙÙÓ¦M“Üqi4I1wñâE‹&NL€ 0&À˜`÷Ž€ÌÌìÝ0Û8‘.b†@¾l 3X”ä$2ø‡mÇ*Š!â±ê-„ù{‘°UÈT%A)®PnÙï†`2RNø@CŠ C¬o9Bbã0[ Aâ˜]¯ÁWÄÁ W]&‰O`ÞÑ‚ƒ¬¥]‰ð4Õ¦ öNXõû(¼Dd$®¾!Hxqv“ú ;”ÿÝmðÙ©O¹BAâêeŒbæÙJœ–´IÎXCîÆ$ù5¹P%©M”¹úcÛ[ÏR',^£0š§‹Í¬K̵Q¢>c2 i?årzV6)H(Ð{Âë¡„úý죜´C ¹“Ä6Ud¡m“§,ÉE— ½ú!Öù¼À¨ ‘dÿpƒYLªÐLV;Ñ:¥7_†]þ˘h@Ÿ(µv’3&À˜¸× w+±^,‹ò „bxÔi†ÕH> ?Ÿ†´\GD½ŒÄc0cˇˆœíL"–!ja0µ 1RÐÀ¦Rë5YبÜ,ýsõQ rã,D("ðÔ¶½õo>Z$,\†ÚؽXãð–­WÓ1W*gâü>5r‡…`ÿ*³â Üœ¹¯ ‡C•’ Ÿ°ÄH›¶É{½À7pþüyTWWc̘1Ëåèׯ•–½¯ûÜ#&À˜`L€ tˆ€˜n2ÿÔ××K‹MÄ·^¯‡é»¬¬ 3f4U6t¨¡†Ìzh+«q“Ö7 tv’&éN‰ ½ÚjêHá2ÐÑÑ_D—ð¥ôžà…íŸÄÁC_‰òêVÊS:m%ªÉC×''8vÆ7‹%Ì„ÔSý•Ô©~ûöÐV–ã&Eqv"‹ +’N§…NW[ûëד|";y¢í`ì+[•ÅpζM9Ûk[O²W|›áähT‚tPJÎΘh@‡öZ«ˆ3&À˜è"6ŸWöLjÕGäô7kŽ*¶#FáA/6¾8¶d=rN;bõ“ÀïÕŸcÃì è !—^l~4Æ¢6ò¹ø]B–„¦â¥-!pÑçÓŠ©¦ ‹ú‹@‡Ò‚(±ÜH†¨w`é_ôsúS¹C8£ ÀW;Ó ÜU¨aE×y,¶'?¾î-_Â,JÂ{2‡zžžžÐjµ8w0=z4FÅÊ’ž<°,;`L€ 0&ÐKØÀQ(/Zë ¹rtj6¹^²1©5„ÁÑ ÎͲH§ìé¼}« ˜çleÛ’ fYmDû¨ßÑɹõþšÕkÚ´'áíÛèŸ É×¹$Ø·.K›ãbl°½¶mHvç;‚ß¹žq)&À ¬$y0Æ™{ɘè1D€?™ç¸¥ƒPYLñg__aþ¨«¾?[‹ß?¿ßè‚Pw@øm{Ïôµzƒávø¢ÅÿfÖØRÝ¢¡Ÿ1üõÇcÂ@…R­äi«uµp\øÏŸl8A݃"±£a7Ž´òÐÃÃ"ް,9zô¨¤(Êž`L€ 0&À˜@!`7ÁëÖ¡²ÿ0Œop½ÕCdg1™`L Ëð›|—¡äŠ˜`L Ë´Ru†M¡:1$¡³ðìÜfB!Ûä´T Ù9Õ”¥ýoZ1&Å&lðm| %CвŠ¡:Û~6’⦼œìëëµ* ÓñQ¡3~4Ã`®ß~Ëœ£pppÀĉÉeN²,ÉÍÍÅÈ‘#!¬KXYÒ‹š»Â˜`L€ ô^6r, ê½ýãž1&À˜€U8p»U˜8`L€ tB»?p°0ópBÀ¯ýl™‹\Ú0#i.¼ã(L§c' ‹¡Ók‘³{²›çiØ7ªilÆb¡/¹yx'_¾Lþýõ[¡>Sà ’Væ†=ù,˜0a¦NŠ[·nIÞ¿ûî;É×öƒI„{͘`L€ 0&À˜`L ç`K’ž3V,)`Là …1ëi£,r³U{ ·éܰáäòùSxõ¥'}9@IDATL YíG*“¶S? Nؘœ±<ÊÁ1XšHGå^7ž!IR?û!´í ²$™±1§.†"R¹ÌpÞ5 /ÎmÈË6¡,qwwÇØ±c%7\lYÒ5×CÝm=.Ö\Å…šò†ï Z³mãq»~ý1ÊÁ#†Jߣķ£ù¾3† pBúlj 0&À˜`L€ 0&À˜€‰@Ÿ(™vø› 0&À˜@w& ‚e×ÕÕÁÍÍMS_–Š%ÁjÄîSa8Ô“uGfÎ9cv³ž8ŒÃ¼¹ qNÎèuÐRŒGÇX¡PaN‹úú~.×Ð.o<Ln¸***¤˜% W¿~‡S›nݮ×—¿Á§ŽÓç_.@m}£Ë½6 ·sr¨ý <9r*暎ù#§ÃcðxVš´ÃŒO3&À˜è,1Ýdþ©¯¯ÇíÛ·é9º^²¸5}—••aÆŒm†Ë1&À˜¸clIrǹ&À˜¸W._¾,Å€´P‡+‘TP™ÿIAb¡u·´´)ìOLélmí[*HÄi{8vâB{{ÒÈpbí0¹á2)KD€w¡(ÞYYÒ^ ),?9ÿ%þVú/¤žû7ªê¤¨AM3uÁÞUÝujã3é#ª?PŽ —'ð¬Ë“˜3Ü}û°'Ú.ÀÌU0&ÐEhzÕu7q­¶Êð¹U…ŠÚÒvem5ìmúcpÛ™}Œû¶};ñ€ÓErs5L€ 0&À˜èiØ’¤§Ë˘x@ TWWãÔ©Sðññ!z”æ}Ž35Îx|®·1Œú †»ÝcÔÔÔHÞ¯_¿.¹ä’ËåèÛ÷Áž”ÿöFâ >Â!)8ïßpºÊF!ôÑ@üjÒR<ÔŸ¡÷o$¸e&ðàÊìK_#KsG¯œB^ùéNý6 ·‚îƒÆ`æ0Ìá‰'äSá9øaV?¸—Ö}ë¹¹‰ØfK’û6Ü0`L€ ´C€•$íâÓL€ 0&Ð=”””H«ïÇß=b)˜@' hµZœ={Bñ7nÜ8Œ1}úÀÎ|ýíúû&‡5 ‹ Ĩ)+ñ‚gì)(<'&À˜@W(¾ñ=þHt¤ßÂsÕ—ºªZ«ë±éÛ‹Ç<†à‰þ÷#ôïkkuYÎÈ:BàN•$yyyiŽó2&À˜°Š€¥8X¬$± gbL€ 0ûI@¸'*..ÆôéÓï§Ü6¸+®]»&)K„5‰P–899Ý•vîg¥ZŠ9òÛÂÖü¿@ÄéIéa™oÏZ‡å®ó9È{O8–• tCÂ}Ö›Çÿˆ¿û‚¢üÐ-$åàŒ Þ+°Öãi8P¬6NL + Ü©’¤+eẘ`L€ ´E€•$mÑásL€ 0&Ð-|ûí·A°ÇŒÓ-äa!˜ÀÝ påÊÉ —¸Ö]\\àèØ;âb||ö ¬ÿ÷;÷eµtWŽÓ‚QÓ‘ðx&וÕr]L€ <N\ý¯åýöŸÍî6Ê‘æØG ‚¨©+IYò [Ï5‡Ãû&ÀJ’N£ã‚L€ 0&p °’äçæ˜`L cÄË•pµ5uêTIQÒ±Òœ› ô,âz¿xñ"Ο?/Y”Ë;;»žÕ £´Wt•x!û÷Ø[òi”ß’Ðvýl=}56Nù9úõék) cL€ 4®´^Íý_ü‰â/‰ø#=!(Ç[3…nOq ÷ž0`Ý\FV’tóbñ˜`L +IPð`L€ tG‘r-”$œ˜ÀƒB ¾¾eee’ÂDv;v,lllzL÷3¾?ŠUÿzššŠ#sG}\î?Í ãŽèH1΢ÀB@W‹\ þ±'öা¶Göú±áâ~Ÿa“z¤ü,t÷ ÀJ’î1,`L€ ´O€•$í3âL€ 0&p œ9sFr;4zôèû(7ÍîÔ]( ¯^½ŠqãÆaäÈ‘±Kºk>öß:¾¯ç}Ð¥«¦EáɃ]ñ°l$„ÿüQŽÎé0Ô°Mûb»–&%/Ô”ãbÍUéû‚øÖöOUžEIÕ….Å&»«¼¿Ñ3º´^®Œ 0žMà_cMÖoQt½¬gw„¤ïKs¿ö Â[>k0Ðv@ïwàÞè:%‰eù‡‘ýÕi\­ÊÇb懇ÜèšT_‰üÜb Ÿ:r±}£|¼áÜÎú’ÊÒ|œª’ÁÇÛíd½ ðô(Í?Œ›'ëZ¿¿òÞ­Tù ô³•îóasÒ½mÝýl^Œ·™@g°’¤3Ô¸ `L€ ÜâÅJ¸Úš6mZu9tO@q#½ž€V«Eii)t:\]]1dÈn×ç[·ëðŸŸÅbÏ·ÿìÙ„Ë—EcfañØÇà7jêg1ZÎVk^vΉCòpý–öŽåÊ›oÄê‰?¹ãº¸&Àz6a1yô}¼[ð·nw¤³„]e£°{þ«ð1¹³Up¹”@—(IôeHÚ u¢LTU¶}×mGt ÍCвH<»¡“б¶ýi;|†S›äó“‚¡~ {3BÑvÎ6«éäI-’.ƒ:p2B½­ªãžÊKÜ^Øù†ß#ÈýΞÁ¬êœY¦.ëç}ìƒYwîÁ¦©ÑÏ#×û5ºÜïA{÷° º·­½Ÿï¡TÜT/%`ºº—vž»Å˜`÷–€¶¼ýœœ`oüßG[YIKÁœàhÜ×S ƒJ-œ â×®•£¾¶?H1tДžÇ50lœ œM…Ú肎ê¯7«¿¬VŸ²¾N=*Ë«aïLýµºvÎÈ,Aܽ¼¼PQQ!)K¾ÿþ{¸¹¹ÁÁÁÁr{|T(H–| § íw’mìñ3òƒÿŸ¤t˜3ÂëNªjQV(]ÖLzZúy÷}—…¤S©8ø}n§'4õ·ëòùÛÐêuxÑóÙmò&À Âj乃¯"¿¢¸Ë;,â ±{Âzm¨ñ{ˆL:&Üz]ÕÝÀÕÚëôMŸÚ¨ OuÝÍ.•CXâÍÛÿklñ A$wïCÿ81{C@‡ÔM‰ÿ†íX·ÈCz®ÖkKñ·7#”¸j}P¸Øb Ôß–þعaÛ–XÈܶ+¢­X€!QìÞ§~Éyë[ПzyÏä­Âá’ÌÓ÷³^À.ÊÙuãrÿúÐE(¬¬¦´ÅäÑ[™¿eëÀý܃zÅ¢vS¬$é¦Ãb1&Àzʼ$,TÃkÝNĹP÷*ñÇåËQÛ°ÊKƒw—*‘&S`orˆ´š«ôлˆÞqïN¢ÕÚ+7Á¸€LBÓ°r¬5Pú"D-…û¶}õîªÕO•ø ‰Ì­5èËþŽåÁG±+#cZÏÆg˜@‡ ’ÁƒK±JòóóáììŒñãÇÃÖöþ¼Þ áE0âUŸ¾uG áÆå…Gi5§÷Ï0Ìþî¯åìß×?u] }N\ý›ý)ß}Þ)e‰p1öïxÈlðË K:4žœ™ 0žO@(Z—Ü„Ê[ä¨ ’ø}š5ÜOŽœ†£¦ÃW>âXGÒyíed”ŧŽA¸ÿ*Ó^éHq‹yëé·þ¿îıò3Øýä«`cg1d]I@_vñ¹€—r;ÂIAbJ6Ž.P¼ú ¾"‹‘Òs—zµ¨1Ô_Aæ®0e“§äÆJ_žÛÞCJn åÁËwB7®‚¹qD±le~2ÞøÝÔLÿ5âôXèT^˜Žÿٹ٩.WŸ§ðÒË¡ðp+ù_Å9Ÿ¥˜» ªl:OÀÕÿЃÜzMÞ߉4I:¶Ò$q+ß•HOz;ÔÙ†3^þ!˜]K÷²ñµ¦(5[ &`ͬj¼£Â¼m{âv êwwB™+•‘ɽ°rã+òv–Ú(ËQcÛ{jhª sõÃê QÈ<<oF™ª•òéŠRÑKõ+?ÇQï•ø½ÈSY„Ýï$`_vÕ/ƒèÿ kàmѯ™ùÉ;ñÞžry*p¸Â?xB¸Km@Kuý.X‰7aØŸ‘ úìŠòC6­ËkÃ[޾¼„œäxÌÍò“ŽÜiuz£,æ_:hµd®bL"_ÞîÃ~“BoYZOe Ç«KÏÐÛŒ/I`H­·gj‰¿™€uDL’Q£FÁÇÇGŠO’——‡ .@¸´¸é…ì8¨K2;Õ´m_I9R¬P#vÖó÷DAÒ\Ð)CÁß¾‰£ÿÛéø"BQò«¬·%ë”æõó>`½—€øíó?°ñŽ$öýúcÙÃs±gþ&\QîGÖÒ÷$«ù¤$騂DÐë8\r¨¢úÎÿ<ǃ>ÀOUb ±w<•þ ?þ俺Äeá Ãôzå¥ERý&Äͺì81ô^¹È4Yn:wÇJrqª‚žàõ¥xsE)H®À_†0å,d«ú|ÊMÙéÛž>•ùj,H¤÷‡§°éÅ– ”ç |ýVd—AHXB'¢$7¿ù(_ªI{±)ñ1P]ôĺ ëà7LCûë‘®¡7š„Þ¤Œ$ ɆuJO:·)4wmÙ&X¬ØÕØJ ’a~ „…)¬$$e’ÀX@_q %™‰ˆ$‰†&ÇÊú õÕ5”ç4f)Ö!lÃ4HŒø”’ÚüÝÞ”Dýó&ùÂ0×!ñ[U((¶ à0.¾^4uMJ‡ñÞ˜éóìˆe,-@J‰J(½¥þG¬Ø„¢†—% …ôG“õ."S@Âü! ZPÇ„"YÊ\†¸e¢®³4.!Pø CnÊV¬ŽËjñ~¥+JÆŠˆxä’ÜÊ%|  ÚLýÌ'”râž§‰vzõV`µ3æJ.â#Vààõ-û •0ûÓΘê.B ]/1‰Y\Gcî?Äüí¿®K³šh“¬žÚàßr¼®¶É Œ”`‘‚Ÿ›?ñ£ëi¼ÙªMØ“ßüØ£\Ü Š*xÎñÆ`Òak Õn%P„„À¢™ªÍPÆjÁ‘g¨ )'M¤)S€‚Ñ¥:ÒY1î9 ÏcSR´ãý¢ÀxºÿÉ,)Ï0FâÞHKÜJ÷Æt¬×e]—‘¡4n¡ ®>Ž(HÛŠ÷³è†h‘ïçz})Œ2±™$t#Ñ5Ç’lļö7RÅXHíÈÞÑúÚcÚrŒÅ/Kc’Ú«*AÅM“Z¨¯:OJÄbÔÔ]uÏ´w-¶ÝN£¼bËúë°i¹žºgI÷ÙSûÂr3&À˜@7%P¸; ™®aPm¨€2´ÚðÙf+X„–D‹ÞJA`TΫÏA<6\Í߇ϯ;a[ ¾Í2tŒž¤4fÁ‹ˆÅ“O€•ù ôBs±û’áVܸ=¾è8åƒ:’Ê|D¯Ž@6½cˆäªØ‚÷Bf£TŽÐ$±’Éä¾ð¼˜‚L±èŒ#¯©ÞÃ̪BZQU‚ˆeKŒ¹|°åÃ-˜M«§* SñâzÊ+z€—\¯€gaÓJ{ü°!uš€ärKs/..†F£‘ö Ôé:;Zp˱?àýÂ}-&å_8z&¶ÿ( “œÆwª|Wšá<ýßÁÇä2,üðv”V]ìPÂõÖÏ?}þqì·¿Cä83è™’¿û ÿqh3„…Eg“°œ ŸüS<ﱌ&µÄtäÝIS‡ºC|Þš¹Ÿ_<˜*)>Sg[ûB“OÊ¡ Òï¹IäÄî›×ÉJ„žÃÇìøu&¼X•eþbº"ö„HñI0u„Á[â‚6Äàfk˜Γ‚$4" 2r)·3FƒÝEÓ^i¯œ¡÷Wly7³¥ÅáóQup>3f«ßò@¨v„I͆#+6ãüåj”æþ‰&ù›ÊñÄ„‡°bM‚[JÚØC ™_vD.rÌŸŠðe(h0™1ô K@d€;Í‹"Š^e|öÓþéäÔþÅŽ/&7|:ߥ¢c¾HØ wñ"²ÈvAJX’À~Ì\DF˱va(|×Db•‡=ÊÒ£Aª¾FJ¦¹†e`O?¶›<¨ðAZbšÅ¸|úå–a©b%\ì°x:â6~§4.éIÒ⸰¥sBæ¹DÑ»Ø|³f®™û3&$R5þP©Â \þHR®€ú½CXÿv¥i$N*#§E‚rùfܘD}ݤÔP“ÔÞ˜š4RëþLqYÄ58µY˰¯Ê‚KC])YµÆßT`/Á“¦úÑ*íuºôC­”xóGáȲMT¢q‚]ꌽ VE¾ »“Kº,?VyÐa’w&Ñ·¶í‹ƒäTAñ,|₱9íp8dæš_äR‘RuÒu”BW…«¯+ÜÛ÷E7±‹´}2ß øsô"IVÅ3¾ˆ^J®ðv¦cåŽH÷†«î‘î­i(ÆšÄÓØ°ë=Š?Hãÿ´Š–¬ÇÉÓ߯kË Lã_s¯t^Êmˆ[eˆãã‰"ªë8ÎéV‘%Fc~±%îÿ¶®ÙW*JÖÕ§Cz;LMHMcl¨½½¿†ž‰‘;¹gš8ÎhçZ´mãÞl.m•µ×aó‚=tŸçhzèÀ±ØL€ 0C üÖ«4ˆú0òyô¨¼“bB¤‡˜[¢¶¤é(ýÔUøðGnQãœ>Eü#ðD=ÔÙÃV;í!“ïe ãáJÊ EP,˜-!ÐÊÄŠ”é’2ÄÖl»øë#ôÞÂd;‡^ ŠŸz Ÿ„ÎEmán,[ÿ1Îÿr*NEOµôâ°mo4Æ#×`jxÒKGFÀ`$,\Üï«1æâW”Gްíqð€ôèeؤÊEF0°š$ÞJ¿ùYi»‘‚´Š)œÊ;ºÌ†âDälØÖâøÁ*ø¬mn~<ð ÇNâÝØLz¹É–VÉ ·)Çs„Hˆç‹²/ËHä¼$½th¤‡ÎñClqæ£\ZГ@ Ã:‘y ¶®?†cCO Ê+ ¯’‚DüÇ*÷n¶>ƒç8GŒ·ØžÔ-þú”€ˆO"b–œ?ÇŽØ1c0zôhÉ%W—6D•å^9…ÿü<¶Ã1<„ëáNf”ƒáêj¹ºª>±2zÛì_ãiR˜üǧ›ñ}üù—SðägŒŠ’{=ùÙUýçz˜hÀÑ+…’r ¶Þ4ÕzÞæg„‹Áu.ÃëÓƒ¥ÀëÍÏßËýÙÃ=‘ù“x|r>¾|ß\û®Ãͧ—Á/þõþ4ÿ5ôíÞ¼; ´K`”4™ªBæ×åðžÝìÙA÷ ^TF@‹ŒÕbvËd8:²&3_zèt´¶Ý¦qªÛÕ/Cޤ eÓÛ˜¿?¦ÅŠtQ³¶0™W‘U%ʯØ0û·&IÓ¢ÒAéOÓßi~ŸŽäD±|s Gƒh—t­¦Ú¦ÕÁa`£ÌÒ̳l”äËPMp+É2„47r?Ì[°‹ÆÖ$ƒB¢ÕF¬S~‹ß’MÇØb¾f«Ë¯Òƒ½;ºgšÔÛöµØV;á Vh¦êîêuhj¤}ó“L7 … 0&ÐÛTÒ*óø\üópël®Òc0-Añ¥j©«CöÛדë‹5~âÌ(øz9àà‰¸îö3,mBz@\»»Pz‘qñžMæÄ;ë/#Ÿ¾ß·Ž‹b‡¥¼™š¾LéŠÈ7kh$²¯ÚaÎ3AXHK¿dS<áHAH³i-×t2©ôx6\¦¬)Ø[­ÀòY…ã¤H™é=¶¡Ý“Ÿ¦‘?ÔÇ0àÖ`„³¤ 'µ'>¥•es ¿ÐJ{ 5ðèZ}ûö•¹O:•••’²äúu2×ïÂt•”Ï|ºzÓòNë*.e„+ªî® 1ïͼ‘S‘˜„ð2?ÜîöÉk¥ùüívóq&Àzïª4Xš‰êº–«¦Ûë‰ø9ôˆŸvß$æ².;_íÂÛ³Ö¡3®³þRœ‰È#ï›WÉÛL Ë8Ž›JOè´øý·*ZwÞ4•}FA¢éÿäÑMO4Ù“iÈ-Ó7-§XK—.ÿiÑ–au Þ¦ø¯¿Bû¹xåK1èý%[8¦¢öû3Cù`¦ð’×|†ž5O6‡Ð¡L1 Þ¡?\rf˜mZÂqÜééTÁ9³ ë|Ëf ôþ‘ÿOIA¸e/T1‘Q,Â[ã3 -W·£†ªÔܼ¡Ær|º¯õúLÙúÛÚK›6vâ½*Ÿ—êL§(ˆÂU8X`Pšž€¨?–bnÐ*DÇ©Ha’@v/ x1—`/ñ wÄàp„‡?AQ~âÔD“dhËk^ mÌ»dì-Ò›8`Èð1’’èx¾']‚—.EØÇЍÀÔCeïdLk1lµÇ¿yþ¶Tã«}¤ ñ Éq§8‹Ÿ(õµ¿­%u€±v³qÆûà—fã œÿÚ }’Ë cj.SQòØJþ¨·|(¹G3koÜí?çΘ*‘J¶Pòiײ)SÕwý»=Ù;*@G™6¯¿Ÿ=))™_…ÙY ÙºêžiïZl«a¤ÊÎ]‡M+éQ{}{”´,,`L€ ô(íȲ¼x›ìBJÀ¡ôpì ö~?ûEôë+G "­¶ö;Çê>‹Œ" sgãµt¨!Î̘À=!PUWC ’—qéfE‡ÚAÙÅo Æî5صCeïUfaáòÿÈý—P– “ަßåˆ]ghá'&ÐÕÈÅTè:šÝOC°2©9…(¥`î‡vÇR\z`§X+ÌÜ?5oÞÃ?HšXŽ_ÿ&² KÉU*^ÙLתLérñà/V~”,=ì=xÍ_ŽªÌüÁpºy}B‰òégy$C>vG+É]0é ²³Qf¦7hY†,<')=Cß@z~ŠòÓ±iÁ*ÅR~8ME i‡JÔˆKΡö ¡ŽÞ(Åñ‹Î×Ñ7–î7@²¹À‰Bai)rhr}M"M´SÏ•ÖažB¨(Ò°1z7òòsHþpÉjÀ¸ˆ½±"Ó–ÑZ.ûÀÇÈÊ)‚KÀJZ6$® CrV>òsR®ÜJÊ 9Vú{˜J5|ë. `웽ûŠHž¼/>“Þ¥„]ˢ種*l^…CÄ£4ÿ¢C7#3û‚0°1KŽ˜·’ä.HDp¬ZêW)_ÖlMA½ÖÙȧc Uô ’ÒóQFã’ôÆ[’b@1< 4ëC£ªÌ¬‰NŽ©y b»mþ†÷Xó2m3 Eˆ°ÐÉÊl¸nÃ)Î ]n(),¶xÝP³&CÔCy(×71ÞU)ôž„œÂBd©cš˜K^B0߸0Ð$OeþnÃ9rtæ†o‘uè‰5ãî<+éZ-PEHc]X˜‡ÝQ«¡&å™bnk:=©éæ×±Åàë&!Åwófç,roïšµTÈXgËScj&ZæýÈG¤{hŸú¯È§{"KÍ{H$Ñ^×Ü3í_‹mµ# Óð§s×aCñ¸Ñäç§ÊÏ"3&À˜@7%Pú÷í´:Ê»(à›yéJĤ$ÑA[£§|Z¼¨i¸.ÆÓS J ÷g7AùÕzl^ÞP—ÿ¼KAéj󓘔†XZÉ5ü\vÃö€Ò³RÞ˜5+ʈ•_ yŽZ‹¤Ø&¦Ê%PnÆÌcô޵Ñð§=‹Ãô°ÿ¹ÊéÜñÃ=µ‘6Ž JîïìH,Qªõ£Àˆ!Â?ׄÍðÏXƒå _v|§¸ÀqœƒÅö~±CÑæ£¡vþËpÁ5xð`œ={yyyxøá‡!— «­¦I¯'§¾½´³ðïØO÷ZGÒo(Pp„÷Ï:R¤Ûå“œÉ ßÂOÒ7"óû<«å‹ÈyÂÅØÄAã¬.Ùè~„‚ø—ÿú ®‰(Ö§‡)VÂßžz Ó'X_è>æt4FRæ¼|$¿ÿºñYÉ‘Ö}±žƒ]0kXËÉRkÊs&Ð÷ lwLÀ+41¿©ñDî«Äæ«$åƒ(+Þ*L©aÛy.vƆ <2 ›×ËÊ}±åw+¥x ׆4u-3wÝkðM …:ò},Έ„ùYïç6Ò¹¤l${ J®~ô=‰”l5öRÐ졦ƾm%™¤•ÿN3ðîö0¼L1 ·FÐD5%áËët¦äĪ¡HÆ=‚bv¢"*êÄMå¹]ò’kP@~Œl¥|ÔKÃ늴gïþ ¢ü!&-ëID-ÀÏß™i¹ˆ_ÿgì¥þ$Ô=„—·ª)PÐ{Õ—Kz"‹ÉÑú'G"ŰØ|,ûT ü>a^}y+7G‹xQ¬ÆWš7žñøùXwáu$ªb(6…á Ü‡Q+èä~ÛÈ#ÆÈ2Dí|Qb^j6.ò¹¢CdRõ+IªHÕ3¤íUooÇÕ—_z+±’ŽÈ ˆJÀI Ö¬³Ý›è`ÚÓçÈ-XSƆ^XþÛÿ·”ÍêrœÑGüø%ömR5\·^þþðÊJCvb4r%c¶ÙØ  sƒÉ‚ˆî‘˜bŒ|L… 6`éf"ÕØ´ÞHÆK·ßlùzí[òÚ ¥ll5»Ç$ebrH;ã.®Õ]¨~# *³±ö Ù‚Èáúº5Õ‡3ú›5~2 éãžé~î'Y–R˺¤\ĸ­kVWÕ±úäí2m6ÆÍEµ/„ø "I…馓+5/¤¥ÙK,îäž1oª½kqï'mÜ›æÑuÕ±ë°Iá¹ÓçJ=Rrš 0&Àz%êêjœ:u >>´jÌ,éõ:TWëÈ<Û öíÏåš•l¾©‡V[ ;GGzHÖ‘ObòWÛÁ uZzسsl!‡NGÇÉ¿qÓêî¼½æ=à}&ÐZºn‹ŠŠ \r=òÈ#RàKS=bµ˜86n\ë“ùg«Éêë£Ur3#”#buoIÂÅÎüP@KŠÉbm.vÄ rö×o-1ÎǺ¸¯ÿŠÿÊy·C‚ eAêâßb˜=-¦èéÿN§âyR|èo×[-ýørúƒídV—ጽ›€˜n2ÿÔ××ãöíÛßb†é»¬¬ 3f&½['¢# ñjŠq@ ;dNpvìˆz¯¬F¹*r¢w€;IBn}­öRû¢^z¯pïÖ$ƒ VŽVÊoˆ¯A¯d%lMzz¡W& 4ÊdŠÏQþÕßqàÂxü2h†±ŠEAÕËh2^µÊ\ßÒnGÄ»øe0¼Oµ“Þá´µõè×ÏÎÂ;—á=IÔàØÞ¸P="¾E}½ åmIBȤ#«éμ+ÞÙ˜6íkü[ßlƒ]kâzæ5Ry½•µýèîÀµ/ø‹ ‚\=96}Im*¸{í»¡ï+š¿£,íÉÞ¡Êø ŠhqHºèži÷Zl³3"]qšU×7YIÒG‡ecL€ <€¾ûî;©×bÅ;'&ÀºžÀ… pîÜ9Œ5 cÇŽ•½8p@Rš( ضâëX¸™I=÷o«RÐ Ëý^'g[}¬.Ó2^¾y ³?^‹Ò*ë¡&>ç=–õ„L€ 4#püjfï[‹[·[:ßh–µa÷±áJ1˜d¶¦õ¯ §zÔ†ˆ7²òÓͨÿá¶Õr?çò$ö>µÅêüœ±w0Wˆí;S’ônVw»wEÉäú˜ÜoÉý”~ÒevB•­OØNÄH+þï¶\?`L {`%I÷–Ž 0&ðÀ.&L˜™ŒW!>pƒÏ¾gjkkñí·ß¢¦¦…ä§X§£f”¦L™‚Ç{¬…ÿ8wéÿ¯ÅñÖ̤ÔŸ¼‹6ÿØ­åë©ÇE`ö9?£Àš4Äî!)>ìVÁš­‘›ó0@-ù³÷I é›-wä³¥ïöšûýOíÇڬߑÃ1ëP왿 ÿñÈôˇûOXIÒ.òÓ÷`מ4hª(‹+k_Â/yXe¡Òz²0&Àî¾w£R®“ 0&À˜@gˆ‰ZazÊ ’ÎÐã2LÀzvvvðôôÄ7$¢ô×_ÊJr`l–Ä báŸÞÚäl?Ñ*âÞª „ßýÿ{"ÒZ$¨¨½·Žï¶:?gdL {ˆùJÕ!ɸ#p`ÉÖ^£ £ð«IK±Ùç?;4 /ÞŽ+º¦ÿ—t¨Î̘À] `ïE!ˆS%###É; ’»À™«dL §`%IO9–› 0&Ð \½zC†´@­v˜»Äî#Fƒâââ&?áÿþwS—Z)>a9aM®µ>x" b¢°·§å®ó;äB+±p.Ö\ííX¸L ×øöFbO챺?µVê¢ßb´ã0«Ëô”Œ¯Nû~9a‰Õâ–ë®#êÈ«ósF&À˜`L€ Üo¬$¹ß#Àí3&À˜@¡$:thÃ>o0&pw‹­Ï?ÿÜbå"xji©A)"Ü«Ä~õ'‹ù,\=ñ'X:Þ×Ò©^ylëìàöÐh«úvS_‹¸¯ÕVååLL€ ÜÿïËDw[Ö$¡ þ㓯`òWk²÷È<ï?¾"ÖŠµi×™4ˆx.œ˜`L€ 0&а’¤'ŒËȘxˆIÛêêj899=½å.2ûK OŸ>˜;w.æÌ™ooo¸¹¹A.—K®îúõë‡Ã‡KÁU~Ÿkµ«™†@( ¤ähc÷ß`u—“N§¢Foˆÿbu!ÎȘÀ='ðååoòeE²%aÂ'ÿ?aéT¯9f×ÏõÛŒÁvÖÅŒ»M®ÿû([“ôš €;˜`L —°éåýãî1&À˜@!PQQ!)Húöeý}2³Š‘#GJKÝñĽøþ7[:mñX̬µpê?Ðâ¹Þ|ð©Ñ>xÖeþVúY»Ý¼V[…¿gBXÜpbL ûØ|ìV 7eè#¿B®…bX‘ùºUÝ=pþK¹RˆYÃ<¬ÊÏ™˜`L€ 0&p¿ðLÔý"Ïí2&À˜@BIÂñHš á&pߨÛÛKAw÷ŸË¶JÈüî‹­ÊÛ3ýfæZØôígUׄ5 '&Àº/ƒé“ó9V (îû]óþýûÚZ•¿7dú©ëI1lm_¶æhmVÎǘ`L€ 0ûF€•$÷ =7̘`&?üð®]»ÆJþfÝ€À‡°½î¶Þ*IDPß¾}ÜÇÊ ƒÆâg®~V±:|©E×ˬÊË™˜¸÷¾I¡hL?XÕð¯}Ó†º[•·7eŠŸ†¶¬ê’p[v¡¦Üª¼œ‰ 0&À˜`÷‹Àƒû6{¿ˆs»L€ 0&ЂÀ7 V®÷ïß¿Å9>À˜Àý! ÜBY“Æ:Çs.OZ“µWç Ÿ¬°ºêëØZ]!gdL Kˆ˜AúöŸVÕ5Äî!¼6ý—Våím™F;ÃFïŸ[Õ-ýízì:fU^ÎÄZÐW"?'šÎK¯-CVz*R³Š ¥íœœ|TŠu(ݤúÊÒF™Z Â=< ñÈG¹ukt¬LÒüä—VZ™Ÿ³1&ð `%Ƀ:òÜo&À˜@7"À®¶ºÑ`°(L€|¯½¸Øš2i©Õ®¦¬©¯§æ™î<Áj¿ûÉVÄ/é©Xn&Г $“ÕÃõ[Z«ºáý3«ƒ˜[UaË$‚Õµd•Ô8ó‰ÕÖ9VUÈ™µÅˆÞ‰ä“íOpëJÓ±6(…ÖÝÂ÷¡ï*ƒ±yk<â÷ÃåâØ´)§jI„&ýÓ"5Z‰èä¢{(›¡©Ús™’L}Ó>ç»*ñˆ 6ª])G5>‰Ø„õ©» úý³»Ð®’ <ðXIòÀ_ € 0&pÿ °’äþKÀÌ üãüa«&´„‹­_Lxpc‘˜3ÛÁý›²¸ÿÕÕoQFŠ(NL€ t/ÖZ‘Èlúh`÷þKÓßÞ(³Zñ~»ÁÍuwýl1ŒdìoEØý%”T£Î®›uJ Ǫÿ-{‘±C·ÄnÙ†IBÎ&ýëm±ù]jFa [ƒ5[{ëòß­\vnض%ËÜva NÚ‹mŠI]X§©ªû8f&ø› 0.#ÀJ’.CÉ1&À˜@gÔÖÖ¢®®2™¬3Ź `wÀ?ËŽXU«ïˆÉ?PnUÞ{‘I¯³0±@îst–Žß–»Ì‡m_›vkñ2¾?Ún>ÎÀ˜À½#PQ{™ßçYÕàJ÷éWNâYÕl·Ëô¼Ç3V[î-ù´ÛÉÏõ 5fb–¦#:\‰… Ò'k£PHF¥éqøi¨ŠrjýÓµH6™“h‹°;:¼!TB: 6   ©‡è¼±¾ %íšµ¦EÖîX(ƒD[ ¡\…ÔürèË!œòªóÍ­ʱ›äŠN6/h‹R¡\²ž¤޽÷"ÖF§¢¼ªíÊÄã#‹Ô¿[g»öWPSƪ´ß@©4YÄ裎k”!<yCA½†äXKûE9ˆ[K2®Ý s#MN‚ê!Ês¥ B\z©±zd%„CI2™Ê?ð¢×Iý ZC¥¦3T!aa œˆ’Üüæ£|Àv(&z¹JE«†¹Àɾm—!nY(TÙgᯠÂorS¶bu\Ä®½X€”ø¨.zb݆uð¦¡ýõH7*!râžÇfÅóV`CXÆ\ÉE|Ä dÖ‚}•†&Ï¿”êêJ?‡ª@ƒá㇉݆d7x$æøzA,ÓÀ3çx¦ª¹%Çp£¾!mØc”‹Å¡*xÎñ&w~@^R6%¥Áa–!Jh Ò©Ü„"zÖ©½\†‚ÚÝ„´|¦Œèµ)9 •¡J“OO šÂÃÈÕT!íÀqüþ<ÒR ×q†v©`AZ ®OR l] K²óÚß$¥ƒ®(+"â‘ o(C”ðu,€jó$Eí±4É$úéüP JÒÔ8iÒ9èK‘¬Ê…vì(’ã&Ž•äâT…x˜kcü‰¨Fõ|ÃhOaf 4Ùé8e¬÷ëL54Uƒ0˜ ^:Y€#š›’mʪ/›+"’{þ!âub"2KJPq³®±Ò–å1«.+@bD(¶[¹—;T¶qÝR=ÚÂÝPF&"W;Qº®ý½k™´ QF·k­_:RÈÐdžÅ\å:¬SÎ¥ëƒÜ¦'JJ¹fÂò.`Vh©›•p&À˜`%píÚ58;;w¶8—cL ‹ œ¹~åºëVÕú“±?²*Ÿ”‰,:´µõ°st„5 ZZù×ÏÎöíe¦Ut Q¯!…&'èM0M:”ÂúÄl¬KØ‹ =Ô/¬ÀËôCr¨wƒÌÚJjc ¡ ½N‹ZØÁѼÁÊ,*ö7Û*+‘_¢‰ NL€ tŸ’Äšä5ØÓ†º[“õžät6æ¿[Ôª^§£ß3›¦¿gwI¥ûbX£)¾ñ=Š®—Á}И»$ WÛÛ h¯œ¡É_Wly7³Doç£êà2|F[. VaËX`I(°ý½HxгCYzÒè\ØÎ¿"ÀE Êå›q¼Ø +®ÈUÀ7ºEð¶Îd¦P_,™ÒôÆÆyB£‰…¡XöZ V¹ÛÓ¤x–T_býÇaUä«°;¹© _Bä*Ò¼äãuu \•Û°c•áÙE±àQ, ÞŠ?ý» GJÕ^!ø0NA*˜¦ÉÞÝ~HÂÁÏ¿Eè g|—{Ì¡à(Îëƒàrþ8)=€us'P[†Å1^ÔVœ±-OaMâqœÓýÅ ‰€Ì*U¸•ÂIÊP¿w+w,j›¥\¬á¦<­i£ðù1 fÌ•“‚é²…K¦Òß3R>áü«íñÛ…rd¦|N×Ä8Ÿ;‰©d 3¼«›V¹ÂÏ /“PôÝÖ¸k¾ù›$‹rÛXå-ä€ÇÀpD¨,<³Ù»´3R)Ù9A\ñšê=Ì•ÛHJÖ®[Owª(¿¶ÿ9t-!`>dk—!)ëkhýobg«×]Bÿ% ËWAü².ð…7>ª…^hÛ{~¦,œ˜hJ€o›¦“ ÷°ûÜè–r¯œ²J®qGX9ÙEn’¢°ImzÁôÂÕï0èŸÁX_ŒObHïqyqAxG Õ [r¡°™ä¿Û”Ûö!ÄÛôzk:jü¶ŒÇ/à± ³ˆÜœ Ó:?]U |ý7àiwÃËù(‘ŸÎjó®|ƒ†• [z£v…¿ŸÒ2 ò¹nÁŽÐÙ(=”€51bÒC$9ÂÞG€{+22IýFù˜íµ¾)& ¯ÕV=П[§cýñÿˆùçöíÛMöÍÏ™¶ÛÊ#ΉÔ}`í§o_ƒ±¾øîLë{Ï9»Š€° ;tÁ:W[?ußUÍÞy=‚N¹ Ûö«¤‰Z1á–E¿·›¿·2ßuø :M§(ï¼YóüÇÎÆ@Û¨®3¬Ò6?×|;óB®•ÿo4/ÉûL€&¹=Vá“} ûE2âŽűÌ\iżÁ~„&¿ÅÄ0jP'fÁi¦«âÒE ›:ö=jPS`ø¿þHq%ú“Ÿ+ù¼ù “è8HŠ" iK¿’&ÝþIuHœæB•‘!mê Ô[‘y´ÞsõÈ$?YrÅFHº˜Æ†-ÝM’ ¸uSXGˆYðÖR­aÿ–áIÆ$ƒ<{³k¨úwV*\|ê`T’(‚Z(H -Œ!E‚Œ ÇP> ÇHéã‚‹IIøš¬eœÊ?üÈ…d*4”˜6ÓͰAGzËØ ÔÔ×£Bt§ðvøZÔж±”Ö¦È* V)m°%͓͘ǡÅC–‹ð¹8™‘J§ýñ„C 2¦öÆoåüR¶$á$õeB>©Y|”PB…}GÎ!d\¹¤ [7@ «gŒ·!ëå’“”ÉsmüÕ|t®ÈLÈX¸ùWÓ1gk©~™ÿIA"öÛ¾n‰-):dÏ$¢©s;2H¥g°2ª–¯›X:®~u ‚¦ÕŠ/ú-Æ+[fÙgzHNL ÃøÖé02.À˜`]EàúõëÒC¶­m“µT]U=ר@'äW[UJÄ#±*U%I9b÷f`†¹_Šß¦~ƒ¿úÒK§š\F, Ã2|D+þ& ëÝ¥ÈF«"“PšŽek¶R3&Õ‡…mœà={6-DâEÛì]Èo9½o“?ðŸ†ÆCè\6¨¦PÔoPSU‚«0 êðåH*U@õIì‹ÔX¾~òWO¤ ñ K@L€; “£°þƒ/°8fQ» ó<?,Å*¨¼UmÅâ·˜”ýº¢OŒ$™°$”õ4ébþÑӲǶöÅ9ó<1¸›–GàôYˆ^$ÖR{t’U]}?8:¶5™kª `c‡ž…äï>k<ØÊV–&Ï{,kå,fmÐ&cÙúD)“=C(6LÅþ­I’ÂRIÓÛŘI“0Ü”ÁÝ“\_õǬQñ•t¬éóÍo’ñpó€ñÂJ‹l·`/J×­P¥å#ؽÎ`±²øQSé®ù6Ê ¹b9sÇ(p‡'=6ÈMmˆ }9¦k}Ǥùt¿¥dàXþ$¦ÜküüpA„£_@ÿìšœ_))‰Lž¯nQœHSª7nŽ"Ƽxç>ÉhЏ{z¢¿l"Lœ›?«5°4ÖÓøå¿Õ>PÇg’T|™Bv!‹[(zLõ¶6~öN³Èþ" ‡sO@›S¯Å‘dSRUr>ò]E¼_Ì"ËR546Ý°ÕØOqÈ$«mab[³™R=n5”²vÃQf’^(:Ú¿n¥vÍjñôô$D;×ÀŒ;°×/陟ãPZTñÙô!k«ßÃlÖ”˜åM&`³[ߺœ‹ 0&À˜@WV$¤«hr=L kœªÃèEÙšD+/?Ü9y‡v#öh6„Á†«'-òtY€@$â9¥ö’M.üá^}´Ü3p›qU¤Ë"lðÙŠâ¦ï²–[Õ[ÎdC–om{ ~·[ßþ;æÅO¯qîÜ{îóœç}‡<ç~Ιˆ¨Ÿ#¤Û&ɹ=ÿš§E“z¸»Š€ÊœÈÜ´ I£3¾7‚|Ñ{~R¢‹ÓBÈI…ÅHj¡•âÔ%ìÉñÁQÃÚñ§q8íS$/™‡„7ñ69RWÚš@YDÕ$Ñ˺²96ô~[ÐC>*zÙŽ+?Ô»R?ØÞÕ»»»;¼¼¼Jèm‹ñê­rŠO]©‹}pJmÛ>«z?¶mûýJ}¢®¡ºnê½ü¶m_s ¦ȱæÁ¯üªX ír$ñZuf@… º“XˆÄ5;aCön¬C VöÍCüÈåð‰\ŠÕáA8½%QK¾¬zŒJtêVil§%Ä×Îï2à> ær§ÝQ½‚î×QX&’iñ¥ŽS‘¨þÆ"éóIeT(•9¥¸èiêžw±À„ömtD^ôtH›H Û?Ý>nØñÖ$)™èÎÈz$ Õ/ÙØ¾a\ï¾½G< ÿU1X8e.æ.˜?œÆ[²z¾3/ “ªûßï@Œ•xhbÂóHð^€û%kÓ‡ñ/k̹C­¿ÿøÞþ  ¶bgšhf߯ë÷=,;5 ›ºÁ »oŸ@`Õ’¬,ŒÃ·ybÏš×°J ”G>ô”ú©úVHH“¦Æ°í÷,×;$”$çJZ)[ÑŠŸ3Ñú/”ôOC% ”°p&Çâ¹p¹Ÿ|½1KÖ‰åNˆh·æÍû6LRsK‘'cü#q§ö¤JÙnüt\¿~ÃGArɉ!è×]ßO¶` …éT<«ÓŠ[IˆÁª¨ÑfÑD´ÿn+VšjËv–|ëJ®™5è$a³r­òïíp c-_‡ç—ùbö„R|þ-,—Xuà´¡¢èë^Åw`2ÚœL“"ñÏóŸÀ€nnør×^m\/O7­ÊØé‰™&ªhù]˜H zÕý¯¡úhA$@$@µ  fÔ“°F£zb’HÀQd™Îér¥w{í¯Ñjmóó”2eþ)êœt¼$¨T¢ei~ÃÂá»jÊS„óßQÀYp³¤~^øê;x௓àzø]¬’dÌš¹%[6¤¡ÏˆÑÐ’Öz¨üŸ>òwó¼%~÷rû˜ñÙ¦õ²z8¶’xK¹ÕËi<36 =æKðÑ‘òäe<™÷©/• ‰õÁÂÊ–#½Û•¦¶©Ê0Ët¾ªÃ vL¥yRAü|É¥-/[¤ügµPo[·½+eG»víÊìWª6Ç"`SæÔÄ+¥B±}lß+W®@¥Æ´ÿžØ¾*˜b{Ù*¶‹³}'¾¼ R³TßîìÔ·z#eQ‰‚Îï®pyzz1›ÃÑùÓ¢ú˜„Þùg°Cž^ð`ö„¶Ÿ,Ò….™k˲Rõx*èT¨äÚV­‚Î¥3ÕWEÁްpܾ1ƒôÝËnöî¡ÕWRu–ªj*ÍàWÂzpgYe#la¿ ‡ŸAhÊ,¬[#Fiþaú%ÖíLÂÚ/ÆÌž1&aÝ*‘ôˆÞÁÁxeÅ <;}9bgI$C5cˆ¤£{²…›ÔüÚhÒ {e~xöy)=2 j^NFøÜ•f[Ð÷Àƒ¢:M[ç‰1wUý³b›ƒÖöOéžÒ-†L–Bò’îsyìqt¹3ãcãñã‹ó%5UñœåÜQ3–â!I#•Ÿ¡:*=[ëöš\a $ µ.”{÷ eÅ“p~TXI £RÝTÔ|à)ÿ‹÷2q‘fÄH=“é[4CßÐHÄN –íkCêé噕eEýzàîpÑY‡°ñ¡%EÕ•eÉŒ Õ_?ï[C%(’„´Pt×V7»#T¢$ii¡Ü·4k-¦^‘jŸ¯ÞÁX½z>^š»Ëç©ïŒa㤮‹s*l寙¨|¤õìØ¶ÄºÚïíè0?ûEI•¸ Ñ)ÖÓüGÍÀóX¯WåßÎÈ¥tÖü…XµpVÉx¡‘‹ðˆ’óÒöýøã•’cÜ ¨š@+É“ûsÕ&_‹Ýj<·q™V$3=!o´yo> ©4Jû¸áp"$e­š™#ô©g>8ù9Æ~4×vj¥ïÃo¸ús¥Çk{@-p« ´íÝü°½+•mQÛü°_ì¶íkŽŠÚ2åy¥ÔC¶ï’-xbûl ®¨ïŽúN© ší»e{WûÔwÌ‘Z¯ÿ„ãD^õ*ºí£Wàž.ª˜qÕM)èFÚta·"'Ití 9º6Mƒ´ñ’fÍ<´™¿uýRîm¯áy)¾¬‚°–c˜:r!¢JîeUŒewÏ –¨ºŸÍʉBjÌí¤ ù<=ç ¤>åYæþ¨ì<#þ•Þ÷Þ[þ r2¿FÚ§)Xž´U2Å!QÒ¸èi÷nšŽOΨÖô­{žÇc½ï¯ÖŽ̓@ùºLêÿ?*P_>`VV‚%¨¡§©€®Eª´{husôÔò“ªæ.%± ¥ çzÆR6fQß™å¾çáå 2›ðvÄX$öšÍ ª¯Y¦w¼ŠìT­ KæŠ|¨Èº÷iµ2¤z‘«°,£!­Óõ«‰cæ¬ÝXóáw›8¾äœìmqˆˆÝ#ó$Ëï5é­¬mµß[­Ôk“f†²_4­£ª¾fù~ÈÜåûÝXW¥ììø‰šþü4ëÈY €Ó`=§»dt¸ÈÍ¿¤k–Þî^ú$ª7ï Ħn–E†|¸ÿñf2™åkuÐ#bS1Bm·c~÷W 5VžÊ”€IÜÈÉÖ’™† $§J¾ýÊšGSíŽ0bæ›ñ¤µø¥GÉœÊnY‰]Pd"Þ´}²ïcP$RS¿¥D«,ÄÔä—æë=*âØÆ°{¿P  Z4õ‡¶ ‚TôR‹ÕêIÛ‚´m¡º}ûö% Ö*% Ô–€úþ¨—J¥VY+HQßÕ¼¼¼’àŠúÛ¾›¶@Šz·½êó;ªÆª*}ÙÕŸ¯â´NÝ-í{V6å2û+WÐyநqX3O·£¨¾wHŸ×ÖìÆ_ï‡Ãÿ^‰ò,µú)µˆúnÃîË1zP™'­Ë f÷¡WèP`zKí4hOV|j‡.@BÌd$øJÞ—lY}?Lß©H9Rñé×î­æÎkO°ßSÖoû#Um·k£Ïå*x«§lUª£Š^êÉ\Ûb²z÷ôôDÇŽKöQRÕÕá±Æ P] E=I®'*¨g ö)u©mŸ:®¾ÛmÛ¶Õ^¶mõ®‚+5iGEff&† VáSä9æ‹(¼j©¶Ë¶®îèìÙ¡Z;e`èq—¤€™…ˆá*wŠRÐÉí,éUì›(j‘þ#¥À:\ ¿_+È+‰l0;qæEÌÃX¯CäŒËZº­üïö`Õòô¼7Y”wÕm˜„ùãöbaÄÍØ2 ñ#$HbVAkÚ[/×{•½O¹Üð…ï?¬çÊͳãÒ Q}Þh”Iêhg¯ü ÃŠ&$àØò¾Ù‰”—1jÚR„Ûêz8¶ËôNWßX»Ò «ÿ‘„5± !•\2nfL]o5gt¸A &"Àt[MžÃ’ @K&pùòe9rwÜqGKÆÀ¹“€Ã8v1 }ÞX­_7IÍcáïTkWks.²ÎžÇt€ŸŸOƒ?¡Yk?+9ñë‹§qó»Vr´tw©ë’ñðI#a‚º/ÚÞÕ¶zúݶ0l[(¶½×çSö¥Þp‹‡€M-¥‚„*pbÿ®ˆêgCÕÏ„ý{EÂ?þ'NœÐιçž{УG2ÍÈ=‰¾k'•ÙWÑcWÿui ¾ŠlÊîÅ×5 ºòÏ£«3r±eå¿áùÀ éæKö6ŒŒX]œ’°lz?©´,ùp‘”-WM/¼U™‰ª fWÔÃÛÇ>Äo?y¹¢Ceö=â? Ia/–ÙÇÍ—@C¤Ûj¾´83  ¦$PúH_SzÁ±I€H€Z¥"ñö֗ަEádIÀI´jÕÀŽJºªn~-ã¡j8|óÍ7ÚB¯ZìíÚµ«¶­ÙH ¥PiIT:¯ŠRzÙTV*˜¨‚'JbS\©Z'ªæ€úY²½Ÿ9sFè‚-J:¿   í! [@%¯ð².ÌܺìJÊ*Ñ*:xÁ§Í!ÄL_‘œdŸÈ†¿Ôé¯C9R:VÙ-•–¥Öè‹úÎNøW¶ã*>utoWÅÑÒCzy—žÁ-   hxµþÝ©á]ã$@$@Í•€ ’øøÔ1±ks…Ãy‘@pmí¢kô‚¢êSÓèꨙ\-›ë¿²i*Þ*2`@õ… +ëƒûI ¥pqq©0€¢žXW›"KOluP쥧§K&¿l„……Áh4Âl)°?\é¶§¦¹"8òMl|8gÏçÁ­CWtó©C„¤aœ¬¶×¶.úÒ åéã]í€4    z$À I=ÂdW$@$@ú\¼x7Ýt“>cZ‘ 4Oɹ¯§ñIàª)åè{2]ï¢bÕ£ñ( €"ÐJ$n¶”t6"ê÷ƒÚ>–¼Ÿ;wÉÉÉ3f ®ÊzZë–ÐyxûÂO^ÎÚ\Z·ÖåúU f±‘ €£`ÄÑ®ý! fNàÒ¥KPé0Ô‹HÀ±´oã¥Ë¡Ü‚K°\-‚^剮N›‘‘*­§y»ëã­§/Ú \K@)Wí› ¤têÔI«KÒ³gOtìØ­Ïž¶7©t›‹û•¢Ñè壮 8IíŠÐ hæX¤™_`NÏ© x¸´A»6üT`ªrW¾Š3—Ï£§W?õ,„Íð€‡®ßX-ÈÊÈ„WïÞð®ÖÞ‚ÜÜKð’ÚHÕšVI¢âƒY¦ó(··“G‡r{ø‘H > œ?^{(£[·nZ`Dm/_ïÇÝÅM×WФš9[¥®XôñÑË»Òx€H€H€H€€€>Ml Ì.I€H€Z&•ú¢}ûö-sòœ5 8n†ëuyùÍEk1d]Æ™2’0|øðÒ×Ô$” ÍdcÙÈ1øÇ¡²O‚«®,9»±lÙ–²ö¦4Lž¾g+ëš]b;a¤•ð³Úîøæ§,]§Þ “µ®ÎhD$p ÀÀ@üö·¿Å}÷݇>}ú\ Q'Ý<¯9¯¢òó*ÚÍ}Å.è㣗7Á’ @c`¤1is, háTQUIZø—€Ówx~Æ.º|üòB¦.»ÊŒNíÝã¨EؘºëׯÇÚW„}©â} Ï ENnãvíSÞ—¾Û””ã°/ž¹iµô…€iˆYÔ"&ØJÌ›M&)Ð\ì¡âW®F?ûÍ&äšÌ•¹[£ýGt²ñ7v­Q¿4&¨¥©.½Óuítuš}ù]vu5²HñyÛ­ªú¾”‚îru t¹5è»úÑí-Îêäs;”±çÆm   Ç À ‰c\zA$@-‚€ªG¢,Ü*Xôl8Ip·x÷Ôå徜ÿ鲫ØÈ‚S'p³ õõî4?wÞÞ%¦¦ŒĤ Ä´Q((´…¦ôL˜µNl×aÂð™ÈÐâÙHJ8)ûËñ·1~ê\L>FÔ"c1rêÄÍ1cÇbÌÈáX¹;0G\ôR—sÓfbê‚>f,&Œƒás“q­v¥Äµj7~ÆÏد“Í-Þ=ªí$@ KÀÇCÒîµv©v“¤ÿ;wåBµvUT« ËÙ‘cÅ¡ TnŽ® û6O—޾ž«BÄc$@$@$@$Ð$$iì”H€Z&ªHZæu笋@¿þºÞùýa]våâ«4 mÕ<¼óÁjÌŠšˆˆe;¬O8[ŽáÓ“0;þIô3^Fù,÷† H$.'ÝŽÃêõ±è-±sÆGØjŒÀ½¾"#áIÞ‰ÿaÌÊõH]»'v"³×lNMÅŠÈ@¬[ü‘–¦ë2rPX$æm.CL°bíf±ƒÚ*|ž¥ë±ì §ö¿ÜSÐ[¸½_Ç^öÁ$@GÀ¥UkèM3xT~¾ëÒªVÐå"aæBéþzu»¦éRЉ"ÎT¢ˆ³Èv©š ¬ ;š{òŸ+ÚÑ൬*”ûH€H€H€H % ª±ãa  ¨3$éܹsûa$@ G äú[tu~ü§38y)»vÅÛ-®¸?n)&Þ_ä8–Œ1Ññ8u;r^ŠFNÄ Œðqž‚Š]1¶o£è`ðÐ ¯öv"Bÿ/QK×e‚œd‹ûz«\Z}0À„öÓìü‚nÊvZXpÆðñЪ½ßŠP±?öý% ›wŃW³w›¤ÓÓn4úÂǃigô°¢ 44ÞíºãÛí@ÙJâ&V{÷Šã.{F¥ŸRNïªô˜ýÁí?r›H  ôíp£®Ñ¿8÷•.»Š*WЙ3“0=© VίËåC$@u :7 «œHk•ëS±vi¤?RÐkN"RS×"2ðÞXLs©¡tßü”…Ì+žv¹½}½o,·‡I€H€H€H é PIÒô×€ @‹  R>¨Z$¬GÒ".7'éÄZKê™»»Ü† '?¯vÉß~Ši}ÇVkwå¦Ïš…Qóã1sHwdlHB6nFÇŽýñµk5s7  ì_õ$vö_ˆ§~ѽl…*ª‘sRSÄmûä Gïr¿ÕÚ*™(ÍII ¤l/~ª©½}'?˜°õÌ>û]•nßÓu@¥Çx€H q ¸®·®?={@—]…F•)è¦tÃÆ¨D¬Ø(Áâ"\ª ‰ê¯*U÷ ¬:töÅ·YÇA|‘tÉvG´zVß ºOϬpÊåw*]w‘뱑 €ƒ(÷礃yGwH€H€š Ö#i6—’i†ß¢+H²]RK½üºx^W3*AX=?“F!E;ÓÓV¼?WùÕÔ»4áK{wO´kßee5ô¸ !˜…¨1Ljc˜¶úÕÒñÕZ ¯Å"m¿—hKZ¹õ9»#%&eìKöV¿‘üíg0é É ¿Arî°‘ 8;:õÕåG–é<¾ºð-ô*O®éôÝ\9º;Åpçô1H,>aÝØá4YÇTÚÀ Z±‚î•Ev º‚rbéœS® ‹ÒèqÝteí­h„köÝyý­×ìã   peÿâtè @³$ RmuìØ±YΓ"æFàW=~'ÿûZµÓ*úù*mÁœþVk[Þ ÛHIó¸·»Á Õ )oý&‚ÊïTŸ½ƒ+…Ø¥š rsŠà%õKlM¥¥Iµ­4J•’Ç’•µäX²u;Ñf™ˆâ]r ¬}ñiºßVýÿtÙªV?c]¶4"hx·x÷ÀõÞ8oέv0¥²«U¤Ý ý§`íZë=ÔÍíâ#æ£ïÒ×1´{¹‰]µÎWbP]~Q!¶d}QIÏewßåÛ¯ì~"   !Àš$r!è 4wJIÒ®]»æ>MΚµ€¯7ýLüÑ ¸*Á’Ú5W* èëÏÞ>Xô_VêéògÓuu8þÆ¡ºìhD$Ð8Z¡îí:P×`ïžØ¦Ëî£b]Š(膉髎‹‚îYøyÈ}Ltêe0t‚Ñà ŸNÞ(' ƒUA—" ºh,]~Ó&„” q­"Ξ.ÖÃד4…%g–nÔVA§$%Õ ž&*E6   pD­~–戎Ñ'  æCÀl6ãСC¸óÎ;›Ï¤8hæ^9ô/Äìù«®Y®ÿe,ìy—.Ûæj4uÇ«P#=íðÃo!°ƒ¿SÚ 4~½“?ý“®Ñêö3l©RAW½6cˆ'|<ïe~R­Û=½|ñíĵÕÚÑ yPËMö¯¢¢"\½zêÝb±”¼gee!88¸yMž³! p*T’8Õ墳$@$àœTª-ªHœóÚÑë–Kà77ý.RÄ]O{éÀ[e=¦ÍÒæÔ¥ïñֱͺæ6Ч$ºHш—À¯z Ö}ÏK8º©Î5ݹ+¤~•ª¨R}{ðÆ–H¯ž-œ•ÀžoÎaÏñsÎê>ý& (&PšÀ™HH€H€H 0HÒ@`Ù- 4 n†ë1¢ÛH9½«ÚQÒÎÅ{'>Áÿ{«µmŽó÷ý*/¿žö»›Gë1£ @#P5Iîé:[Ïì«vä·DuòRÈïá%…Ø[rûÛÑ(¸ªïÞ÷°ß=-çîä/^\é Îß0D1WqG¯N•Úð €ã`Äñ¯=$ §' ‚$;wvúyp$ÐÒü!àA]AÅå™/Vb”<‰mpõhQ˜v}D+^¯gÒjAu’(tØH€“À£½†ë ’ä\›`VЯs"àÕe‹¯ù¾®‘Tª­ÐÎ,Ú® ’Àœ9s*ôKO\[·B~aËUÓV†;I€HÀ èË¡à„£Ë$@$@ŽA@åV5I¼¼¼Ã!zA$ ›€ zÜÔ®›.û“—²1/íoºl›‹‘z‚:Jj‘è-\ÿxŸQhׯÐ\¦Ïy@³# ÔpzÕ!‹Óÿ¼ÂËÍŽÞ ýåËd|åG]æ¿ís?ZëLߨ«C‘€pui ‹ÔYa# pn ’8÷õ£÷$@$àðòòò`0ЪU+‡÷•’ ”% j’ÌéÿhÙU|Z~ä=|rö@Íëм´¹pBפÜZ»bV¿–ûÔ¹.H4"&&`tóį{…éòBÕãˆ=¸F—ms3²Î=Q×´ÔÿG¦Üü+]¶4"g$ ‚$EET’8ãµ£Ï$@$`O€A{Ü& ¨w*H¢íõŽ•’@£POû»êO)*&m_„óæ\]öÎl´%k–¤ÿG÷T-’¾ºíiH$Ð4žè;^÷ÀKåðÕ…ouÛ7ÃÙ_¼•rLOÝãPé¶ØH ¹P鶨$i®W—ó"hI$iIW›s% &  ê‘Æ&™C’ Ô6­Ý{ÇTÝ]1GøÖdÁ H÷9Îf˜™w¿Ù¾Pwš-õtú ';Û4é/ ´H·]×a7ëš»J¹7峨f}¿+bÓ©ÿê®Ã¤Î4±|üLÍŠ€KkI·E%I³º¦œ @Ë$À I˼îœ5 4¥$a¤Ñps h*Oÿ=]èî{ûwûñä_ÓmïL† Lx`K ~0_Ôíö¼Ã׳£n{’ 4-ùô5¿8÷øgÓ:ÜH£Ÿ½ü~÷YœîÑîí:wùé¶§! 8#WQ’±&‰3^;úL$@ö$±§Ám  z%  ¶«Z$îîîõÚ/;#h\­Ð «îš—6ºþkÆzÉׯ/g½îN›Ø0¿¨ãSŸÓ]‡D¹«žJ*pB{ÎáI€jBàî.ý1²û ݧ¼|àm¨|͹)uàÄm  ê‘èiêÿº=J)mHÀ© ¸*%ÉUÖ$qê‹HçI€H@0H¯ @ƒ Š¤Áвcht·x÷Ä‹Á¿«Ñ¸Ïïýân¨Ñ9Žj\$õVÝö"¶‰JFoS©ÊV U´HÀ¹,¹ó Ý?»ª“ |}ñ´sM²ÞÎÜý:>={P÷oºƒ:ݪ۞†$à¬\DIRt•Jg½~ô›H€l$±‘à; @½`¤Þ‘²ChR³‚~]£´[?ãgD¾ÿüzs“ú]×ÁÕÔ¿ýäe$ûiºZò;MIR£“hL$àúv¸3û=¢Û— ùy½eΛsuŸã,†Ë¬Å_¾|_·»íÛ ‚Ll$ÐhJÖ$i —šs$hæ$iæ˜Ó# ¦$péÒ%Ö#iÊ À±I ž ¸´j ›Nm;èîY)0T{gM½õ“Ô yð£¹ø×7éž³2¼¿ûx†‹kÄŒÆ$àh^8½ÛwÓíÖ±‹YøÕ‡sWxY÷9ŽnøÎññôî¿ÔÈÍW%@ÒÅóºCcpVn.*Ý•$Îzýè7 Ø0Hb#Áw  z%ðóÏ?CI¼¼¼êµ_vF$дºzúà?ÃÀµµ‹nGT*šçöÆãáÿˆÜ‚KºÏkjÃC?|ƒ;ÖG!åô®¹âgì‚5÷ÌCk *±‘ 8/OW¼5ôùÝïöžÏÀ[bpÅ’ï¼/ö|ãÉšŠNÝÃõ¶_õŒÈ[Fë5§ Ô;Ë…ó¢è²”é÷ÂwÇqôèq\*³ÛK™Ïfœ?_óßQ\ZKº-*IÊðæ pFüËͯ}& ' påÊ´iÓ®®ÌÅï—‹.’@ÜÛu ^<½Fç(ã÷3?EÐ{¿uøÇ*½VÜÁ5¸óƒ(üïâ©ÍÓèæ‰~‡ë<Ú×è<“ 8&Á¡%5iŸœ=€Q>ƒ‹¢DsÖ¶öÄvLØ:…Wˬ"W9¥ùÇÝs¥d{«*íx„€ÅŒ çâÏC;á¯óJ†8úï©èxC$Þ\ £ÛT–8ÈùýÿĽAá¡á÷bÅ6ëÿçÏo[ˆ§ßÏ,9Oï†k±’„¥Ûõ£ 8&IóºÐ+ pzT‘8ý%äH JOô§õçë·uvÚt#7ÏÖŠ w9ǶÛaÞw~·¯ÿ=æî}ùE…5òK©k’Â^D¿Žþ5:Æ$@ŽMà¹Û"´z5ñRJ0GsOÖä´&·Uª‘÷¯Æ¯¥}Mîêþ÷Ÿa/Ö(c“O–4+æo6aú#Óðìa }›â©™÷cÚoâ±õÇíXöæv|67¿Yù9ÞyüÏxö¿àƒí±HûDC‚e‹€èWc&®¢$Q­¨H¿âªÆƒð  'À Iƒ#æ$@$Ð2 ¨¢íLµÕ2¯=gÝr,ôÂýÃj 4S&“‰J’fzm9-°'ЦµÞ»ï%üJòïzö ý!ÝÛç®\Àì/ÞÀâôãw7–×¯Ð«Ý ºÏ¯©¡R²|vöþvt#TÞýºGÔØª^ÁÓýÂkêíI€œŒÀ-Þ=±~x¬¨-ž†¹È~)¶ú‰lÉÚƒÀ÷ì~¿FÌm“`hä€peþ÷û#xj× ¨‚óµicz„bÅ`y:Ÿ€WŸÁ¸çð'PzU/ye~¾ýzÝ'[®è5p˜¦Ù¿8^zM{#3Îaæ'O#2þ0¶O/M½5gΜ g§‚'®­­J’"*I*dÄ$@$à,˜nËY®ý$ '" Š¶»¹¹±h»]3ºJu! ûþßý¯bT÷Áué*X{0½“&b`òü1íoP5BŠä)纶¼ÂËX÷ígˆÚ±=þýîÙô$þõÍGu ¨[¯Ü1 ‚§ÔÕ=žO$à$TJ©ÿHí!Uƒ£¦íŠ%/x 7%…ã/_¾_§ûOMÇ.oÿå…LŒO}wmˆ®u€dˆ¯b± V,ÊûÃÏ$Ð \½Ñó±z÷éþömýýýÛ—ua^Ü9èw²?ñC÷Î×Ã÷FÉ×e*µ©fËÕÅZ“DS’TcËÃ$@$@ŽK€Jǽ6ôŒH€œ–‹¶;í¥£ã$Pk*PòÁ/c1ë‹¿`Å‘÷jÝ:Q)=H]õzùÀÛR€Õ€;;Ý ?cÜàéƒn^´÷ ×C½¼ÛxiùóÏ]ÉÅ™ËçqFòêŸ1å KŠÄŸ‘âðªpòþœ¯QxÕR'¿ìOVó]=ô9Lð¿×~7·I€Z{Þ…¿ßƒÇ?ù“v¿ªé”³/ÿˆ'ÿû^Msú?ŠÉ}F5ZªAUs)îà¿°VÒÕ´îˆý<\×[Ò½Òh~ÛÍm¨Š@Éök«I~XõÙrÜ0¸#žU'Múò¶U±`CÌr„¿þ¾pÅï6‡â¦Ž*à…]çB”µ®æÊš$º8шH€@«Ÿ¥9º“ôH€HÀ¹|ûí·h-Òó=z8—ãô–H ^üçøVLýüUüT ÿI̺ ìÖÚU[ì«ʼn?;økOOßÚÁO9mH€š)U]ÝëêlPhT øÁžCðˆÿ0 ï•ư>[fÞY-µà»'¶a_ÎÿêÜõ@Ÿ>Ø2òÏðñ°{"¿Î½²ƒæH@-7Ù¿ŠŠŠpõêU¨w‹ÅRòž••…àààC`¹tç~rE§®$bk\º`†W•ˆ«vM¥Û õÄ}°oEC׆ÚuijH€H€šœ@éÿšÜ:@$@$Ð\(%I×®ŽW˜´¹ðåøW<üñqìbVÝWõ“6ŸÞ­½Tg* ëãÞ^ Æøxxãú¶ÞÚvG÷vZñøs.räþª^ç%å`NþE\ÈÏ«³º¥üDT ¦goû ^ ù=\$`ÌF$`%PR¸½ˆIZø  g&À ‰3_=úN$@H °°P“Ñ»»ó)k¼)÷¿ªK–¦ÿë¾ý¬ÉUZ­HI 6#pƒ#ø¡K$àLTñõ%w>ßß2s¾X… '?w÷;µí€NfŠA§¹bt´© ”(I®²&IS_ ŽO$@u!À I]èñ\  k¨ ‰¯¯ï5û¹ƒH€ÊPÁ’¤°q^ 'g~е™ÛñéÙƒV·D¥Çy G(&øß+©dî„»‹[yù™H€jMàæö=ð¨çv~Ïï×îoµî¬Ol/÷çûý3û=äa#ÐGÀ¦$±±&‰>b´" Ç$À ‰c^zE$@NKàÊ•+0 Në?'h|×{xcjÀƒÚëB~ž¶¸õ»}Ø&¯Œ 'ñ³üW­Mk7ÜÙ©/Ânư®µmµH€’@hç~ødôëØþÝ~,:ð–öÞãÕ¤ïîFL¿õaME§¶ÙH€jFÀÅÅZê·ˆJ’š£5 8Iì‚Ð pff³®®®pqqqæiÐw &$ éÆÞ8D{)7rÌñmÞY|w9gLòº|^ÞÕ«tûb ­[µ† ¶Ü`ðÁ ž×Ë»zɶ¼wõTï>ð7v…*ÈÎF$@MAà^ ΪWúÇñ÷ÿm¿¾IÅrkì¦ ²ßå„)7ÿJSÒx_lìKÀñš*IšÑÅäTH€Z4IZôåçäI€H ~ °h{ýòdo$@€Ô Q¯ªš*Œ¬‚$.òb# G'Ô±–ž¡Õ-Ù‘Ž”Ó»rj2rO6˜ë*vCFu„Q=£»¡SƒÅŽI %`M’–tµ9W æL€A’æ|u97 hd—/_†§'óX72vG-ž€[kþJÛâ¿@NH@Ý»Tê?õR…Þ³D%÷™Ôeú,ûvÈ«.éU‘Á1Ä·?†v¹ ·_ ˜^Ð ¿$tÙá ¸´n¥ùXt•5IþbÑA ¨‚ÿ¢¬‘ ÔŒ€ ’x{{×ì$Z“  ›¤|ô¦áÚKá(¸ZU§éGíõ~4Ë+ÿ'\(ÈÓö{¸´AG÷vPi Õ{éËcOIªe]¼%Z †#`S’²p{ÃAfÏ$@$Ð$iÈ‚H€Z $éÚµkK™.çI$@$@$@ F@)?:·í¨½lvL$P'¥5I~®S?<™H€H i 0qsÓòçè$@$Ь\¹rmÛ¶mVsâdH€H€H€H€H€H ".­­ËjL·Uî# ç!À ‰ó\+zJ$@M ??...pu¥HÑ¡/#    ¨®.Ö´v–"*Iê(;! &"À Iç°$@$Ðܰh{s»¢œ 8Œä8 >ãW¦;—ãô–H€H€HÀi Øj’XX¸Ýi¯!' E€ûò{@$@$P/˜j«^0² Z0cïš­½Ï í]«x Ô”€kIº-*IjÊŽö$@$àH$q¤«A_H€HÀ‰ ¨ ‰§§§Ï€®“ 44KN:>ÜWˆ›}Nâý÷ÒáÞk0&<Œ¬mïâÃôŸà2¾‡“7¡pÀh ñ3h.Y²÷áýr0ôÑð-ÿÛ«%[Þß„ybúÕ^|áÓAn‡°»°?FûjççÛÏèÑ!À– _¡Ï>ر&'Ð w?ø†øXÇÉ=† ïmBzV>üƒB1ê!ð)?žfÉH€H éäf¦ã8z ØÏ»J'2ÞžŠ×ðÞ|,è;íX‹¼öØ5ÆÜA$P)—Ö­ n]­Ô†H€H€ŸÓm9þ5¢‡$@$à¨$qŠËD'I I äŸßƒåKb³>í±?i &O˜ˆy;$(ÑÍŒÄå1xuK6rv$`áŠí°{{è$¤šà]QÀBê ™2ã¤ØIÙ‰óòþÝçK°ü³3%s=•ºËwÉçüsX³*Q“g!ý:™w!vúDlË–‘LéxbB4V¥œƒ¿Mv®Zˆ‰“ßFNI/Ü  Ç pvó,Ĭ?U­3…—qþRÅfÚ±‚Šq/ @͸¸´Fk’Ô ­I€HÀÁTô§¦ƒ¹HwH€H€œ€ªIÒ¶m[gp•>’ 462²?âÖ.C°<Šý˜¾~(Ö.‹„zºOÎp,>ž‹g¢"°|z™F#؉÷R€qqÃàQ¡ß>³—öŒDÁËËàô¾0­ԺMñg@éÝBgÇcÁ?Ù‹üáqôÌ%tÿæ Q–„"þÝð“ßz3f¾°Ì€OÅ—öÏ- (O@©Ü6‚ÿ€.Ø•¤”kÝpøXtûq7ÖlLƒ{· Œ~øt8µ[ÏöÀC#¬¹°-9ØöþV´ú ‚}¯½ùäflÇd0Ï$ms6¶}° {2²àÞ)w·ž«Ýú;6íÁÖ´,t ¸ãv­"f¤où)»2ŠýÞU‹TÊÏ–ŸI Ep“ k’´è¯'O$Ð PIÒ ."§@$@MMàª*,,,„»»{S»ÂñI€š€<¶l¼·”Y|kSÎhï£Òc¹ÁðK„!÷çÀ’¹iÁÈþeN*7Ë|¨¢ %¢QA³ —à²ïÖ£S±U[\g”M1¸’wÆq㵉vÐw–½ƒÞ×®QŸË7 ¨‚€¦\[‚è¨Y8Ñ®™„yQ19f#|ü»áhÒ*D?û>~ºx Kþ„¯Šo_æÌÄ&$¡Ð£â›Ï…SGñ¥’ÌÙƒƒÇsP$÷Ê•F 6é¸(ôüqñPb"æ"Cõׯy[WaaR:aWB,"&'”SÈY°-n f-I’I7œK¿&ÌűŠo§UL˜‡H åP)·,WY“¤å~8s æ@€J’æp9 hbf³Y ´j¥2ò²‘ @TíJZaÉ~_<4-ÑÉ)ØÐc½/¬êŽ’Ãz6Ú”†F LeÏ ®­Ùg›É;vJR|?͉¤ø2Ý*^«´uÁw ¸–@±r-lîjÄ “ IŽ?†OŒÅÜÄ71̘x[ÆÎÊCÇÁáŽBÊÞl ñÅÁM‰"w›‹A•Ä…ýFDcþ÷»ðlÁsˆ ÌÇ€›GaÅ 3!B:äÊ®³Rq¹ÈªœƒqgB†DøÐ[0<"ë3&âŽb-Y"v+0må¿1^E…й#£ñO³;Büf#¨–€«R’°&Iµœh@$@ŽL€JG¾:ôH€œ„ë‘8É…¢›$à P²(u°÷°pøI”!y˜:¾oé[yŸîD¦ÉŒìôd,X—ƒÊô%‹†•µ®ƒåÉìåˆß‘ ‹%;þ9 ëö¢U†ŒûI€ª! ”k>]:X­¼|$Pá ¥^SÍMÝ”äÍÕÃ}±uÍ%H›R€ˆð;µc•ýsÅ*³öèñû"iÆx >fIE ª0±ª;b¦H4cß_ BÆ/° çÿø£vhUôíüá I“=YÇ¿×öóh.|w‡Ç)AVÚ,òÿûÒOqÄùÊŠøØ›U°íª”$ ’T@†»H€HÀyðÏ>ç¹Vô”H€–€R’xT’Âa¦c$@MCÀ¶@(£»Ê¢!®ïXRkD-êiÁ å™w&‡±Ç#p—oõ¿²/7ª3ÑwôLš‡¨±IòÉ(ÿ7—¶Ë4užZoô4‹"NcÞÂ(¬Ó,|¹tViú­2gñ è$`PgT=HZ [²%@††ê;·ÞôDH’„ˆY 5c)Þ¹·/| waø„xmUƒ)ORr•¶œ5_';'\Œ^ÚáÙñkqW‹vÞÙ£é¸Ü¹WéiÜ"§%`ÁŽÅáî5ÀSƒÂkñí°ùÌûÎ^ƒG_‡v×ý„°yoaú°8¿m!žþz"ÿЯƳUJ’"¦Ûª17ž@$@ŽD ú¿8É[úB$@$à”’ÄÓSý)ÎF$@•0D"5¹ôxïñË:¾ôs@d"Ô3ÐÖ–‹£;UÖ™_B[.”bÆ;¶îÓjŠØ,Ô{¡<16%§–ìvõ„e©›a2åÃÅÝ»ßx—¥–ÚIH¦Ìyƒ‹Eê£f˜ò‹àn0XÓn•ôÊ  ¨*”kZ/v ˆÂcZ`,V-Y‡ÀÈ•¥Ê*†Ë;þ ²L}ÐáŠ5‡aÏ›{À[”(ɯ-“³ (¼"È·‘°pZ,VnëŽ)wwÅÁ.ÅN Ïï)‘ÖÎ=üHº/`Íú/<- ®g?Ãóób1$n-‚ýªp€‡HÀ˜Óñij™Øu%ƒ$›Ü´Â¿þû¾^øg<ûßtÜïµA­þƒ¨Ÿ'cÙ"`ÁöšH¥$)¤’ľô‘H€*%`÷'c¥6<@$@$@UPJ’Ž;ViÃ$@$ —€)# c§'ˆy(âïV™ôU“'œM&¶)~|Úºö+Å;µ7W µøU×Õµ9Í~dn“ *P®ÙÒ`it”t®Dææa“ÆaUÌÇÖ»Zx]‡‹òd&GüˆõïÞðÀ$¬Šž€Ur¦oH ü{‹ß=„—½$á—1‡b£1&ÖÚmøüÕâ㊌’QüðÇ•3ðtôLLY¢íõ7QÁ•E)9$à<‚°ø©Ã|ÇTÌ}±ñ÷`ëK}Ñ3¦?n2>ˆIý6ÀouÎmø0o>j«Ÿ²*I®:ºH$@$PV?K«ì ÷“ €iiièÛ·/Õ$z`цH Z–ÜcØúÅiô¹ónøy×"ÐQí4  Ç"ñöTLß9ß/)-ÈØ±§TQû&1჆"ÀG‰/×Ìf“”]r‡AIçdÛìj¯¢³T¨¬+Û…ÕFäwÖ>Êä'¨µÜdÿ***ÂÕ«W¡Þ-RÄöž••…àààZQõIç±âÞNHèó bç#nò|LÚzs†uÅñýrK\Ç@IDATŸádÁMrSÚŽ™¯Üˆ—Ä¡I˱îoÓÑËîÇlñâÅ•3gÎLûûgèÔ®-^œp{¥v<@$@$àØøW§c_zG$@NA ??Ÿ5IœâJÑIp®Þ½1bDõOS;Çlè% TAÀ| qFc«dÍŠX1¢¸F“¤Ê’Z&& ŠØkç ¤€Ra™BÓ¥ýzxØÕ1‘m»õ]1Ò£¬ÓcS:·HÀ˜¾ŸÌÅ™ísÐU¾ËëkŒÞùIz ×Àašrdÿâ xéy|4íŒÌ8‡™Ÿ<ÈøÃØ>½4õ– „TÔlÁn¯ˆ÷‘ €s`Ĺ®½% ‡#P ±»ºº¢uëÖç"   ‡&àÚ÷L{zcPIÁv ‡vœÎ‘€ãpm§B#+qô»…èÚÕ'3AW¥íÂ6¼¸s$>˜sžûÄ:_ߥ–Oº©ÔFÇ–‹üd©Cáö£ßå¢ÀR„ ×é&$@$@ A€A’† Ê>I€H PõH<<Ê>¯Ø‚¦Ï©’ @í ¸ú`Јµ?Ÿg’ TJÀµë/±uùZ„Ý Š©6 [ÏÜfÝ”´vb–#üõ÷å³+~·97ul%ÛQØu.¤ØFß››‹IêP¸ý…µ{qá’>7­[)ØH€H€›ƒ$Mœã‘ @3# ‚$îîöd5³ r:$@$@$@$@$@NHÀæ'âÊ”¸"éëÚvèP&ݰ¸Á«ƒuY¬×ýs¤~JÅiµª›¸‹K+äUgVéñ%@¢ÚÙ —qCG»Ôy•žÁ$@$@õM€¹Qê›(û# F€J’vÁ9]    p"^С\€D©G¼:xÕË,4%I-Óm™ Kƒ+çó®Ô‹?ì„H€H æ$©93žA$@$`G@m§’Ä7I€H€H€H€H€Z —Ö­jnëâå‚N¹¦Òí’Ü  h ’4 fB$@Í—ƒ$Í÷Úrf$@$@$@$@$@U°*I®VmTÉÑŸ®”F~² ˜TbÎÝ$@$@ D€A’ËnI€H ¥PAno)W›ó$    °'àÒZnÿÙ~—îí<û ‰Ý¶îhH$@$P/$©Œì„H€Z.$iÓ¦MËÀ™“€“0åäÂl±9kAnn.J>Ên³|Î5Ùí±˜ÅÆT|‚ٙǑq 9ö6¶î*xWýé4­àìŠv]ësEVÖ}b«æ[¹A¥GÌ&á XؽêwöC×dNöçq›H€H€HÀQhJ’¢Ú)IòÌRQ¾¸ýd·mÛÇw  Æ!À Iãpæ($@$Ð, X,ÖUWW×f9?NŠš Ü} ;qænÈ´NÉt&LÀ![ $gÆÈç)o*™òî×Ç`“[`ÊÙ™ÃÇ "*Ó§GcâØ‘XœQbWá†åæJo}e B«ší4¥‰ÏSJ}®âlKÖL˜ø rª°©ðò{ìâc{ŧո§ »W;-YÛ°lå6k'÷³²×¡Ò³x€H€H€HÀQ ¸ºHM’«µ ’\±IÜ]]p‰JG½Äô‹H àªV ¸Èœ" 4‚‚mo(¸ì—ê@þ“¤õ–sò{y÷\Üà+ÿÙÚ¾wãµÍëÛ¸Yw™ÓñF ¹2 [žGBf`cìhxÈѬmq˜;ûF¤"Ø`ë¡ì»%çŽHÿ“zUd`É”w<Êÿ&j6ÁTäƒA°Ú Ö>LÇÆ±èePû-b£¼±oV{w±¿”ù5Z2C³É„"é÷šíÏ—³ÿ§ù·v5‚½ås±°FO Ø"cä—CÔ8¦ü"(ŸlSÍܳ)ÇÂ0M í„øøwàWŒÉ"óÏ/r©`nb«±©äXÙið €F@Ôp9Eðò±ýü)åÖ%xy{—ü<*Å›ÙÍ ÞÚ=GNR ºKEðö–Js.2OŸ•€¦=üºÁfR9\¹Iÿ¥Ï„{H?åïS•ŸÝ(Gä>’k{R]îoÂÂþ¶¨”t¶Ãʯ²Ç¯õÑÊôÚý²ÇMæ_î>m‘þå ”ò.9ñÚkSrˆ$à\]ênËl­IÒÙ»­üÎ`§èu‚yÓE hNl¯5§9q.$@$@D€EÛ 4‡!:Èx{.¶úÏ@âì}¦™ƒP&t!‘×ÖyböÜqH>tY)óãÕÈ6F`towl)»¨ø½Û°'‡{ÐÓH_9³ÖÂÚÔHœ²Û¶; Öƒ% Q|Rñ[Nz2¢f­B^ñçðE‰ˆää©c°êD©mè¸q8»n´]¾ã¸:y_yG0ax¢Õ00‰ËÓ!¹›ðäôåÈÖŽø"Ð7£‚kn:L™…Åú‡/‘ƒð•¯2 L˜õ1âÖ'£g¦b0·z[E¶‰ärvÄabr 6.³Šv/¤®¯`ÙƒÌ3GŒrZñãdNу|žüf­Úiõ˜Ÿø*ÚošŒYIÊË#ø}B<׿ üéÒHŒ’ÌĪÖ@æ¼ZæÜÍÕŒ$a“p¢tÿq‹ðfô â~ùF$P} ¿GŒü¼M‹ßˆñ~¬Ðt1ò³^àU º‰ a‡äè`­¥ ›·žø‘ó¬ÁekÿFL[ñŒèi%Í’¹c£V•9j ‰ÄÊEáðuˆ¿ºÍØ4w,–«Û\I3""n% – ¹¦¤‹–»“}«fÞÂôÉ 1Å÷^ûó€ÐÙ«±`D·2;ÅO€\¤Î´ò.9˜«”‚‹µûpeÁ÷[n€pm-J’Z¦Ûº$Jw7´÷tÇeIðêÒ% –B€é¶ZÊ•æÆ˜•둺vŒÙãÛ|3þwP)I°bíf¤®_‰Ð#‰xg·¤ÁÊÝ) é5m6Ëù‰‹†âˆÄuÅîÕ pü¾ùÚþõ+"p"霖‡4í}u3Ä´is´€ÎÉÈA~?u*¦NÀøñ3±-Û‚“i{à; —¦¤‘±?%·t…ùl†¶ :i©Œ±‹F±n×äì~] L[ñŽÌu#f‡Á²M_!hÒ|„Èáq‰Xýx/dì<^ý:aGÜD ÜZ2·°ìuXú¡¤F6…1tÞÙœŠw‰uûÅ6 ªX²¶hesòûKVS½ ºçûà5 „ÌX©Ý«Råg{nXVMÿw•?{9™éòîÝÇÔ=nýêEè™–€gþ)û«kJ)fR7gû¦Ôq&»ZRvÇ4{ÛçV;»£Ú¦RÐ)Õ]iËAšÜFÇÅ©ûR*6o^E=‘óÒåV%?–JIOݼ3Bó°fûñÒ.Êo‚±z³Ü“Õ}.T¦/'kß›± ÌGó¿Ôìû_X/MWZ#KâW§öKÏ£p‹ªîZåçgpš’¤–é¶.åÂèᆶm\p¥°ô'ÆqfGOH€H epˆgZZjÎ’H€š•n‹EÛ›ßu匚 yjxi¬LÆ'>Û„óÙ;Eìp§r,èÖ ÷=q1ç’N`ö;Á0\¯gÓ×c«,V½#jÕ ½G#yó½Èüê0v¤¬Fì¬u8$Ž™C|á7h´JÞ¥µÒmYû8!SmGŠ $ÅÊèEs±ÿëMˆÛú%¶J€À?¢\³O M”+ÿ8^‚*|q\öG®‹¬–å(eËÍè잃ÓTú¯§ ©@K¡ã¢Òl}Š[{Ðsôóßÿ%^ÛŠý[EÕáNòÛ¯ÁÎoƒß yÊ\ù˜+~‹%b.&ßÚ— UÒOÜâs ïIPdðÒÊH÷Na—„›ž™LÎç{¥Ïi2v6va¾dùêã†ÉU8m¥–GGN&ÄCÄÝb½/¹º0èÑ(ø'NÇçÇsÑé¢DPŒÃÐצt•{ÖEq·›5 ceÊAoMr—‹¯ä;dQO9Á‚¯6”UÒ-zçU ò:…/EqwdÖ E[[„Œ•ŠåÔÁ]#yBófl[9±ë4¡Üï¿<JÄFŽLÀµuký\+/IŽ;ƒ»IÜ\‘]`UôÖª#žD$@$P'T’Ô O& –M€5IZöõçì›@î¾`yš£Â‡¢àä1ü€N²<מªvƒ—á–½ð,vJ½yàè~=3ô†Ö…8›á±¤¹ˆž÷üàå/>—°Ðÿ¶ÈÍ”Ô\þCÐ]{t'$2l`wí´œo!Ï÷6tÉU‹{¾*Y)ËÅvI£3êî^°œ:û”Ô0Ú.©_Ã÷»$LŒŽÁÎÜ1øÁñî/ëýo-›jÌæœz7ŸÕü2ìncРAò ‚·å”„\|qgqî0Ó×{¤ÿ!è) ‰™»Òà?¢_ñØò”öÖ< èw ²!Jë´®/Éâ2?Óñ¨ŠjÁO椂C7wvÑT=>4rÀ„ý©Ù i'&²hYÂøfçVø†öcDAe#Jäìx‰Ùaxytotèì:¨ýŒéVÐyôFôŒ0¤-ŸŽáÃÇcÁÊ$¤çuǰ!Ö@¬½­t[fì¼c  Ø5Që©Öeà@ä}lõÔþ²C0}”K%J±º¨î²±l¢H‚fhŠ–õñ³q~ÝB¤dJ­•“‡Åë½KsJý#q Ïâg”’./“5%ÝTD #©¹B±ôqØ‘ûg%ÊAí`î7‘Bn’û~Î.,Xu\R *…ÉzDøAÒg§a)VޅͰªþâEz’“,ái³¨ê²r#27Í•‰V®Wj–w0î|’æhCðpdJIòóÏ?㪼jÚL"«ò%‰‡(IÌE5=ö$@$@õD€A’zÉnH€H % ’¤%^uÎÙ)X2ñZÌ:Qd¼‚™‘‘ˆž9ÑÑ31YÒ¡||à”6wYË;‘‡iSî+ 0\>±[O„"*¬›fãê~'ã±#ËšàÉ”µkDUá{'r3v iÓnmñÑ~[¥ RÏ{KyÞÜœäh/.ýpRR±<èñÃÐ1ç°¤©Rok*«ÐëÂî·ÄVèn}løäþ0 €Y[ÜËÆÞC}U½ô¤W°Nìî–óÑF¢ß§¥Á1gïÆÓ2oã(Iõuå± Ç£Ãb¼®à¾;{•ñÛ’›ä¤MÈ”T3æ³éâ·?”dþië&\¾b‘t4ûðÒ,y:,D‚H98,ONß.i·´fR‹„¾¸­»¾Jùp¯,ýÙûÞÆ˜ °WžÚó©<îõ\vîÉC¢Ü Á!7\/Ÿ-ž›Ô2IÈöEÔˆÞeÙÈ Õ"ìÐÛ‹ÇÓå?$@eH]¥ åÅhÆg›’ñwÖÉáw¸ D!†RÝ[JA) :µßNAQ¬8é=:›×'b颩ð8”€Yh©÷ÔXJ57zHoµYºmR 3 ’Ü(÷$»–)7ž!èÚY~nó.ÉÞ5^Øüèžó?-PúЍè´Ûަó•{¡Uu×5STwR«hz¢Ü™\«º;«©î^±ªî,VÕñÄGH‘¤~ñ FkŠƒßP„KI£“?šqê€xÚ_ €—¸xé¬Ü‹è×%Jºg¢¢5YÔz!bvY¢>T­ÂykGdV§ŽH8~#wžŸ.×±`æ$`%é:!ûë½ôÄS£­Á&¿0©Õ‚qÜ$jÁâ×{’ŽÈµû￾ âþ‚]pß*ŠA6pt®.­4 kQ—Ä$é¶ î®ð%‰¹AG¿ÖôH ù(÷W`ó(gF$@$Pÿ$©¦ì‘êƒ@憢¼ÃêpëBž­Ï.þþÈ“ ‰’^µÀHŒèm HXwÈBÕ´ßÀ¯ø7ÄÞÍCÄÁéX8y‚í0GÍÆëàTÂx$$݇£áÔÎe%Ûy™Ö­Ø¨‰%çH¯xùå±È{>ÃeÍÒÖâ7íÇí;$•ÕY¡“¦-´‡¢—æ’5m×}rìÔžCð ÃÎ)Ö®YúbƊ׬Ö#YˆQ©QRн´`rh?zxŠN#Fj©Ø»'â· øö¤_Åq¬JHA܈Ñh›)Šc¨Uå¡Qü»·ó°pâHÙa”ÿdq€Ÿ8{RE}ñŒ¤õRÍt\ 5ß=Ð+z66F/ĘíFÍŽÇñ5«û@)y‹ˆ•í± Í~ =¼¸(27«§þ˜±Ræf0!AØ -f#p´ñž*ÏÚ3ÿ%°'°/~¡xÕ 'eý“ZèÿY—$k”w©‚.OtT ú^JA—, º¢ [-I…5õp‹ù›¤áõÚ õº í†Å¡3—0Ì·lÄ6¶ù»ƒ¢0 E» í–ìmPñšð¥·À£‹§Ü-ÞÀ ¬SuK$]aî \£{§•ê.á$ÆMû?QÝÝŒïwAoMu'7”ûbÕÝ”îš 6Õ¯«DD5ءı¯±MvèxŸf#dRÛy·`[B¬ÜÖÂÑÏCÒþ‰ÝäQ丨|Ž$MDšÌ{H%ó¶ß”HàágFòRcé|"ÿïa„^‡GÖké3?H%Üc%J¸\Mõwz]±ªê¦x‡•P ŸŽ@7Iw(Ñä‹çOñÿ¯lƒñ€J·¥š*ÞîîêR#MR¬½‹·T“âíù ’ÔˆI€H > 0HRŸ4Ù ´0 ’´° Îé: ¿ñË:þZw{©Y÷‡¿™*: »æ„7¥Xo™æÚ Å&ãQ‹—$e”‡—7<Š{ôŽLFj¤ÕÚ~Ãf"U^µÔÍÀ$e5 ‰‚˜Í0KÎ{ðÒ1 A‘HM¶i@drñ± 7‹ƒ#³a–\ †õ <ü031Ó¤±J fóO–D±L .›ÄÞ]³—ñÌò¤¦Gx‰ßPãÙ&1lømÛîÝÕ1É›ñdI?¶cݘj Ù¨=e|ï=BX†ic»¸—úÔm„°‘—µ%Â6UßAI‘d+wacû½dþÖÊŒWÜ ßH€Š hÅÚ×eKí¢¿!¼$ø›s)8rÊ„Aß(QÐÍ.§ “G¼¦ “ûÈål¬Š{wüùQt“hÖî÷E¹DxºiJ´-ÇÝ0Z‚Ã…¢¦³mŸ;|@‚ ½Q˜›‹¹_žÏøÏ/LˆêlL ²V†úžÀªå'Ä¿šÊc_šèæB,+ÅVtÅ•í6Õ]0²v¬ÔTw“”ê.ÞÎ>תºUFu·W öb/Žg›,õ”’_œ…Æ ëþ^”DoÏB˜ÄǹûðЍþ|׊ÌEF1ŠªÎ«‡¤%¤•Û‘Rc*7= æíÆÊÍ#Н0ßHÀq ¸Iº-Õ,µ¨K¢”$žš’Ä–"-eWëVVeŠãΘž‘ @ó#`û¬ùÍŒ3" hp*Hâæ&ú±‘ 4k®ÌðöÖäu›§ôc°ýöé!’÷&AŽ’Êžìáa]®+»×U2%J€¤ìQýŸìûÑ{V ϱg£wÚ‘ 0ãÃ¥K$Þ\¯œm’ PÒú-ŒÒ>ê oÈ8Ä%NA°¯²·)à ̳S Šf¯xGÔ3®HßTñ¼U?R„_K`äö'”"Ѐ¢á‹™`GIïòN`Ãþ[¤æ“/‚v"b¤•«oØ ¼$ªÄW%Ž"ª:9÷á—§!eºÔ‚Y¥ú5""n%zÛnáj 8(—ât[JIRÓ¦”$ZDj’¨Š&*å–g~ñkÊ‘ö$@$PW­¤¸TÍ+KÕuTžO$@$àô, öîÝ‹Áƒ;ý\8   °'`ušèà%ipê¼\™³ã'.ÆÌw’1Ä–ÎÊ~°òÛ¢F¹FuW',¢ÔÑ µ —÷¨FŸ;ÑðÉøRcÁd‘_©BÎꛑKeåú¶ˆ Ï" ÀÒsÊð£PËMö¯¢"QJ\½ õ®þ–°½gee!88؉ffuuñâŘ3g>J?W7Äšÿ CçöRôMg+ºú3î݄߽íÛ¶ÁŠãݧ~‰w=ÐŒH€H ¾ÔàW­ú’ý 4T‘4‡«È9 üöî>Šòþø'!9ÈB8“p áA<<@ETT´@+JQð_­ˆµR•V‘¶"ø«ÚŠÏ≠*rÈ!ˆ€@î;@HBî;›äÿ|g3Éf³›ÌæÜÝ|_ÉîÎ<3óÌ{væ»ßçK P€¶¼TvZc$Э_4/®Võ@Qµ‰ŒHd0–™eõȺóRÛ´ä?ôÅN?¾Ÿ vX¶ºÇæ`žåÎùœ-$àUÏé¶ ŠMÚˆ%sÄWÕ$‘ƺ$Q€hvýï.Í~` \[ ¤¤>>>®}=(@ P€hBÁw>e÷D!6ÚÖ”€Mx`îšh6½p{‰ƒÓméA’¶*Hâçm¾='Óm±Q€ @ó 0HÒüæ<"(@· 둸ťäIP€ (ÐD!bµBíM´{î–p¯zÖ$ɯÈ$1IÌ™$ ’8Áå(@V)àÙ*Ïš'M P€ `¤Á„Ü(@ P€ (àâUÓm9V¸½ Øœ5ÒVmçt[.þ&àð)@—`&‰Ë_Bž(@–`¤eÜyT P€ (@ PÀy¼õš$ª»#­°Ä\“D2I|¼X“Ä;ö¥(ÐØÌ$ilQî @+`¤•\hž&(@ P€ \X ðÜ1ìÝ{ç2ÌA ó©˜`²|‰B¤¦æÖë,ë›IRX‘Iâ§Š¶Ë´"k’Ôë"p# P€ `¤€Üœ @k ‰—[ëõçyS€ (@ PÀÙÎm^ˆ¶oÃï-BÇ0o,ÙÔÝoâšþ1ñºk°dýiíR×?Ç>:Q¯Óñòôжs´p»^Dжûx™oϱp{½®7¢(ÐPÞÝj¨ ·§(ÐJLê«W,ÜÞJ/>O› (@ P€Î.P¸“¯z›Òqe(0ÿד°>5ï=ô2žü>c·£¿Çû˜Q> ‹æÏnH¨×Õ;“¤bº-©Gâ]Qü]œÔk ܈ ê-ÀL’zÓqC P€­[€Ómµîëϳ§(@ P€ €s ” öâ¡«=àáá ¿ìCßKûহ0.h¦öØå· eÕ À3O ¾ž'£×$))u¬&‰ž5"Smé5IŠ9ÝV=¯7£(Ð0f’4Ì[S€hµ’IÂé¶Zíåç‰S€ (@ PÀé²Õoþs"v\Ž÷'uD÷¿Cù3Ëq´÷&œ*~£‡fቱ˜ýÒfLõ˜€=Sã“=Œx¿ªS[¸paÕ ÏôL’Ò²2kí/*2™ûK&‰>ek’Ø÷â P€M)À ISêrß ÜX€A7¾¸<5 P€ (@ ¸º€©1 ÷ܘ?u÷kÊK‹1­Ïä>3ñƒGk™#»NÅ ??ofÞ…qR0{ãc˜¾l/6<\5õÖœ9slJèÁ}ª,Gk’WÔ‘z$ž*ÓE~ôì›äB P€h2N·Õd´Ü1(@÷(//Gii)3IÜ÷óÌ(@ P€ (àÚ~‘Øš˜¡Ç¦ÿ>‚„—®@ ~VëñÜÖqx0!Écѹ}$¢»E…yzCm<Í·ÖL¥Žf’”ªZ$æ‰H2JŠJÛ‡¡²(@ Ô)ÀL’:‰Ø ¬$@Ò¦MëÅ|M P€ (@ PÀ9¼âñ§M‹ÑqHfj#šm)ý+Æfª¹‹1éÕÔk/Ü¿f$º‡y¨çÒghEczM“ƒ5IŠÕt[z-9’d”°&‰1sö¢(ÐØ ’4¶(÷G P pª­Vp‘yŠ (@ P€pq˜+FyÁ}È((APh¨ ‡TµÑ ÞA`¨yIüØ9(/·=­VÕ¶Ÿyµ‘à `r°&‰D$0¢7_/•IÂÂí:)@ 4«€åÿšõÀ<(@ ¸®€I˜Iâº×#§(@ P€ @«ð D¨E!vóy{©IåÄ[ ¢Ð ·;\“De’èY(2™nK²KØ(@ P ùªBÖÍl‘ \T€õH\ôÂqØ (@ P€ @£ èG§Û’ Še&‰7§ÛjÔëÂQ€pD€AG´Ø— 4f’ð@ P€ (@ Pðôð€‡úq´p»ô׳PÄQ›n«¤”¤ (Ð ’´:I PÀÕ˜IâêWã§(@ P€ KÀ[Õ%qtº-é/Ù#z3nçt[º)@ 4§@ÕŸÆÍyT‹ \Z@‚$¬IâÒ—ƒ§(@ P€ I@2B-Ü®I<«nËI&I± œ°Q€ @ó TýiÜüÇæ)@ PÀE$qÑ ÇaS€ (@ P€. uIÍ$)--¯6Ý–V“„Óm5úµá)@ `ĈûP€ @5Iªqð(@ P€ (Њ$HâhM’bS©Õt[*“D-c£(@æ`¤ùÍyD P€./ AO‹Ôp—?!ž(@ P€ (@z hÓm©ÌGZ‘ ˆÈ[zcM]‚ š_€A’æ7ç)@ ¸¼@YYk’¸üUä P€ (@ P€! M·er¬žH±ê_=Hš$q-¸ P€õ`¤>j܆ @+àt[­ü ÀÓ§(@ P€ *$“¤D}‘Ì‘Vsº-µ-Ž})@ PÀ¾ƒ$öm¸† ìpº-;0\L P€ (@ ´:úÕ$Q™$ÞUÓmÉ>X“¤Õ½ux €“0Hâ$‚à(àJ2Ýk’¸ÒãX)@ P€ (@¦¨Ït[R“DêèÍGÕ'‘âïŽU6Ñ·æ#(@ 4D êOã†ì…ÛR€ @«`M’Vu¹y² (@ P€ @-ZD8iR“D#z“€‰H8å–.ÂG 8À/Ixõë½ÈÊ/vžAq$*àÕ¨{ãÎ(@ P U0“¤U\fž$(@ P€ (`@À«òŠJ ô4w)+/ײF,3I$Ð"M¦Ü²\nÞ‚¿)@–8Ÿ™ÇW|Rõ¹Ýsò"ß;¾Þ-5·‰$i"Xî– €3 ”£ôb:L3Pš®~ÔóÒôLõ:k/ CZ„ÂüÚ!Ì7Hý´C¸o°ör¾\‹òâÆ ŒÔuº‡²NãPâiü-ñ=t ŒÆý½nÂ}ê‡&uÉq=(@ P€ (àÞmÔt[d’HÝi–A}ê-IÜáÁsp_’Òá¡N¦_§0픆uÂÛŽâ§i*`í.§ÉóPüº/ß \\À”œ‚”çþŠCÝãÔ„©ÈþäËf XÓÊMƼ]ÿA·÷ïĤu¶ û¬»ð5(@ P€ (@·,G¦Ûª ’TÝ–Ó&ú:·âÉPÀEާd#<Èþ¾æ<ƒK:…¢­Ê ùédš‹ž‡mO€™$öd¸œ €“ Ÿ<ƒ´‹UæÈ{6Vc²©¬ÿ;¾^û¹"º?žøkŒí|Ycížû¡(@ P€ (à42Ý–cA[…ÛÍ“¢Š,§99„­XàLZ.:…V xyz¢_ç0$ž¾X¹ŒOÜC *díçó (àö¥™YH~ìé5éKßrº‰õØ’œˆq_ý#WÍ„$gå×ë¤Ïgäk#Ñ!þ5¶ïß%ß$žÑ²Ç¤˜;›ë 0Hâú×g@ ¸¡@yI Îÿ¿¹H_ö߯9;• êÛ#þ#‡©ŸË´Gß^Ý¡þ_cÿ¿VKäǺeça{Ê~l½°R_Džç–Xwsøõæä=öé |1ö%ô sx{n@ P€ (@ PÀ–€éô‡è:¡-w½Žþ÷£¸iîtš€) «»üRV½<3ñ¶v`p™$)1X—DŸRK/Ö.‡Ñ³QôuÍn €•Àî“©;ƒc#ÑE]—`G}šW"‚|¡¾-÷1¨[>Ûy¿$e@&l®/À ‰ë_Cž(àfe9¹8=qr×nlð™ù¨ÀHÈä;´Ÿî± Ú_;Ÿ\ßi˜ö#;*)3áÛ³;ñæá5øôäæM›u*7W®zŸ^ÿ"®î0°Aã䯠(@ P€ `:†'ºÞ‰wN•#! °W"Kü¯–ãhïM8Uü*FÍÂc1û¥Í˜ê1{¦,Æ'ÿzñ~U~ .¬zaç™wEˆñ ‰9[Ä2“DŸzKÏ2±s(.¦êHMoo´»uÂf݇€«/·™-Rï[lèíé…q‡k? Ò±ôÀ*üóÀ§8Ÿ_¿¿€dçbìšÇ±RM½us5n6 P€ (@ P€õÈMü2ËnîÉ{U„Dµ  >ÈùìAÄ«š’9²{áT úóÓøfæ]w ³7>†éËöbÃÃUSoÍ™3GÛÖú—eðDp®Ib*Õvçã]5MLÙÓÆÓE%æuÖÇãk P n©GòËÙ Œº$FëÜAM•µ~ßY˜T–—5i)*H"EÚm5/ 莭ªöÉôÑ}luá2pìÝáb'ÇáR€p%ÒÌ,œ¼îŽzH<|}öà½èyätþß0jd“H¬]Û· üÁ÷âÄÝ+ñÏ+~nAÑÖ] ½.,-ÆÄµÄg§¶êÏN (@ P€ l ^2 gÏžEʺïp`ÍSªËS8ðæ½jÂ­Š–±Ïm‡‘¼1ÛG"ZM¡ƒÂ<½‡áG}:£SeéÁËé¶ä`R—„™$†ÙÙ‘5’Òó[X‚Þ1æš§RO¤LNR²›*¼\í9-G2IÚÖ8†¾`L¿Nãí:‘ª/⣠0HâÂC§ÜG ¼°§o™‚‚݉õ:©À®A}[óú_áݵs½öÑù¶ñÆ}&àÐ]ïâåá¿C¨oû-.+Á]ßÎÃ7I;Þ–P€ (@ P€ÐüƒÈÈPtëØHè‚N¡úx-øxzcvÂ]8pçÛ¸­ÛUµô´½J%¿ý#v¥²ÝK)@ P€ (@ ðKxå‰Ve‘¨íF/x¿êbž…>~ìÈ4=ååK1<Òñ™é«2I̵Fê–d‹è˾’I"6 P ~'S³µiëºD˜sÆôL™:Ë‘v1§Pë^KD>÷ÓFõÆA5½×º½IŽìž}P€A'¼((кR_Z‚Ìw?rø¤%(·í+„þvªÃÛ6×2 ×Ç×ýÿù$pâHË-)À-_ÏŹü4G6c_ P€ (@ P€ux!0´râ­:úÖ½Z¯Ib4 DúùZÔ#ÑàëÍé¶t >R >'SsÐQk×ëH&ˆdl¥:8ÝÖÅÜŠ I ýLßµ ÔÔ^!øï¦CÚ´^õ3·qIœã:p @+Èýö;¤üñE‡Ï>ðúk¿ãø èëð¶-±Á¬Kn÷7-B¤Ÿí¢göÆ$’IëþS¿MeψË)@ P€ (@–гBŒN·%™$ÞmÚÔ4§ÛªAÂpHàDJâ¢ÚUn#Á’U_Äô¨\Qǽm™$² õs÷å=p>3Û_¨c¯\íÌ ’8óÕáØ(@·0%§ iÊL”—:›9 ]¿|mB 8´4æ•ѰýÖ¥è¬æv mINÄüŸÞt` v¥(@ P€ (Ð|ŽO·e'“D¦Û*qì߇Íw–<œ[ ¿Ø„óyÕ‚$2b tè™!FÏ@¦Û’ ”ð@ß:7ѳ=$cå›Ä3uöeç`Äy¯ GF ¸³€Ô!¹ïa˜.¤:t–íÿüļ¶^ŽÏ“ëК¨s\P ¶Üò†Dôrè/ü¼;S:´ ;S€ (@ P€hG§Û*ªµ&‰±º&Íq^<\IàDJ¶V@=Ö"“DÆà‹ôÜ"‡N%5§¡>•ÓvÕ¶±§ ¦\Õ';§0ÈY”“¯cÄÉ/‡G ¸§@Æï"gÍ:ã'§þ§Ûañ ˆ|z¶ñmœ´g„_°šzë\ÙÇðeº­û7-@I™Éð6ìH P€ (@ P 9ª‚$ÆÅ*[DŠ´[7ÙO ·[³ð5 ÈT[ÒâÚWM·%¯CT$3ϱ IZv¡Êi+›jÃ⣠ÓèíKJ7ÔŸœO€Aç»&(àæ’=’üû?9t–^žð‡ëÐ6ÎÜ9Ä'_ûú†Æfbú1¼¼÷ÃýÙ‘ (@ P€ @s8^“¤z`År|>ªp»d™°Q€Ž »… ¶ÞˆjW=¸ª‚$YùÅí0Ez ®¾ŸÚvЧc¨6=×ás™µuã:'`ĉ/‡F ¸§@ò“Ï£43ËðÉEþñ1„?ú€áþ®Ò1Ì·¾»íÛ†òŸz gó›¢ÌðÎÙ‘ (@ P€ @=¤àº´bƒY ö¦ÛòQE¦î£Ãä&pkã²kÔ#‘n§ ·©Ïf¡õ~R²TÄ*ØRž¿¯—ÖÿTš9›¥¶¾\çœ ’8çuá¨(@7(؈Ìÿ3|vÁw߆öÏÏ5ÜßÕ:v ŒÆcžƒ—gÍTs[ç’[R€§wþËÖ*.£(@ P€ (Ð"ÜVb0 D!¾Þ5oÉùªLIZäò ..P®ê¾žHÍA|ûàgäç­-Ë)¨žM’¡¦à’)²¬[¶ê'EàÛ;I"ûèˆ3ó¬wÇ×."PóOd8‡I PÀ.<ý ¬æÿ„m‹_¿>èøŸÅP9›¶V»Í²«; ÄN5|>+Ž|ý' ÷gG P€ (@ P€M)à¡þÍæ­%òmu#­H}£]Ÿ¢Ë²¿Iù¶»å¶|NÖ,p6#*°oUDLª‚$%•D'U@å¾nÀColÒ¶«\¡ž\È,Ð^¶ö·\\çóNaHJÏ­³;8§ƒ$Îy]8* PÀ  ~ü ¹_­7tf~¾èüþ2xúŸÓÐŽ´ÓýÃ{]YyžÛ½ÜP_v¢(@ P€ (Ðô°õ­t[Ç..-³Y¸]йK…pL@ê‘H‹‹ª^´]–Ét[Ò²,2IÞÝr¹…%8¥‚%ŸüXýK˜ç3óµþB ’tTA’ü"Òs+¯Œ¿Z\€A’¿(ÐZVïùÝ::ݨ?>ß¾½ õu‡NÞž^ø×•sÐÆÃØÿ–>:ñgq‡Sç9P€ (@ P€n …ØN•¥e’¨¬ë&™$2e—LÄF 8r> ^*›«[dP‚+‚$™jz-iòéÚ}2×÷ñQXýÓim™¶RýJÎ2I¢’j»8—Á)·tKWz4v7ʕΈc¥(à„çòÓp·ÇWôT;|øÌõ(d?âß O<ä„gÑ´CÙÓ{7tÉ&Y¼o¥¡¾ìD P€ (@ P ©ÌAcS+k5I*н[ŽËOIä.§Ü²Tás Ô-pð\º«©¶$PbÝÂü´ERƒDÚ™´\d壗pŒé×TPä@R†¶N~WA ¬H1vGZL¨9ó„AGÔœ§oÍwŽóŒ#¡(à6oú¦²R”x¨©¢:ŸÆ€™ÀâG£`ôÐ5G¢žs:¨Û<‘ç‡Ü oc)­R›$§Äü ƒ»g7 P€ (@ P€M" Óm)Ü.Y"ÒO¦Ö²n’I"SnYËð5ì ”¨éëžÍDßÎa6;¶õÖj]Ì)ÔÖK@EZŸŽ¡Ñ#Z[·åÐym™ü:¯ê›Ä„T¾6ú$Jz÷Tõ‰ô麌nÇ~Î!À ‰s\Ž‚pcrõ] 7¯©q†ÿ?‡awçâñ—G"mâ•ððõw—N¾û¶}[Ë‚¨¶¡x¤ß†NW$+o0Ô—(@ P€ (@ 4¥€d’)Ü^$Ói©øØ˜nK2I¤1“Dcà/ ØŸ”®}öv°ÙßC-òCZE䨅lÈg­sx€–-2°[¶N®ÜV2A:‡›§Îª\hà‰—§'"Ûù!¹¢¦‰MØÅ‰$q¢‹Á¡P€î)ðCÊ/8–}ÖîÉ}†knHÅ=ˆŒ—gÃÃ˱”N»;vѪ I€—9¶®Sxçè7uuáz P€ (@ P€M. ™!Fj’èY"¾6§Û2ÿ[A’&¿\<€ üx4EËÐ5ÜîYE¶k‹Ôìmýé´t‰‚‡ÊúvyÏh$¥çá¤*â.ÅÜ“³ Ð#:X[çè/©crAGÙœ¢?ƒ$Nq8 PÀ><±ÑÐél›®ãn2Ô×;…ûã7=Ç:ÅïÎÿŒ´Â,C}Ù‰ (@ P€ @S Èt[’%RWÓ)Òߺùù˜—›¬Wñ5(`G`Û‘ I[û_8•©°.¨à‡´³* Ò)¬j:­+zE£§6þr»N¤B¦Ä«-àbgÚâöÁ*HRqœÚúqó 0Hâ|ׄ#¢ÜL೓[ ÑÍ].Gûß|0´7éô`Ÿ[ I©*à¾æÌvC}Ù‰ (@ P€ @S Èt[R¡®¦Rô©µ,ûë7y™Ib©Âç°/°[5Î\ÌÅ•½;Øï¤ÖH†‡L·•_dÒ2E,§Ó ðEï˜P¬ùù´ö#õHb£ÚÕº?{+£TÆÊÅÜB”–ɤzl®$À ‰+]-Ž•p9#YI8šdhÜS{Ü`¨_Kv*Ì+Ds|§)!,ƒÂ{:Õo’vêÇN (@ P€ šJÀ[IŠKJëܽ>Ý–­L’¶5I˜IR'#;PRˆýå/÷ ƒ €ŒéשV |”© ‘ÇR´L‘®‘AÕúÿæê^ÈÌ+Ʈ㩸6¡ö}UÛÐê…Ô$‘Iº ”°¹–€ý<$×:Ž– €S ¬;·Óи‚¼ý1®ópC}›¬Sa"ÿ8ŽW;@|º½S¾Æc3þV±.Ó,ä!¶‹¢UÛ¼/?]õrØú>Ÿ>ç¿~3æ®À˜µ³Q&)ÌC^iHÁuòòŠà€ªÑ™—µñ €!k2ýØÓ?.Óž×öë\~ŽçœC\PLmݸŽ (@ P€ @“ ÈôYúTZµD¤Øº©[$iŽüýÚFÉup>c²ñä;Ûµ€ÈàØ-{dxöèhQ_ÄÞ¨õéµ68_/t¬šXö¿4> òÓ$÷D M¹Õý¸ó¶+6†81HâÎW™çF PÀJ`û…ýVKl¿¼¡Óe¶WX-5%oÇSŸÁ¾Šåq*p±päaÜñðq,_û,$)4sû"ܹ0Ÿ~<<ˆÅë’µÞ’L0éïX1½ÅÖ5"¢¡•/K[{>毜®!y(V];unùß}h„ü¥¡HÛ8ñ¿³ñ~8¾Óœ17òF„%®ÆÎY‡ùïýs6á©/VŽyÌ#K0÷æ>Úöµý’)·¢ýÜŸ^[7mÝŽ” ’Ô©Ä (@ P€ @S h…Û L·¥×ñUÓsY·EÚú“@IDAT¶…ÛóY¸Ýš†¯)€wG;¼~ÿ• 4#Œ²´këƒ`µmV~1zDÃÃÃÃè¦õ ¯—LÆf[ 5»@Õ€ ±½²—Öü¹ÃCS€p'Ìâ\œÈ1§rÖu^WwXWmýÎ7ŸÁ©Û`íÚµøüµ™8þÁ?p*æJ ÅV¬?§õùá³Õˆ›49›_U’,xo Ö~þ†:‚t*ÄÇO¿ˆè©Ápíÿ[¸uÎØù·¸îºëpçÜO=ýf-‹Äù*@Œ×>]‹•ŸŽã[W#~Î 5¾•˜Þï8þñé^óØ×ï|®Æüé’™X·øœ0ðÅ(xàÊè†FýÓÅÆú±(@ P€ (к2NÄÞ½q.×ò!&˜,_ª¥¦æ6FË$1$©-“ÄÏÇK»y[ ŠK³Q€UŦ2lQSeîÛÑቾ=›dh\òEôýÙz” ŽL–žkþb©­>­yYIi²U Ê§"c¤5¿3yî @“ ìÏ8rõ_]-Ü/=‚;ÕÕM[?üÑ÷ðìà4ü÷µxäÉ×Õ25¯¦W,î¸1+¾:¨^ÁÇ;ƒ0eL¤ìÝ Û¦aH„šôʯîýýPä—ÔySÒ·xýx4ž˜X‘íaJ¿ŸY ¡S±`Éß1óÆ8$ÿû5P_Œ()ÎGФ è¡ÒOBzöA´úoØÀhuŒô­/*áZ`ßbŒŸ:ï%àµ÷æ"¶j®ZÇ3,²îŒÙÁ/'kÝWR€ (@ P€­MÀ„íÿœŠ°®3ñÆAÇ oüswRw¿‰kúOÄÄë®Á’õ§5”ÔõÏã±N4H¦Ï’H¹*][«Ê$iS£›|·Ý_J˜IRƒ† Z¹ÀÞ3µÏÅ¥ñ‘õ–˜y]_\Õ'7 êZï}Ôµ¡|†C|¡¦ g«)¦2läOH}Z²š=Zn‰ÁÛT-7@™ €« Î:chèÂâU΄‘TÏB|ñÜ=X|h$™};æ^7Ïzöpë`ÆZìÑÇ£'áRU,Ä:·¢ ³îi«dÀ{V½ô{—hón©E°C=ÌùÓ¯1De´é3?®ž ‡3q…l`xÑ¿_y;ï݉Õÿ ³þý6¬\!²+†÷0癩ßG²’êèÁÕ (@ P€ @«È݉3߯¦œr\Ì t]ô%Š÷¼Œ'¿OÄØÀíèïñ>f”OâùÀ³Ä#™$róO‚ zm[;¬­p»ô÷÷mƒ|f’Ø¢ã²V,°ÿL:<ÕY}:†Ö[¡g‡ó nœ1F«•0 9Ÿü›“TQuUËdÉ¿#ÀÇ[õ.Dâ×có óô\Ú*åaÿ·99~@UÑõ6þïjlûþˆ*É$m_‡B‚Á9$¿zbÆ/?‚žWÞŒ¹OÏW[&ã§ó¶Ž]9ˆÊ'½CŒ}ÃãTn²¡¬Êó (@ P€ (àÞqöl F¨ õ+ÞFBÂ%¸iîŒ š€©ýG vù-HYõð̈o †I¤é™"öv§w·U¸]¶ñWÿfË·ú"š½}q9Z‹À©´\tPÅÖk @:‹E(ƒ$v/EšªG"-"¨­Ý>-µ‚™$-%ÏãR€n/p./ÍÐ9Ƶ‹1ÔO¦°6}(~*Ö©-‚ââÔïãxå½D­û˜I#ñÁâ,Œ•4Õ¢¯ü=æ«"ïÏL»U½ ÂÐ~A!•RìXú:>¸¶+Öβú…)TÑõøRæ½¢ùõÁsó§cÖ3³°úEó²ÛžZ†+Õaè}*ýáoþ·$#mì“ó°nêó¿Úü:îÆßãž>zšŠy™½ß1þáðñôFqYíÓ„–#£(a¾íìíŠË)@ P€ (@V%à‡˜?d\…‡ûLÀÛ£^ÂYƒ¸G{o©âW1zh˜‹Ù/mÆT Ø3e1>ù×È·¨ ½páBCjúÍÛUt]n’Úk•™$^ÿp²êàë…^;Qί¿Ê,“>ÓWàc½³_¬X»TËu‹Ô> ÕXÕÆamžžˆöÃéÜ un’V˜Å IJì@ P€ (@Ö#plÕltŸð ^ú,%·$Tü;Æ ñƒGk™#»NÅ ??ofÞ…qR0{ãc˜¾l/6<\5õÖœ9sl‚YOô Iaq©ÍþúB ’Haç6ž¶§\ögD§â£› dãç“i¸¢wm*­ÚN÷¬ ’ôëV[§YhžnKêy¨)Â\¡Iܺ}I¸R]‹vm}šlÈ©jº-ù3Nj/9[s¾9›ÇC P žYÅÆ¦” ÷s,ûÁ˯*H?t°3¾€È\ñ ƯrêÉê¿~˜ÿÞ%vzYì¥Wÿÿmø©±Ö§Ivˆ‘ I¶AïúŒÛP€ (@ P€.&P¸ÓU€äõmg1}h 32€¶AÔ¿µ•±Ïm‡Ïæ„ãc1¨}$¢»©/°%ûwœµ†Ÿ93¤®¢ë…ª¸»>5—õ>äu€¯7R*¦¤±µžË(à.xÿ:—‰ûFõÆ=#í×#ÍTEÐ¥NO‡ÐúÝShn¯0•IRZVŽÌüâZ³Êš{\µïå/÷`Ãþ³øþP2þr÷eµumÐ:™n+Ò §Ú’“ªÿÝ®‘pc P€î/PPZdè$¼šh.ƈáXºös$'GN7bb;¡1CçÒü½ì§ª[O¦Üb£(@ P€ ( ¦³‡pQ=ÎÑ3+HF-Þ¥²DËZ¬š»“^ýH=÷ÂýkF¢{˜|ã{¶¥ ­èíØƒþÍ躂$%*Hâcgª-9¢ÌÀé¶³go×8r>K ÈÈ׫›óµIÎgæk'£j’¸B“鶤]TYµM½ç,ç"Sn>p^žžØq,‡ÏgBŠÜ7E“L’'œjKΕA’¦¸âÜ'(@%PZ^fÈÁËÓö\´†6®³“¢;ŪL×mF}Œz»®GN P€ (@ ðŠ¿‰jº{mô‚wj¾-?vÊËmO«eo{ëå2…Œ4¹áX[+6•Á[M·e¯I&Inaí5ímËåpõû“´ÏÁ-C»á“' ÓÐùzÛ¾7’\$‰q• ‰ù‹žé¹Æ¾8ÛÒ×ì—¤ ˜ÊÊðÇÛ‡`Á§?a“ ˜4YDe’ÄÆGµô)Û<¾ý?•mvçB P€0*ÐFÕÓ0ÒLeµÏYkdîÜǨQow¶â¹Q€ (@ P€F¼T€$ÐHGÃ}ü}¼µ¾2-PmÍ|3Øþ¿ý¼µÆrÓ’­yäÿÕ´OlÍ'ðýá ]ÂQ¦‚™'Rì:— ‰äyE»F$,¨"“$·°É@÷'¥k™*q€}gÒµI—uoÝÂñýá䯨m}ÈŸi8Šl×D³©Ô8¢c ìÿ©ìØ~Ø› ¬üÚ+v•g*°Ú’/-rKŒù´58-—å¾ùœ (@ P€ @cTd’Ô5UV‘šnË·–é¶U&‰´¼ÂÚƒ-1fî‚Õ¿{s V?{ÕÍb¶¦8§Š°ËÏeñí×Þ\£õx-A’ Y ðµ›iÒô#vìR¸] ¶Kð­)š¼O}k«ö¾•÷oCÛ/g3ÕN«•4\JÎ\ÌÕ®OC÷k½½xH1ûH'n‹Aë+Æ× I ØÇØ7“Ò‹ìc¢‘†âÒ»1êìãEÜ\úbpð (@ P€ €M¶ªp»ÜÍ+ª}ª,¹©Ykáv•I"SnÙdnô…‰§."UM$7o¿Ü}ªÑ÷ÏÖ,iúG!Je‡H=Ÿ“©95;V,¹•öÁΙ}`kÐRÛ#Äß›(“DŸÊûVÞ¿ i2!¡Ô éc®A2¼G{mwÛ*®QCöm½mŠ vI‹b&‰5 _S€po¿`C'x.¯aÿS«<ˆ©yy6¾©`oyå†6žä%áÀ c鯦¼Ldæ5Í·œ¤ÎÈ…‚ ¬¹(¯i ‹Õ<—P€ (@ P€¨. É&©+“D ’¨›ÂöZƒ$öhšdù!uƒX®ÝàØHÈ´ClM/ðÝsè ©1"ÓhuŽÄé´\»NQÁ€( ’ȉ„«)·Òš(“dÏ©4 ‹ÔÞ·òþmH» ¦2“€lwu=¤É5‘k³Q]£Æn’$McÎØ˜IâŒW…c¢ÜB Æ?ÂÐyÏiøÿ|Ž|±×ën_€yæCÛ[^50výw¾8R±AÅŠ]˦áéÍ竺ÕòlÏ«wâοGýW%奠¤¬îL€—B fîÔ4Ü’ (@ P€ €}-HRGÑõBUØ]²Nì5£àímÏåŽ IÎB§°\ ÉXHo¢oÿ;6*÷í}@MítPý\—Щò$;…")Ý~$5»ÐiëXTž„Õ“ð@¿&™nKÞ£|‘ú!ò¾•÷oCš>ÍY¼šnKormäɵjÌ&Á. Š1“¤1U¹/ P€. Ð-(ÚÐ(f6,¥×”ôf-^‡G^ûk×®ÄÔ€uxú£°·¼ú rñÊÕ8’m‘n:‚·WGcÎÄ>•]ó2ó O‰k*Ì«6?nßé˰ìÿ%TöL°ìo±Âá§2ŒÙt5híð¸(@ P€ (@ƒ* ¤®é¶¤°{ÛZ2Idê!iue¤»Õ!püBbÕ âžÌ3>ß°›Îu®U¯.-+ÇkßìƒÔì;°K¥EGu³?EBŠMe•Ëô'’åP ‹A®3Ý–Œ]α)2Iž3gŽôVÓcÉûVÞ¿ i’Á#‹.‘A•»‘k#ã—k%׬±šxBÕ~}¼œ3gÃ~~_c p? Z©@ÏàΆÎüç‹G õ³×éÈúÏ1ópELŽ)À¯®ÄD¿@ùß+6—Wí'‘sïÄééK°hR¤mûûúMÁ_‘ˆÙ·ÿÁ‘DZõ¸tŠÃcü±zÝ>y¸Ûæcé¬á8±z^Á£X:±³§.Gp×}Øjî‚™KVâö>æ¿lj9øË¨QkÏî (@ P€  ÈTY9_@³±e¾ºá«Bl¬F`[sM’œÂb[«¹¬äZH„±º ^—›ÅòÍz½.C#Š»Rol8¹ÉÿÌíCª cBýµš0r½sxõÚ®z ÁY‹}Û»°2ÝVf~1JJËàݦñ‚Ç’³!5OâÚ#V66«i±lý™r6=Û\€eVS•]Þ+—t զ貳dð„©±Zþ™$A܇®ï‡ùïÒ®ÙoÇ\b¹I½Ÿ'g¨Ú2Î9Õ–œTã]¥zqC P€î)Ð74ÖЉ¥fát®¹p™¡ luZ÷<î¼u*fÍš{îü~H«˜¢ÊÞrm˜´tnS_¸qÞrüubµ4«—­Ã¤WÀKe€çç‡ßø×´ •éýŽcõ‰¾X±f-V.™ŽãŸ,G¢*RRœÔ\õ­ÿ>]¹kÖ®Áß§Æáõe?¨¼’ú·R1´±QkC;c' P€ (@ P€õTA’Ú ®Ëw²%“$À×±uˆà¶>Úâ,uƒ•­áç2ò0oåÚ<·lg/æA®IUC®I¤*(}:Í~qËmùÜ1GS°rû1Ü2´®êSmcýƹ^³ÂråÅŠºêF¾+5™Rª¼¼R\ÝVÛ}"¼¹û“«ƒs"5´l yßÊûWÞÇ–íØ…l<ôÆf,ßxgUdÓÁó˜ýÖV,[wÀ²›öü\F>bBj,—k$×J®™\»ÆhçÕ篃ªyâ¬Ag½2(àòa¾í`tÊ­Ýi‡x¾cðÚçkU0c ž“Œÿý}Åþì-×ùž†op(¼¼Traò¬Hƒ›û˜ÿ'™hÜx­OBпo4‚.‰hÕ-$¶¿Z“¯¾¡ïÇü(ýï¹±¼Ô=/½Øw ög­¾­õ«2U´}sr¢õb›¯…÷´¹œ )@ P€ (@ 4—@Ÿrj©I"Ó•©§~ö'vñõn©Kr1·¨¹†í¶Ç1••áO*@rX·–y.Ëôv¶"hÒ©"{An:Ÿ±ºá¬÷åcý$›bÉW{ÑUe>̼¶oIpJ𭀂žI"5>\©é…æm~~<–‚§ÞûYÅxâím8âÀo'Rr´i¶ÄBßêïcYV¤nÒ<÷áTƼ5k4þ9ýj¬xh î¾¼>Úq'T¦”e“Œ“Ž6‚$ÒG®U75¥—\;¹† iò¹“š$¶2 ÙocnË Icjr_ ¬†EKKÜrÁX0Àj÷ÚË’ÜT• rzhgðÂe7Þl=¨ ÎÙ^nžÁ²jO–ßOJüx)â¦OT¡Žª¦B´¤ñb=u\¬ê§?Óû›ã'>°ÿ)} Û»Òá¢Ê²1Ò.‹2æld_ìC P€ (@ P >Ajª¬uãÓ^Ó³LjË$‘må[è¶nÛÛ/—ÛذÿN¦æàÉ[i?ò\–é-93_{ªß¸í„¤‹¹Ú·óõ>|l¸À†ýg!Si=¨nº{Ù˜z*<ÈW›Jϱ<¢Id4™¾Ê•šž1¡¿Ç,Çþß͇µ¬¥Å¿‘<<µà…åz{Ï%Èš¢c£ÌõCô÷­å1ÖíKÂyõ¾~ì¦ÕÌî¯eŸ|ñÓ©ÊÝKý¤ŒÜB-3¥r¡ÅÛjª-¹vr ÒΫŒ©o"õgœµ1Hâ¬W†ã¢ÜBàòöý ÇWg~0ÔÏV§.Wܬ~ë“TŠeaÞ{öMº}í,¯^!ÄìÈNKQ»>åª@É´$sÄ^³i¼-Ì{úü´ž coÏæå’­Ó) ²öN\K P€ (@ P ‰$HR¨¾5fï[×¹õJ¤vImM¦²¼ñY[_®³/ 7Œ%;dP·í§« ‚|»7©rƒó™y ð…ŸÊÞ‘ÖEe”H}‡4UDœ­ñÖ«ìÔÍñ!q¶ÿÝ.56‚ý}TöTMw Ê5j̺wfö÷$™$m<=`=Å[f^‘ÊÉÄuý;iç,&ûΛrKЬkÓÃ…›ƒ$ò¾yëí»çµ÷üàØêÖ2àPu¬T½J5ïO>öšŒO®\Æ´3*ø(ͺæLCöÙØÛ2HÒØ¢Ü(@ kb[¼²ÿtÆ ̬ŠèÛïYsMHÿ)xê6¼8íV\7~>ˆ¼ ŸÒö–WßC†MŠu/ÎÀ´yK°/î> ­E‰€¿ùï‹Úf‘þÕ7W¯ªÿõÞ¢¿ÄS¢ƒ¬ÖרÜî‚•Ç7Ø]g¹bLÌPË—|N P€ (@ P Edº-iö²I²+бUÔ±7ÈuSòœšGnˆ²ÕO@‚{N]ĈUó$ŒTÅ«÷œJ«¬c=ÕÔz&ŬÙG@¿#{u¨u‡R˜=%«fýÉbh¯®Ö$ð#ÁNë ÉÖÃÉZFÅU½ÍCT0CÎ1I}ÞëjRDZ75m™Þdª,yK“L5y_aÇúÒø(u¬5¥œùý}¼bê­X5¥VmM®|–äZÖ·I€G2‚:« ¥³6û“ :ëˆ9. P€.$‡Ž*ËálžšúªŽöÖ‘¯ðâ¥ÔÑËÖj?ŒžµWÝ—‡¢Ò6ÐÓPí-¯¾þ“^ÄÚI* %/…mU5‘Šæ×+Ö.Ò_¡ÿôXª¿ÒÖ­0¿RË?®XnÙ? ÿt¬­è¢ofôqgêAÃA£±/3º[ö£(@ P€ (Ðd’I"-·Ð„0÷³óÍ™ùí*úÙˆeFƒÜ<–лO¦iEõ)vìmËåfŸ•—IÕQÖ=ª’dxöxwëìRE³¯V…©O©·#Ô2½uª¸h2å–dŸX7 ZÉ~å¦=¯ƒµŽí×?¨ylõ”€Âu=¬›ÜÔ¯ë&¾õ6ÎòZ‚nÖ5n¾WAyïèç48Öü>“÷•dlèM¦¦’âîѪйž}!µK¤^QLhÕ—W%Sj[Evˆ¼¯e;yŸÛjzvÉOêX²O©Ó#•wœô®NóÈL§¹(àŽ*V~Kב†NíC_¢°ÔþüµuíÄË/À"@RÕÛÞòªæg~!©¥€ uÿ¦|ý¯ƒŸÚ½oo\ßñRC}Ù‰ (@ P€ @S èÓheÛ©K¢/—“µ5)p-ídÅ7Ç?Øv sßÝŽ˾«ñÍôÚöÓš×Éà¶ê†ì%C+zÅ„¨‚ÖÞ÷é¹E©b-¾•/u/d #ËBØ•«'+·Üw¶ñ:X¢Ôñ¼ò:tªº¶6‰–)æTF…eö”%ËB¯ïak;g^ÖEM‹uVe%•©ó&EÕ÷œ¼X-ˆ! „&ž¾XíTÞSÁ¼?¼ÿfüë;­Æˆ¬KÖ{Û½£þ¼’©¿<Ô8$À+ç|ZÕ?‰o_û´^öö×\Ëk‡6×(x P€n,pUôt ´îh}Ú/ü´¢AÙ$ÖûsÅ׋ö~€|SÕ_Œj;‡{º_[Ûj®£(@ P€ (Ðlò-mi)v gæ!@}Ó»®Æ2¥ŽÜ¬—ép>úá¸vóøÉ[i…¯·<ßlç㪒›Ì'Sr´›ÏÖç ÓkI1ðÏvžÐn&ë7™õ~Õ”[¶¦}Ú¦¦IÊPߨàÚKxt¬:óŠJ´ñ$©«Im iz} íy†¹Ö†«NmeÎ;¡Þ‹Ò¶¾ M—Õ¿K¸öZÿ%>òžÔë—|»7I 2̺®®ïßYMO‚|îe*­VÛÊûW‚"ò~–}XN§ïßòQ¦âÒ¯’©Ó¿KÝ×F¶—@Ž|¦êªK"93Rˆ~Ô%1¸}X¾?”¬]‹²gn ’8óÕáØ(@·ðôðÄ}½n2t.gòR°xßJC}ݱӅ‚t,Ùÿ¡¡S ñ Ä„®WêËN (@ P€ šZ@¦º‘º$ç+nîZO¦ÅѳM¬×Y¿î­2G¶«Z«o”Ký ©!0¼{{mê)‚Ìf_à˜úV½LqdkÚ!)ÞîëÕFûÖ}œúf»L#dÙ¤ÆC²ªƒ!ß~·lß8¯êÌøj^KûÏ©éœäF¼¼—ëj+êqX0×&úººöálëeÚ<É9¨¦É’÷ã¶#ÉU-»IÆ<¨[¤6ô]ÇSµÇ5?Ÿ†?${ãÖ¡±(QÙ(ýügøª©à¬3Iäý+ïcÉ"‘÷µ¼¿kkR_$À×[ ¾ÊÔr},¦£«m;ù,É9Èg«¶&Ù.’I7LýY5~HWm›Ÿý¤m@×êÁ¡ÚöÓë$i u“hu¿í=ÞžÕÿòeáÏ?½ –´ÆöÇÿBnI¡SŸÚãø{éEê mÂN (@ P€ šT@2ì1$!¤bJ®ºq׈îÚMQ¹1ÿàu}µîCâÌ7S:i¾™Z×>Zëz©µ Mê7X7¹Aü›«{iSM¹¢§õjtQÓiÉ}Ël)¿óxŠú–~´ö­}^‡l6Èu)—l«¬7âár“ßrª³¤‹¹Úg@ÏÐ²ÞÆÙ_ËùôPïA©å!©ruŸ5†-9©²ýè8›¡M7n`­Ÿ¬“? ¤ýæª^ðVS_Y7y‹³¼¯åý][“1ÍÓGërߨÞ56ö¶Õ?KúgË^?)6/“} Qé%hÂ¥±Z×iêXäqæfìŽ3ŸÇF PÀbü#0IÕ&1RkC‚3·ü _ܰÐάñ†¸íÂ>¼qÈXMÉÎù]ß;ïàÜ(@ P€ (@F覦ØÙª¦˜±Õä&iœª7b¤I=ƒÿ<0J»±¨ßø4yn«Mg3A}ÜͶ€Üh«`;µ_î1ý:ªÌš_ºwi§Òr´oóËs©Å_dˆžæi´yD¥î&Ó.‰§þþ­m ¹Éß1< Z€ñ´ ’H•ý µíÑ9Ö]Ù»þ½þ^ûfŸ–E6¢§íL+T?É“:,’‘vmB§Ê¸ÿšÞ«‚&*²m*WT<‘ì‘÷¾ÖæûÙº¯¼¾qPWU¥=ƒj¾ÿmõ—eòY’÷½|¶jkR盧 ì蟭™jzº[‡vÓ¶­m;gXW3üä £â(@ ¸¡À“'«oûc÷ËÓÛ𯃟»¡‚íS*,-Æý›¨TÌ2Û¬–Þ; =‚«þÒ`µš/)@ P€ (@ ´ˆ@èmÎÿ Yù5Ž/A’0nLÊFëÌR¿`Rz}sA•€øÈTEµ5ý&®u™ÚI¾­¯×‘õúTF»FTvçu¨¤°ùDjPHĺþ†ÍÎ »ª, NéM2²º¨ ‰+·TM5%–L#v‡ ÎÙÊ‘ó»ypW­V‘LW5QÕòmô&${½½÷³¾ÞúÑ‘‰¾­|¦jû³Gj¦È5`ñ9‘±KpÅš±»u®p&#(@'臉±WåìmK°?ã„áþ®Üñ©Kq ó”¡S@Ó¼Á÷êËN (@ P€ šS@¯¿ð‹Õ7® KJ!…¬ÃÕôY irÓ9U†×‹<7d_L×$Á(GnÎ[:H É~°¼Y/·t ¯6]¯ƒ¥ZÍç¿$e  Ø¤êmT–jöª¾$^ÕÖ¸™¯-Ï-,ÑžK½ Wn’±ô·Waáä &{M Ëf\î¾ ÷Œìa¯[‹.—÷¼|¶,§D³Ô^‘Œ+Ë`¢åzgÎ ‰³_!Žp+ç‡Ü¯¾`lÆ?µÕ¡bõ¿VµHú†2µÜÚ‘¯)@ P€ (@–›º’ý±÷tõl‹jid’Ø:›7wŸH³µºÕ/“úÒR$ZŠYKí…rµŸ)ÙZ@êr«i’x4f»¿vMÑê] Rµ)Œ¶¾Â4sÉFñ—e®ÞÚûkÁ¢º¦ “@É¥ñQZ±wgÓÜXîG¦Ý‘›®RHœ­¦€ŽŽVE°2ÅܨÎÌ+• ñÝsÚMë‘VA^‡šö–K¾?œ¬ª,§²\oë¹§$óâUÀ\~ä¹,csùLÉgK>c¶Ú–CçÑ¿k¸VSÅÖzg_Æ ‰³_!Žp;ç‡ÞP_s18#'·æÌv<´õe#]]ªÏÅÂ,Œÿz.2Šªæ­ëžrbü¥®ýq=(@ P€ (à¾ß}a§cé¢éò~{U$u÷›¸¦ÿDL¼î,YZ;ùÔõÏã±oªã=Úk5,o&JF‚´Îa ¯±pY($IÕ¦%ÒvÊ_š€LÑ$.úG5Hdpl¤v£÷Ó'°æçÓ芀šÓ¤ñ:Øf–÷½LÉt•*FîH“©ÎÆôë„o÷&i?ò\–±9€|¶lýÙ£_ó«û8vÍçÌIœéjp, @«ˆô Á‚a:t®K|†y»þãÐ6ÎÜY¦÷Õ8h°‰œËÐÈÞx¸ïÎ|Z(@ P€ (à,…»1sò2¬K߀EK7`ÓSË0ùµ-xïÞ—ñä÷Ÿá³ /âßcÞ‡Ê!Á¢ùÀ³&4ÚÈåænõë—¿Ü}š­ÕÍ{ù&vC3Id7êŠbSþ·íX£Ùv$â"> i>^ž¸.¡6ì?‹¬übüæê^6wÇëP“EÞïò¾—÷¿|m¿Rõ8:Td,Ès6ç°õžoè5w–3ôr–p Z“Ào{ÇÊãðíÙ†O{¾úÆS€—ž0Ùð6ÎØ1µ0ãÖü»Òž_,¿ú)Ãõ\ ï˜)@ P€ (@·(<{ñ>®˜­'þªGMŸiîtš€) «»üRV½<3öK*;ÎãëݺãR<¾â{üæµõè¨Õ¶˜4¢»ã;³±ElT;L‡÷¿?‚ ¿œET»¶P³|µÚV®foNQÓ™IÑÅC|ÚVScˆÊ*±W|œ×¡ºrž*Ú}&-WsûûÔË«º¯ÞÓþ+™bkɽWhüÔçˆÍ¹¬ßó¾^Hº˜× kî,gèQ®š³ †ã (КÎæ¥bàÇÓ¦¦r¤½xé˜;pŠ#›8Mß#YI¸éë' Ž´E#~‡GûÝåÈ&ìK P€ (@ ´ €Ün²ü)--EYYäѤ ‚èIII2dH£4w÷BÝ뇜ć!\i¯—öFùÒql÷&œ*îŽÑC³ðÀÄ ˜ýR7ü¥Ï왲ŸüëaÄûU gáÂ…U/|–_îƒÔò@d•·EˆG:zf¢ ÊÜ‹íîr3Oö›Z„lõØšoîI|¨òôÌA°zlÎx¯CÕûÓÇäùGzäÂߣ¸jŸ¹•€å{¾ÞÍ~ÍçÌ™Ó$žÌ$iVî” @Ý"ñö5óp“švÊ‘ÂìOý¸’ñ×ËfÁÓÃufM”Ú*“7<ïP Qße$éwgÝ ìA P€ (@ P B °çŒÚ»R.]‚$'¶¬ABüµê™âÖ2Gv/œŠA~ß̼ ã¤`öÆÇ0}Ù^lx¸jê-{7ä$xbo]Åøàf¼ænvA œ¯¹¤fìÒ u]Ãt»ku ×S€pA: ËÖ'‘Ó|yï¸aÍã8ŸÑéÏÚTVЧ\†›¿~ÒáIlP¼5êiõM æü.Ó“r€ (@ P€ @]^!h‡yX¾=CõÌÀ®u1 .¸j«Œõxnë8<˜Žä±èÜ>ÑÝ"€Â¼ª>|F P€­B€™$­â2ó$)@gx¢ÿ=jú©3ø×ÁϦÔ3é÷᯵Œ’i½ntÊ@ÂÑì$LÝðglOÙïйIç o|zý‹õ rx[n@ P€ (@ ´r¿¼¾i1:ŽÓB1åäÜ¡W1aÕÜŘôêGj…î_3ÝÃä‹Y3°-e¨ôf£(@V$À I+ºØ<±±ÁãëÜ “»_»âF£OH×ïÏrÙÅyøøä&¼{t-ÖŸÛ…Òò2ËÕ?ñ Ä'׿€Q9¼-7 (@ P€ œKÀ2@"Ï$q®ëÃÑP€ @•ƒ$U|F PÀi$àðÀæ¿â?‡¾h´1u ŒÆÑýµº%ò˜çоOäœ×¦ò’é¼~Hý»Ó¡¨´Ä¡}Øëܽ]'¬ºaA£rìË)@ P€ (@¦`¤i}¹w P€h<IÏ’{¢(Ш*9ów¿…gw½¡ž5þ̈m½|áŒp¿`DTþ„À¯VD>µ )…H)ÈÐ^˜ŠõüôÝØyÞ¾æ„ú鋸H P€ (@ ¸¸€;I2ÎÃ…l S÷xZVƒwñkÖ‡_˜›‹ïþ…†ªŠ5€)ãŽ&e£]lwÄð"»É[ÄÔs9µ¬QdÊÀ±£õÅÍø˜ª:EüŒ»É%wà4$q‹])@ ´„À—§·aÚw/hŠ–8~SSŠÕÏ2süªYê¦4Õyp¿ (@ P€ @Mw ’|÷ô™|Î^YÖ‰9K‘Puoµ&—8±@.þyMfâÜÒÈÎŽÃ’Ï!6é]õ™Œ)ê"¿ýÊ2,OLǽ ¡N|Z] ;¹ãÿÐgŒ/ÒËç@»š¹{1;¨?~žñ(Ú-{Ñï$bé¯ÀÏx]šî¹ÞÓ=O‹gE PÀ}nê2{ïx wÄŽr›“’éµ¶Œ sNa€Äm®*O„ (@ P€n.P¸3'/úô X´t6=µ “_Ûíæ'íÆ§gº€CgàÀÚÏðÙgŸaÆE*à•‹·ïšŒ§6¥cÅ¢¥Hßô¦õ_ܽøÃÔ0ûkàáÑóßýó§öWÏ=0Õ1 iï‡óÑ_½–õKÖŸvc8W;µ\|ñÂøkÌ<`T;xW ï“ñÊS›°aé"|–³ Ë&?ŒÝ™¶?ã…ßÅÔ©³ñÀ5êúöo~ø&¦j×zVËåõwµ·„ñ2Hb…‹(@ 8›@û¶aXyí||9v!zwv¶áúëf]r~ž¸—E]bx;v¤(@ P€ (ÐÒ…gb#^Â Š¤‚ø«23[zX<~}LYX·wúxK`Ãæ¯B†) [÷ŽÂ¸~æ‹Úû*$ (Á¶·—!lÒ¿qjÛ,Ì›|'0ù8›¸ó&,ÄÁÜݘ|ç<Ì;U‚‚£óðȘßaoa}ÆíW w¼°»,6W캇~Ü‹çoég~§Ô³¤Ã¶?㦒4¼ýöϸíßg±í±|L»ó-L>z»Þi‡ }“  òú7îUkî½1HÒÜâ<(@HýÉ*yõòG!WjÙzËkøÇÈÇàåçJCçX)@ P€ (@ À”•$øU~½]Tì½hþ9y\O 0%#f,Fbz În‚Šv`Å®óØ£N%@O7h‚p¤£À$7ןǃ£ãÑeè0ŒÂ üjloÄô¢žoC¦É#T;Ÿž‡/Nu©”wä­ÂæD…ù–ƒ1áÜÛ€¯Oå…FtÂEd§¶ý/)DÂSÏ`l| ¹¸å!íù% êùªãÈñâõ·ÔuÅçž®8hŽ™ @kðñôÆÿë;Çîþ»ì!Dû;w°D‚#«®_€mþ‰í+¾¥Ñš/ Ï (@ P€pIÀž#0j¯d˜Û‰-kßÉ%Ï…ƒübFañÒ‡‘ê§ž_‰GTbÐþ3Á˜’¤T\äÂ;TöP,ä_ÝíFkEÝQX¬ž÷E{A4IÕ÷pÀ«7§Åšq!øìÑè5»?!çizÙŽîƒGÚýŒ‡·õ×¶4©€É¨‘qæçÚo8åõ×$\ùƒ$®|õ8v P U H6ÆãýïÆñIÿÃÒ+Ÿ@¯à.Nãáíé…»ãÇ`óø`û„¥ßu¤šhËÃiÆÇP€ (@ P€pXÀ+í0Ë·g¨M3°kÝF ˆ vx7ÜÀ9Î}ûÚz,QWRµÂƒøà o¯D…oÄœåÛµAÙõ#0%ZRÈE‹ù³Ôs è­ôü*´z—ýjV$¦¨©›ÞÆÁ ýõŽ|l9Ë ¦F ¼òè{ÚõÏ8’¨‚a=hð3^¤OÛe>Ó9^ÿ–»°sd¯ÆÙ ÷B P€-%ÐÖË3zß‚é½nÆWI?àû?Á×ê±´¼¬Ù‡40¼~Ós,~¢ÚšçpmöAð€ (@ P€ šBÀ/¯oZŒŽ#Âð¤ìÊ;ȹ#¾)ŽÄ}6ƒ@̨Ùx~T„y<¢mÔS+± !Aï¬Ã'GÀC»È3˜s‡Ê QA“p‹ù³,Ÿ«ÐYÛ#ðú” j_óT `ï”בoÑ¿·‡¨]ÀËÛGM¥UÜèýà;XüAÇÊë¿<1ñ¶?ã»U&‘Åî-Ÿ#Á^1¼þ<.ùÔ£\5—9M P€vR 3U dVŸÞ¦=¦eÛíÛ’’‡ ݮĤ¸ÑèÛÝq[ P€ (@ PÀMäv“åOii)ÊÊÊ &“©ò1)) C† q©³6å¦"%Û Q1¡æé—\jô¬µ@nFªšB-‘jÚ­ÊfÊEªšs«mT$øŠynF†Ú—·ÚW`宸ęLH=—¢" Q*‹¤êB×÷3ÎëïÌ׺ö±1HR»×R€pyÉ(ù%ã$¶¥ìÃö û±99G³“>/ ˆt ŠÆàˆžÑKû¬#ü˜^î0&7 (@ P€ €› XHä¹;IÜüÒñô(@ ´:IZÝ%ç S€­Q ¤Ä<ù¦··¹RYai12ŠrÌ?ů‹ÊJê„0?õãÛÎâ'þª (@ P€ (@º$©Kˆë)@ PÀYªòˆœeD(@ 4ª@vv6¾úê+ >]º˜‹»ûµñAÿpí§QÆQ€ (@ P€ (@ PÀ…$q¡‹Å¡R€pTàÂ… øúë¯QXXˆ¢¢"G7g P€ (@ P€ (@ ¸µƒ$n}yyr @k8~ü86nܨEIZó»çN P€ (@ P€ (`K€A[*\F PÀÅ~þùgüøãy€õÆ ‰.ÁG P€ (@ P€ (@ ˜$á; €› lÚ´ ¬qV ’Ô á P€ (@ P€ €ë ǯ|ˆ4‹3ñï0ï¸ÚZ,¬ïÓÒ,øå:'ôD›ã_àÕß͹±ëú‰ÛQ 1<sgÜ(@ ´¬€ÉdB@@¼¼¼`Ê85+Wbåʵ8‘´/,ù yõ^á‰qûÔÿÖkÛzŽ›P€ (@ P€ (à @¾êÕ¤i˜>}:¦ýjÂÓvcÅ?¿@ºƒû±Ýý">_ó)Î>QCpë—ÁÇvG.¥€K 0“Ä%/M PÀ¶€G† ‚àÛE¿BnØÜ0²<Ó÷¡èbý ·—æ¤#'Ùöñ¸” (@ P€ (@gðGT‡H„Iô", ÷üÖ ‹^û»OÁUmvaù/üzêp•ýQŒíï½¢ËïÁÕ.âÃåßá%8~¦+f=8[>ü‰ç%äâž—OÀø>øäu•:¢Ú§o‡Çùâ‡Ý^è ¤ÀÿÞúgJ¤{\÷Øjÿïáï¶È?~ùj?ýÇMÁØÕŸN*À ‰“^‹h%¦d|ýÿÙ»°¨®s_àÿ”¯‘a-&“ãK”HhHbB­”šðVhS®=ï1Å+i$\LML¶…´Zª=…j¹±GrÚgNZ°•‡Ä£Ð›Pª'Á|P®pbˆ™DBPtÔû®=30|Ì Ftþ;ÏÌìÙ{íµ×ú­=Ùוּþü6b¾x#ö›«ÐŽ(ÜŸµ QÝðÂî&„D% ý[_ÇŒcu¨ûàf|si´/n{êÿX‡ðÅß@¢Q7 «¯ãUÔüç'ÀÜxD}~!Â>éÂK}Îtv´x{ÿo Na:â¿òî_-ùÚÑ!Û_RÛC"qWJ:–$Õ¿‹´Eý›G-jÐððµï¤Ãèéÿ"6 êÿTƒ×Z;r}¾œé(§åà4õG"üÄ›¨kîÄõq_Fæ7—hùØ»Zñò“¸9² u/7a <©}‰QzljùL P€ (@ P€ €Gû9·]aÿ„ÛB£m!9¦'?»Ï¡ç½.ØÏ©wgðñÉ.œ JÀ·þÇ=ø¨îy4÷ßÜï/:ÿвßÕâÝÄïá~ù»ý7•õ¸çk‰:ýW|ЮŽ<—+vãôâ±ûþ ïýõØUù{D,GßÇ]èê¿Y«rqî­ÿÀ^jÄ=·-C¸[ñ¸J_àp[¾Ô, (0õ> 四S€öéQ0c]Îr¬,ÜȘ(1—#÷É?âÔÉ7Q±é'8lsÙ:jQ\aÆYÝè‰JÑÕvÝj×Ñ7ñß½ÁXxWŒ¼¹EíB[ÕÈ]·§"£qSH¶¬ËÁo[­è¨Y‡çöÈ“¯ ¸À„²Ýµ¨ìÚjÖki>ŽZà9@ ʾcB±ù("ãbpòí šŠÐ*eÿäͰeC!6Tw".îzì¯(†ie…6nêÀñFlÙ²ëžb¤¼G*Q¸röt:+í*_)@ P€ (@ P€¸(Љ”8’:_ ©¶žE(–=|?bn GÌ×þ'–ÝŽ–¿îþ†C²7H~N „}v¦¬IO•ëÃó:ßyX~èޝ݇iÓpËâ¯"RþºoyWþ†—_YÞú­¥˜†˜»¾$ÇvàÃÓêl\(à›ž~웥e©(@ L6ùŠüs)E;P¸D‚$]Ò{by1Š*·a‰X~û,+èÅÌ{²„Ô¾nAÂ"#Þª©’‹°0blý¿‰Yü*~ñ, ³â`m~Mª¸¸ïY‘õL%²%Ø“q¼v%Μ=+CjuÊþx¤.“Þ‘Y¸«¦ ýÙÔ+Ãü­ªÅåÈÞú"²â<œX’Â&ÌKÃÖæ#N"+=q@cÁ^ôËTBƒ¥¶†4TVæCÎŽ¬Åó‘j*Æ®ÖåXäÑtuÙï+®ø:n2=„m>„¥¹‰*g. (@ P€ (@ xŒ‚H‚3àˆŒšõù™ØP‹s”åP7„UðcpѺ˜œÃ[¬À¾Ž„»1/~>ÞyïïƒIÔŠ{O•sgTÔc:B\çÔz¦H@eZãX8ú­ PN=xòaùñ |E€=I|¥%X P`Ê ¨‘>#oœá¨X¤ŒˆTÁ µ8AÑXžeDÝ “ÈXð§ZÀ”u·#ÍÏ|ðô:ùgÏç YƒÿÑcÉwþ†ÆçššŠÔV¢NŽWá‰[—=Žc —? íûý±`DEª¡®dooµHTJ"f8ÿ%ïÆ\t±È\~+Ìy™Z>H@GBAªgÏôKŒ$E hÇï…Iꪂ4ª³.†/«‰¶Dàö{ èÝ÷&zœ[øB P€ (@ P€ ÀXgÑýñIô<‰îí¨Ùñô‡&àž˜i2Ζ„DÎ~‚‰[œù oÈŸà£9–÷€ÙüÜ¿8 7h¿˜t?ÏY8ã ÚF]Ô-ò—þ{8øŽcjøîwÞ’~$7bîõƒ7 Üæ:|Z`ôçÁ§‹ËÂQ€˜¤Z`¨nÎÐÆÐYKH_ ˜wàÏ{,hB òâÆž«£¯¯OþárüœCņ-”>”‹†”ÕØ^ùCDKwŽŠÌ‡$Yzl3‘ýë—°VB‡ßx¿ß°+ß Â®*ƒ”T>‹wŸ3aSîv|¡*w(Ð1,éHÒf†© iy›ñûûnEäÙý2·ÈvÕÛVë5Ó{´Ëíˆ.´KÇ“ëÕN- tÇm4rÆIúÕ\óófcœ°Œ[~\¥(@ P€ (@ L=Pù«»^F¥¨wU=2A†·¾ß1È·!&ÈŒÊÍ?•½¡ˆ ÈpÜ CÜÝ7ÂüÒ6üô%ù=üFDõc×ÎWðØÊ¹’ÏYìþÍËøn¦d$GÇ ëk·bÇ® 4;ÏyDzU¸QnE¨ºÇåÒÖƒÆÊh;øDŸ`Ä'šsW!Þ@IDAT… ¦¬€³÷©Çú»GK¤×Åêøb”oªF|v™Ç …ÅbÁ 7Ü0vï ÛG8"'KˆÃìÈ ´îÙ³)ÒNö¢µ*ÓPöâjÜzÏ—qgR9šlªi1$cшħžÁ Ë7à‡;¿„m+Æ,ö¹Ó’¡,sæÝŒéõRõ‹Ry§ÇÙÓòË•àH ©eõ³ñÈ—oÂ[¿ÝŒFéòÌ ƒœPGµcý/j°ýñûaçÏXWÛ‹¤¼[ø)EÃ… (@ P€ (0–€,ùþ÷ÇÚãØ6m¾•ÿ8úúÎ xZ‚%áXÂð½ïÏõs›ðxâIœ–%ÂÃT”s8#÷-‚åSÁã² ­?ßqÈ´ÛÓQpëWpRå6aŽQ´ñÕ|·²HÙ¾÷ýUƒçà |Q€A_l–‰˜:2¹™„ F,Ž¡©´êÇ®¡· Ã’‡3P^¸™)±#Žq¼=þ 8 3gºmà*üPຠ²øa¹Yd P€SR uç*¬i\ŠÝÛ2%dbGkCŽIÜÁµœ”±GOuŸBò·FœkÌ*×N÷W› V‰zèõj\+;¬V»s]ÞÙ¬‰Üô:ç˜WîÇ ®>·¶K:žÜ¼p±vn›äs!޹QdݨÇ;¿5a=~€ªl™LÞjE@ˆjêµX[+°ì©`ì®Z@Ù7à<Ö±›Ï (@ P€ € ¨ÛMî5$°úQ—zµÛ탯ڼü¨j,*(@ L2Ñ?žddu(@ L [J¾“‹:ÉÊ´u©HÔ¢&^; «&œ=ZÑÕÕ%¿ ™³²Ëë"¡M%@2ô¿ƒ@þ"†·}nu¾322—ëÜ:Égp‘u­Ì½™úÍÕÑëÝö«„g¥r½!)I!û†J4˜ W(@ P€ (@ P€ (pEØ“äŠr23 P€$`ﺃÒM# ãFÐ¥Îj“Þ!o½õî¾ûn\wÝuTO—­µ³Çp#â¢"Fed·vâð1àÖ¸(HFép(@ P€ üKÀ½‰ZgOÿj?–– ÀT`d*µ6ëJ Ljwß}WûÃ#&F&áB P€ (@ Pà 0Hr ñyj P€¸$Ï\Rj&¦(@ŸøðÃqà 7ølùX0 P€ (@ P€ (@ øšƒ$¾Ö",(@ËèééAPPÌ-2bžËÈ‹‡P€ (@ P€ (@ P`ª0H2UZšõ¤&µ{‘Lêæeå(@ P€ (@ P€ &H€A’ ‚e¶ ®–€š±»»³fͺZ§äy(@ P€ (@ P€ (0)$™ÍÈJP€SY «« áááÚp[SÙu§(@ P€ (@ P€ À¥ 0Hr©bLO PÀÇ8Ô–5‹C P€ (@ P€ (à7 ’øMS±  F  ¿¿3f̽“[(@ P€ (@ P€ (@¯^÷r'(@ ø´ÀG}„ÈÈH|æ3ŒyûtC±p (@ P€ À¥ Ø{ÐÜtgƒ‚F7ý†¹ˆŠµ(@ P€—#À Éå¨ñ P€>" ‚$±±±>Rƒ (@ P€ À8ŠŸ­[‹‡ìŒiEøMþè<쟺›mh({Û÷w#㙟#3V—sÌEdË$ üD€?=ö“†b1)@ ŒèëëÃùóç1}úô‘»øž (@ P€ € !ÒUƒ˜ddddÈ# ñÇFKm1þÔfu¥à«›Àñ·›`±´£Ïà¶Õûêåã=Gî¥(à?ìIâ?mÅ’R€& z‘\ýõöñ (@ P€ (@É&`Z[ˆ±Î>#¤`Õƒh—Jöž>먪µæ_nGÍkÿ€ ›Ìš÷Uääå Ѩn{ÙÑ\ó[ìØý Þ=¡öê1'!¹O¬@lH'*ÖmÆ!Ü€/ÆMGã®}8á<þÑ‚$D:o›Ù:Qõ|ª÷5kùëgÍCúÃ9ÈZ-©Ž=¥(©úK2¿‚ÿoöýCrÑÏBÚÊBd/Qi¼”AëèaÃÁšç±Ý,ç—"êõsž³f0í$nO=mõøu™‡Þ=á¨ï¬<øè÷~«æ= …#Ë®_ä¡u~&žÎ_Œ#5/à7ƒYsàá5aÑìÞ1yøÖÃØZuó\‹üt5zêmR‚Þ>‰×þé±!ž]/¦óŠVB>Q€ð I|£X P€—$páœ8q —tS€ (@ P€ð73§mRdG¤ëV-ᨃÌWbï@ɲÔ¹Uª·©…¦6lÞUŠ›þ¾[ªµ½1ññèoiAKc%r/þúv¼ÞÔ"ÙÖ4”:¾`ù)l©Ñ](ûîJT»ûÕÛÛ„Š MèX»…K£`ûðÚÛÛѾÉ= >?ƒø;*ñù#^ÊP¹Gʾ‹un'èím‘üsðñÆß#wá`Gímx6·XJ bâ1§_ÊÞÞˆ-ÚöSÔ7´WÊÔ„q´êY–;Êf¼+un±@NHó˜‡>ë¨S¿å´æ:ö7¢]²·HpªëÀ¿yv•:qƘ¡ë‰k €ï p¸-ßo#– À(“'O"88Ó¦Mµ(@ P€ (@ L&sÁCHMMÕË *Ы*—”‡å zt¼¼Ý 1¦aûî½Ø»·F• ;ö´áxËÛê,1Èùa)*_,AZR’æ}€PÇN’ó°kï^¼¸u5#zÕ¡îpº*Vo}Qòߢ´í¨ºMh³«Ug.R†²]{±{{ž3 ZŽõx-ÃÉ÷ëñS-@âÊ/6g'iùWÿ´rÔœ,¶÷þ®H$D‚e9O¢´rÖf$#)>¸.Ûö¾“£xÈÚ,åÝöm|ÐÜ ƒÑˆ”Õe¨,-Å/¿'iµ¥&ŒqÌ LsÖÉ壒‡¸½ñꪙ8ÎÀg P€þ Àž$þÐJ,#(@ªɬY³Flå[ P€ (@ P€SD $jÆu:*l}%y&m]õvP˻'¾òÀ¬ÆŸjGáC©ªûR¾º|'QÍZ:pX•}¿ Ä%K\Ž/G¹tÕxý­c¸ Gi’Á×ãý#–¬|;j7H㜠‘ &õ~hó¤G/Dšq ÌÎrÜï¹ ‘Çv:‚>r–ê_< ­Ï‹ôÑ–Þ7ð¾ ¿et¾J7û6¨J“UYhB¥”=>ù«xpe&–DëdÏ9„8Žv>ë°tý¯pËÁWÑð_5(Ê?‚¦gþ’BúâhÏÃÑ6z}òV§(ÞmôjÇ €ï °'‰ïµ KD PÀ«€š¬½««‹ó‘xUâN P€ (@ P`²d•ü^zp¨^"»±yµ³Dã6üÍbÇÉÔ<#²ôZ)+‘HJNFrræ¡a sñbÙF˜Ò’ u0émG]u9rÊG«8‹áa®;ûˆ¸ÁÑ—¤ÿŒ "8ºO®‡+tᮾ#® ¯gœs¤H? ƒ[¯‹H/eøÇyW½RþPm²úÈø$)2â“î}žÀXïÞµÙˆ7ªröÊðaÕ(.0¡¤Þ0rf¬…@ìh(ù6r ‹QY]‹ôÏDJŠ ¹åaqã¶3صދO¸ÖEÙKZÕèh\(@ ø‘ƒ$~ÔX,*(@%ðÉ'ŸÈD~zm¸-ŠP€ (@ P€˜ôA®ð„ i™2p–ZzÑþQnŽ›ã¨¾ ¿õ3JjÛ¶Rä=˜„ëÃg#9m!Ž×”âY™äüŒì¯”@Ë®2×PX-hlsÍ·ay³W‰õ0v×izaÁ¼ë¡›î8[mšñ˜Ž}UÚÄñj‡£'†£žžÛ¼”áµ÷]GÉp`?*Eé¶m(ýá ÄHPfÁ_vôLq%‘W[g=Šò6cÿÇóñ³Ê*ìÝ]×àY‡Ž|è–øø¤ÔÃv/8ë“&sœTm+ÆÚo|qX:÷7Ú1nN?éx×Õ†ýí½·:5¾ã„rˇ« |YÀõ_.#ËF P€njË ƒ« (@ P€ À”p뤸ÅÂPтަ-x`U#Rfv£®É1œ”éþïÀÞ}-2¼TKK.ºLYˆ<^ïÞ*wÅNÃ!§^KELõ~9ÑîœĈ¯ÜfD´. ñ[ÕÔî(0­BrÐØèÈߘ‘™­ã´€·2Ü›òu„þ¹•2ÄVá2’S梹®Q+£!ídÌûlšÚe,0y<úQ;–ÄtÁ5hØÜ¹7Hê³èíwT»Á„†{—c¼U%n0WbÎqö”› û©Œ8Fægù¡#…Þº H}Mz¬ôºEHähouºë·ñÁœgâ (@_`O_n– À5Ô–êI©º‘s¡(@ P€ (0ù Aný5ts±Ðy_Íã<¿9ÏÑ»¤½É 1 -o+VÈ"q+~Œ¼45¼T/ê*+`®S႘6®CÂàD N$Á" ‹ 0"»¤ Õ$º8ïxÉj¬.ªË IÊ(¯seãÐjp+§s³­ÊktXñó2dÅ«XÐè Ä$g£ì±…C™;×tÑ™Ø^”¥M ßÞhFEeP‰—ò<±4JREàþÇ~uHo{ ÔÕ¤¥ïm©E¹HúcÔ¹ÔÒ…>Q³¬8æh7æ|3)®d 1È`®·êHïuR)¸P€ðë.Èâ?ÅeI)@ Lm5‰ÅbA|¼ç1d§¶kO P€ (@ ø‚€ºÝäþ8wîÔ¾Ô«Ýn|íììDbâð`Ãå•ßkONË=ÿ°È¨)̇-v¬}6œ•€K˜ _¬ ­bkFþƒÒG$[_*Eœ½]}Ž—ÌlÖôÉ]Ó"" ¿œ±YÆ*ƒ[!í’T`Z˜ä¯ïR_kΞ ‚.L±’Ûå|åA ÊJÖ{¤þAº0¯y;F³öôà´ *á¡wÈ8ur«W)@ ø¬Àx߸>[pŒ ÀTPCm±ÉTlyÖ™ (@ P€ð.½ ^xJ¨“ý#B'çT_ µt¡_Mâ®@äˆ$Únç“Nöë<žÀ=¥‡õ±Êà–4Pÿ¢ó—úJzoK œop‘õˆ‘õÜ9´2ìÙìÕT6N†ræ(@ß`ÄwÛ†%£(0L@ýâJ µ;l;ßP€ (@ P€ Àe„\•«W£'xæ ½uùð P€ðkIüºùXx P`* twwcúôéÒUš_ÝS©ÝYW P€ (@ P`‚X’™9A™3[ P€ðNÜî/-ÅrR€S^@ÍG¡¶¦üe@ P€ (@ P€ (@+(À ÉÄdV &J@Mp¨†Úbd¢„™/(@ P€ (@ P€ ÀT`d*¶:ëL ø‡Úò»&c)@ P€ (@ P€ ü@€A?h$‘ ‡Úâ5@ P€ (@ P€ (@+/À É•7eŽ ®¨€k¨­Ï~ö³W4_fF P€ (@ P€ (@©.À ÉT¿X PÀçzzz ×ëäóee)@ P€ (@ P€ (àOþTX–• ÀTPCm±ÉTlyÖ™ (@ P€SWààÁƒS·ò¬9(@ L˜@bb⨼$E  |GàÂ… P“¶Ï™3Çw Å’P€ (@ P€˜`±nbMð)™=(@ LQ·5EžÕ¦üCàÔ©S ÑþQb–’ (@ P€ (@ P€þ#À ‰ÿ´KJ LA?þ˜CmMÁvg•)@ P€ (@ P€ ®Žƒ$WÇ™g¡(pY ’\¢(@ P€ (@ P€ ÀE 0HrQLLD Pàê X­Ví¤z½þꟜg¤(@ P€ (àvt67À¼³eeØYµ­ÇßJªxöž8ÐŒ»O–… ÀU°££ù :¼\ô=Í8ÐÜ«ó±På9 ŸKÛ«{OÇAì©©Á¶.X;›ÑÜÑ£å}uëå¹:>ó½cïA³|ÿu]Ɇžˆ<=SúÌI|¦)X P€ÃØ‹d¸ßQ€ (@ P€SLÀÞ‰Šü°²`**ÍØ·ÏŒÊòMXcZ†õU­ÆÀ±:¬[W€?vÜDbBþ]]iß²U«PÕ6ôòZ¡KMï5³+·ÓÖ±«2×£õ"«ñéÏ<€º‚Bäüî°Ç¬ŽÕ­Çº‚—Ð7VŠOíhEÍz“|Ûœ¹«ò¬ÃšªCcí’·Ù:ªðPN!6mÙóßOàèË(ÈÙõ ^¯‘å¸äS]ö>ó½3pòýWùöüþ›ˆš¥ž·£¹ªLz(¨óJL«PQïüå¾­ %RŸ¢ÒR9Ïiʇù@§Û)­8`.…Éuüª"T îW¿ÀÏGYM=vJ•¿r)«qôÎQ™ô´Õ‹çо¢Ò·!}lÃò6å—â e膹[!~¶¶¬z` ,’ØüÔw°j}•Ö&]­{°>ßy^ñ^UT†Vù‘üXé?Vydï‰bëtöêPMäáÜ®òy¶¯þ®äÜSŠoçVÊ Ö[ÊâìNâõš:ܹfÃAÕփבôÒØy®«Ìn9ˆÒ¢UŽvJUíô >–GöT¬w^£©È/5ãã'€1FîËQû,ô´Éµ0t+÷æ±Æp²µÊµ÷]˜¥ázk“i¨¥õ/¨(q–sÄõ«#íZ“v-*Û£õ V yÓVS‚\žrÍ×tØÐûÎ_`®}î‰=–Ãóµ©ò^UR…ƒõ;a’k¾¢yxï Ë ùœ Õ]´ÏWé×gÚŽùìšÖ× ~Œû½3ئîß ãþ†ªjÃ1]UZ?x=¨}Í;‹`*’ÏŒýêvü}ÎÏŸ'g«´›É„Š]ά¥ ’¯Éù¹S{šÍò}!õ?éžçøeµw5£lðú”頻*¹F†.'õéI|ºyX8 P`ª ¨^$3fÌÀu×]7U Xo P€ (@ P` tu8)‹¢G+èQ¼w/ —ík©­ÆÉùYÈ[}{#ŠŸù£v#ÓÚjÆò‚-hjEVv6ÒæYQW¹¦’zm¾ïûmØÈAEÝ»XdZÕ¦E°¶Èð^+˵›û¶¶*GÞH€)Û„d} *7Hú7_]èëlAyA.6U·À‹VH+ÛÇ[~‰‚òjà.©S^6æ¡æâ\šJÝ:?Žv šjkÑ97Mö›0ÏÚ‚ ÕóÆy“þ@ÙÿºŠZXç¤ ;; sÚ›P.û+:n [?hAõ–bT~°«×®FÊ,‹¼_ƒ=*Ø!7 ŸÌ-Fcÿ\dçå!+!Tεù¿sQVäiy‡JÙ²MibR‹BÓ:hEsUV{õì÷á´pD'Çà éŒspgÒç"7¥ó×lBcW”vÞì ©uS5~ò‡f`ŒôönéÁÑŽîÓgÏz®÷=­WGÿ9ÏçV¯m7NýO¦V‚>‹yñ1Ú¦ÞYшÐIï†q®¹aÇË›N¹y_¨ÚZkKi‹9V4V®Ã *Š&±u¦BÔ6@Zvž\ƒ ¤6¡Z*áè·!7îKÁ&s#f¥¨kÅ4TÈ5ÛëJ0ütc8†Ø;PòP.*[0/ÃSF‚æ^°|¬6Õá¦è¹ŽøKo(Ü“€Òƒ&DFÚÊ|(zôõ„N”.Sù¿‹4S6²RfIÒMx¤´aÔœ)ÓÂoBr¼Q+óœyÑŸ&Á˜Îýhyí½áõ~ec•ÃÛµiï–?uå(,®„ÅÙݰ<#>k@¯¥9â$XZ÷£ÉÒ‹Ú—ßt(ìï¡V>»ˆ¹y0þäé{Çëõ%gõúùV*"§÷£½ÖŒC®È®´WUe¬³o’rœÆòÙ>Ò­¾¼8ëå“f±Àür³ÃÜz»ëÚai܃#Î|ÿ^g†¥73ôîyŽSV{~´¼ÕÎë3;c&Ìå娓bîŸËaUòÑ7nËG†Å¢¦¶€ ’DFFNmÖž (@ P€˜²§Oª^"1¸ùÆá72=Ä›6£tE‚¶{ÚSþ&ŽÙ¾Öí²-^†î)Õ†îAÖ7‘Tºjÿû³ïÅ ¯ûï@Ÿº_jü*Z‘‰(Y]²à&<û‡Øí6ì++—¡ÒPY™/ƒÉ’•† Ór˜U‡·ejéͮE»‘,uz¦òWXd´£&ÿAÇß~ç9Ì€³Fz´é÷ßÒ'žG„Ü4v-†”"T.ÑÞ¦ßw;V-“aÈvþßxúfì»è†äµøÝú¥Z9²¾‘ŒõÊeÛ÷HÙÒ¿Ê7f r[®£ì‰³ðÚò xï#êÈpý’kRF²Òã€ôtÄ”áí™rÑÖŒíævĈ÷6§wÖ’[‘ºrþýoX¿D)¹›G¿ã"®7bUj.’eþ‰q: .ì”àS 6þ² #T÷¡wß2¼"kº¨±Ò¿å:‘Û«c ª ¹­}Ùm÷óÏ{®¿Û™Ôjô’Ø8ÒûØú«BÄJp&ßÛ5·‹Fü©ßk=)—z6¶¬ÏÒÚ*ý¾›ðÚ²u’ûYtìûwH‡*d•<ìD…’Ž/ß2Ë×IPE-Ö·ñ‚DÔµ°ÍíZÈ—k¡E5âˆe,ÇÎ=ëQ'é2ž©Dî"G€âëwïÄC…•x¾¶ Å™±C¹è¢±¢ði„z5©£p…\ŽÔzy¨ë©rôõÔùvj%UÞöÿkYÝŠÎÂâè"äV¼€Ã9‹ŸK碭ÀÓR„r÷"g£ò¡¹c‹U¹6èåÚÌrž#%¯ …énurn×Å&#Ø÷êÿCnb$þ»é Çž–×ñž=ÑゥµÅêE·ÈçÀqíyúÞ9êõ»aé×XŸ?£vñ;K|áëRòê-xõ ¥}l¯¡Qö®~àvy~GK§t:÷xsÞ†Ô #êª_•ÏØD;$aWµ´ãÍ£V$&ôJ ´ƬÇ÷ìq‰¶íeµþ£VÓæç±"Áq}ƅ壠R‚I~¶0Hâg ÆâR€“_àüùóèééÁ-·Èÿx¹P€ (@ P€˜‚ö3ÎJŸ«òvôȯ¢í21ƒ+fðÅ;ç&¼1¾ýçÎi7Ê »Ýˆ ÄrÓµøÇ»Ý– ˆçýÀK/s5V¦VKïd¤¦Ü§6.Dd õÚMè#x.úe=44-ª›‚¡[ûåùÈð΀¤1¤åH€DÝŽ@·—ãg§Hż[rÄc<Ò/ÂýOþqFÉÕæ¨jZÚÝŽõ¬OÀÊ4Ö5Xðñ1«v4ãÁ»‡5º8d™b¤‡B+>°KýåÜÆÅ÷ ÞEX8f¹r 1`üð¼Næ}I-7 )å«2ÔÓ£ÈOŒÒêæª±qòû%›Pùï]íÈ£G>”(’{$ <úÉ!¶ÓZ0âÌYU!ôq+ðÒ®%húkJß|oÔ5i=vý4F§×N:â)`ð½·sÓv^ê?˜½ÛÊ€]½éÇYu79p¼k®G‚$Ão‚Çe•â÷w5cŸ¹Mo½ŽÆ&Çík•ko÷òœ„Å_:&2)UÂ~ÕKWæqÜìÎH½C%w,r-<œa@á>׆¯#Ü»?TçHÁו:"ñëÈ0Tb÷é«·Ž›üg†zðx»žùËå\ò¨Œé—ÏI‹ã&úkG{ Ý\:ÃSBDšçÐöÑkÃËaíxK»î=^›aê—‚#@âÈ;J  $¼žüùx£Ö‚dé}öAEþ.=¬¦jÐŽ¿W‚–ÎÈ <}ïxûlk_oŸ¿ Œú² 2‰}mò¥ãÐÞI‘†/«r8¿ Ô!ã9?|Ÿ|î«+pHêrK³„Y’¤×*±ëµcȾ¹K d­^¬îC9/*OmñRÖÚI’d,ºuèú¼uQ À ‰/ (pÙ'Ož„^¯G` ãØ—È)@ P€ (@¿¸)NýB½uïBÂÂ?½·Æc¦X2J°ë>G5Ïœºa늫'0^ÙŸ˜½ /¦ÄžºWQ/Ã[Uni”‡ôvøýO¡ÍÓ-£Ý;_Ý7Ö–Ø l˜çaÂl‰e†Nêíø°èEØ»ëv4ì©G]Ã^Ԛ˵GZ‘'îÕb2ÔÓð †Ô$Á˜æüSr`ˆÄ‘P;áIØ´›újÓðê²¶F£°j7¾Ñ°ilľºj4É£8)ÿ¾Ü‘ÄrB‡{Ýì¬v,|A'wΙÁЋg¿_aaØP:µfm­Â²5åÚÆ¤” d­½»7Uh”á)=¿ëëúXv:¢ò|îqÚÎKýw§µä¹^öŒh3•²y§ó—÷2TZÚ¬M¾›¶˜µLÉÃÛÔ‘s ÂeEI\Ëȶ cBWâ¯Ašðô¡€š¶?—…3DZ¯'W•£æÏÇõ®sÇ.~RÁ¸ë¦kÿå¼:OïñÚ<$W¸á&mˆ7OÙÏ¿o™öâæùØ/‰rRRp\‚W¯¿õ6‚e82P5Êv×ÈWž¾w¼}¶]?£ ”G’$fZ‡6kþKz‰Ådßߊƒ_žœuwI¸­û›Þ†õ@;âï/”>%­¨¬jFsŒšw%wż ÅCÛ«ÏšAn·¯ìpE¸‡Žö‡5·*øCqYF P€“_@ µ5sæÌÉ_QÖ (@ P€ €ýͷ˯å+QûÓJ«r‰_ŠÇr3áºÁÖ±§ »zBÏÅÚãáø$Ÿÿsô6ç®À¢ÌrWV&k—yÞh—Þ÷ia€×_?Šì8Çcjâð:5›¶t»™á¸|ìcpŽ[%çëAcôP0šp³Ü}}¨£Öl{ðìö£2ÜQ® ¿”ŽÜB™÷B¢ÌŒ÷ò×ç®ÊCîRu»X-=¨*ùµm\ Ž­j>²¢ç=øõaáGºà ),GÕRIغ»2ú–,VX¶Uà•à¡ÛÊj«+}€ÎqƒÝõ^íkmT¿ö—>1rî§=Û{ÛÉlì(òPÿ#Öt$zˆ?iwÆósx»æT9‡–¼µKzU¤=ƒ½ù‹›{°m‹ªgÃÔ½:¼Öö8¢Ô lYì2ìS£¼ª6ú›oÑÚãH‹{[[ðr¥EÚZ¥ö¼¸ÜCÔíöj¼Úñ2UïµXþŠ]’ŬöŽÎgoûÜê´:Èœ*+ó±ÈÕáÀÒ€’_w`fĈkÆí¸‹^u–C?wÁÅ_›2×ÇÞ#W`%Š×KŠ4ÄÉ0èái1¨Øòcm¨-ÓŠùŽc³‡Ï¶ó2ãÏ›¢¿ô ¶¬ÃóÛ+å0 hqì¨Äã:Kð/-Å€ Ruý¬^-ß«wʈ[åPÕ5¤mö=;êclÔÆ”ë³C®OçµÓÑ >ƒþ·|ÆÿŠÌS€˜ÜŸ|ò ƒ$“»‰Y; P€ (@ P`<2(wu’D8j±Ò´5ZÑ!“¹×ï,ÁÊM2ƒ‚̲\ 4üÎÃr=+¿ù_,c죷¹2OÄÖV4˜K[Þ¤Íq_ôLïûçE!ø]™ð¼àIT54Ëù[q`¿#¼ŽÅKÞ-åXYbFkGJ`#gS5¤3ƒ§[¿®_ Ëím¯ÇŸýðm™ÜzÖï¬G›Êû¯¯à5­vŽ€úýv{eJk £³M‚?Ô†ËIÎ\}äx8^Š&ûÕñ­­±³è¨JRÖ¢q{BJp¢I&áÎy¢[ÛÐÖü*e>™Y7Ì\€ É»q“ e5ÑÙÙ*ç~åuu°Ns-A7Ë‹ŸÔ㜣ñ_þ´ÁѨ yå X7cçzÌÒSÇ"½Y:m’ëˆô7~^ ì2ÿšÅ¨Á¼Ԅ岜 ñvnïmâ­þcÝá>«~9oÁ_þ,mÕâýšr!´Rª'qPA—†:4´v í` òÚU‹öÖ£2çÉ·´ÿå¹ÏbO³j‹=X—ãèm£ŽFÄíZ{´›åZ¨’kA®Qóú'´kAEÒÆüxŒpŒNXs,ÏÉÓ®óæRÓ&)ƒ§©]c/–½fÔÔD×`Ϥ±ÓE/ý–äß‹ ¡^êÐÑ\õ¹P×xÜóeì¬ÆÜ:XŽK¸6ÇÌI6: ª )IZo›ï’@‚Ö"2¬T¬3B6&¬#Sí{ç2¾<IÛ.í¬>ÓMµòÝ“…»µ!û†q1祦ÉAêêJÂm³åeöm²æØ²,å6Y»´%v©£gMyγòÝŒ†ªR¬ñá¶T­=}g_šSS€ À8}ú4Îɸ¹j¸-. (@ P€ ¦²@lf1¶êË𔶬S¿}v,Æd6<±B»yl3xê…‰Ðù1ý’µØÜ”›±nc#C|žûQ–voZ?Îþ囟Aç3P¾¡Àuz™§`#¾§—AfÖ¢$Û†B™³`M]…¶ß˜œâGÓŽ\™3sÚà&ã"ÏÇG>‹ÕLjòÊb™GÄqˆ1) Ï,W=GšCPÉ\% òëòZgŽI¦(\¥½Ë,Þ¾gebw·ãU¹ Ó£eÿP¨Æy¨¼8z§h½¢3±u­EÜÍ(tšA†‚ÊÛ*¿Ô!ªx;ºŸ}F†ÿ)”þŽ%-o3¾éìé0”g¼ù© KF²å2?̆7²±ë—O ¹¶Õ›œùƤÈþC¨n4ãÅCß’9QF¤¯ü&ÍNBAE% 4$£ WÚZ´½÷s{k»@]¢—úÕε¦¿)I3ªËåçøsî@î8×”ë8Ç«_{Ô„]ë*±aãOKC|C-Ë×£ii~¹5O®Ù‚MMÚ!ƤÄÿ£Î9 ™™ª=Š `.w^ ÒVñF Z¤?“„`F/ú‘ŽYøyÙZ<ýä&·ë\µ÷S£&™wd¦Ç¢•2I{±|.‹âÆ»¥Ѩeèz‚>Ï9ëPì¬ IÒ[ç1/=TеȺó‰n¦ãúvìYŽJ‡…‡k³mÿP>®œG¿B $Ô™±(9VÛ1÷‹ä1ãDZ ´yçekÀxß;^>Ûã}þF—ImÑáËY(o©FJf²‹CK:(uÎ ’%(bFSR2fkQÙH–(ISS2î¹uè>Ô`žcÆ­m#±cÇ3øQÑùŽV×§))2¯‹sül¹î‚,~Vf— À¤8~ü8úúú8iû¤maVŒ (@ P€SC@Ýnr¨ƒ?^ûQ˜Ýn|íììDb¢ç ‚Cˆž®>™KÃŽ@C"õºKG´Û`í“î2D“^7Æo†ÇÙo³Y¥Ì2”’š?räÙåX5Ïǹsòƒ·Q{G¦ýÞÛñª\ç«Ü¶f˜,@jÙn¬ˆ•~=}‹€k·û ìRî>ÛY©öØûÝÓŽ^·ÃjU“­H½F›_JÞ^ýFœX]v™½[§SÊÐ3€ˆ1Ü]Ç©ôê!œ± ¼žÛ›=¼×ßuz¯ªí¼]sîJùÕõ­~Ï®µ³ÝŠžD º+‡>‰eÉ÷ƒÛÜ3a¸l6)±ä Ó¾F‡'õøÎf•ë\öŽy{<êbw¸<%æ1Á? ½”kóbKYé¼^_—•ãEtõœmðÂËÇ‘²<Ñ΋¥¾¦â×ðÌï«<Ù.¢ × Ée|s_ƒRò” ¦ˆ€šÄh”.ã\(@ P€ (@ PÀ) CDäè›ô—Ä#7Ðõ^òg¿N7ô+ëQçõps~T:O¼¯Ê5ò-Kß)5U„ÔË5у¶yØS ”Û[µ‡%õÆ{ÐçRòöê7â¼PÇ"eoÞ gz×#²“ Ãå¶÷ú<Ϩ÷ã\SÃÒKtƒu–=ÒnÃ*¤<·³ÊëÓG\eÑMhðâSzº y¯—rm^Dv—ŸÄÛgûòsçÈ«èxfs9̵¯cõ÷Ò ?шMÒ‹$Æ„Û"Ç)¦íöqó±²±8 ¦”€úUÕ©S§0þü)UoV– (@ P€ .A `îˆOÆsFÌr Y0)(@O+h\ŠË‚°ãy3^(Vs锑‡¼GÒ%|ë_ ‡Ûò¯öbi)@I,ÐÓÓƒwß}_øÂ&q-Y5 P€ (@ P`*¸µ¥Ö?Ýp[SAŒu¤(@k%ð™kubž— † |òÉ'˜1cÆð|G P€ (@ P€ (@ L˜ƒ$FËŒ)@ \šƒ$—æÅÔ (@ P€ (@ P€ø´ ’|ZAO Pà œ9s 㘲W€“YP€ (@ P€ (@ Pà¢$¹(&&¢(0±j>’ˆˆ\wÝu{"æN P€ (@ P€ (@ 0H2HÁ P€×NÀ$¹v%à™)@ P€ (@ P€ (0õ$™zmÎS€>(ÀùH|°QX$ P€ (@ P€ (@I/À ɤobVðuþþ~m˜-NçëEeù(@ P€ (@ P€ (0©$™TÍÉÊP€þ(À¡¶ü±ÕXf P€ (@ P`b¬¨YoÂúª¶‰= s§(@)/À É”¿@ \kI®u ðü (@ P€ €ï ÀzÔ‚æn»ï%¢(@I%8©jÃÊP€ðC“'OâóŸÿ¼–œE¦(@ P€ (0¶V”äý¯Y€ÞÚŸÀôÊ\üà×O õGOËs¡ÿk6U›w•â¦c{ð¯Û_@c‹$†1I_ÅãOæ".B•ËŠ†¿Äö]u°ôƘ$d=Z€ô„HG¡­mØù³2T6¶hÇ&e¬Â“¹K¡íi«Ç/6í@c»äk0"iQ KG$ï¤9ìøL P` °'É$jLV…ð?¾¾>kÿ+=KL P€ (@ P`"t¸)z.ô*ëÞP,¸'3B€¾Î”äJ€¤ÆøXLë9€ü5›ÐØ…ì¼éš”¢üI@ÍGrà 7øS‘YV P€ (@ P€WA`ê,gΞk@"†´-@¢6êãVà¥]KÐô×*”¾ù:Þ¨k’ ‰ £HH¥ã-´ËkVÚÝòì\"¡rï^íMóδWsÉ£¾&è E‹v xí}`¨+_ƒÔr’R¾ŠÔÔG‘Ÿ¥íç(@ L.I&W{²6 € \¸p§NÂ-·ÜâG¥fQ)@ P€ (@ \;½!hðäÖÖ*,[S®½OJÉ@ÖÚÛ±{S…Ö ÎØJðPr-ÝfƒpmŽš?×»rŒ] ³šã®Ù·"¡j7¾Ñ°ilľºj4É£8)»ŠÓÀ¹Žá+(@ ø½ƒ$~ß„¬(à¯V«U›‹$(ÈõÏs­ ËM P€ (@ P`‚FF9ÜNs´QfoG¶î.FœNí°Â²­¯È1úë£$à¼Ù| +âÔp]²Øb僅Í.CaØLÙpi+ó±HªKÞZPòëè{êP´ù(r6æ"wQ:r íh(]‰ µf±¦#Q›,EeÈ… &ƒ'nŸ ­È:P€~)ÀùHü²ÙXh P€ (@ Pà* XöšQS]j®uY¬Ž·ç&ü啃èèhÆÎõ&˜{%Ö!½?:#îÀ2w«¥â)TìiF§ì¯xöÇÚp\Y‹£½ô[2,W/6½€‹Vf ²¸[ŠâÆ»­e9gæ´Á¬¾õ’k P½©ªO bR‘|Õf¼xè[Èn+>~ò)˜7À¬e@VQ–Ȥï@"žÛš‡'×lAqA“¶†$m s¢õغւ§6™Q¸Æq$ ñÈÛú8¢äÐVGj>S€ À$¸NÆÄ¿0IêÂjP€ð+àŽ;îІÜò«‚³° (@ P€ ÆP·›ÜçÎÃùóç¡^ívûàkgg'ÇÉÍûn•Ÿ}À^¹e‡µg!z™yıØd¨cÛٳЅE@çÚ8˜¥¤·jSÄC¯9Ž–k_€ìÓÆó<Š+ (0yFý¯aòT5¡(à»ýýý`€Äw›ˆ%£(@ P€ üD 00êáX¡p­;¶è$øá9Ä!éõÃÓUÛÛ¾¡T\£(@ÿàœ$þÝ~,=(à§§NBxx¸Ÿ–žÅ¦(@ P€ (@ P€ Àä`dr´#kA ø™€šdúôé~Vj— (@ P€ (@ P€“K€A’ÉÕž¬ (à'ª' ƒ$~ÒX,&(@ P€ (@ P€ À¤`dÒ6-+F øªÀ™3g´‰ CCC}µˆ,(@ P€ (@ P€ ¦„ƒ$S¢™YI PÀ—؋ėZƒe¡(@ P€ (@ P€˜Ê ’LåÖgÝ)@k"À É5açI)@ P€ (@ P€ (0J€A’Q$Ü@ P`bz{{a0&ö$Ì (@ P€ (@ P€W€A’q‰˜€ À•8þ<¬V+ƒ$WŽ”9Q€ (@ P€ (@ Pà²$¹l:H PàÒT€D§Ó! àÒæ (@ P€ (@ P€ À`äŠr23 P€ÞÔP[Ó§O÷žˆ{)@ P€ (@ P€ (@«"À ÉUaæI(@ 8Ԥ휄W(@ P€ (@ P€ |C€Aßh–‚˜"}}} ’L‘¶f5)@ P€ (@ P€ |_€Aßo#–˜$v»gΜAhhè$©«A P€ (@ P€ (@ÿ`Ŀۥ§üH@ÍGæG%fQ)@ P€ (@ P€ (0¹$™ÜíËÚQ€>$ ‚$œÄ‡„E¡(@ P€ (@ P€˜ò ’LùK€ ÀÕà|$WKšç¡(@ P€ (@ P€ ÀÅ 0HrqNLE PàS ¨ ‡ÛúԌ̀ (@ P€ (@ P€WL€A’+FÉŒ(@ xP¶Ÿ;w:Îs"î¡(@ P€ (@ P€ ®ª@àU=OF P`Š °ÉmxV› (@ P€˜õC´îîníqúôi¯ç˜6mfΜ©=‚ƒƒ½¦åNÿ`ûûG;±”ðIü¥¥XN PÀ¯8‰_7 O P€ (@ øÀÀÀZ[[ñ¹Ï}óæÍC@@€×Ò©^ý* røðaÄÅÅ!$$dTúë~³hÔ¶‰Øpá» ‘í”Ês"Úe ®Žaî¡«sž…¸$I.‰‹‰)@ \ž€ ’Ìš5ëòæQ (@ P€ (0(ðþûïk’‹ýKQ\iÕ±111ƒyùΊÍûÑøÖ?ðqfœ;ïýâŒúÁ"öt4ãH¯I Ñp¿¡g³´á­ÿ¶ 4ê$D ¥å–¡”Æ lÿM.¢U,¤ç Š*DÓÐ^$­ÞŠâÌ8m‹­­ æ–îM{¦ù‹ŒÎ÷vÔo2¡¸.%K2Ó\é•ÉÖþÇê~&íêÖn`i%/"?ÑÕÚn;®æªµOå`þˆ²´V­ÇšòF·’ÄÈ5øs,vê|áZq+W)àU€·{åáN P€Ÿ^Àn·C=Ô8¸\(@ P€ (@ PàÓ ¨á³Æbk¬3¨cÔ±¾µXQõ„#@’±v+vïÝ‹ªª½xi׬N6¢©¢%õZ‘ƒ‚åæ³!l°øÖÖ*g€$ %/V ÞœL0ÆJçžÕ$)«Kðâ®]ؾ1K5JþÔ*©m¨yNHŒX½¹»]e(_ƒ=Ò­Döï+“IòZ¼$åܘa@íöÿ„ÕyKÃ/%@˜6?‡‰¼¯?¹Ú Á4ìØý’˜ïÂ.·Çc él7O/]ÍõØYVÓ²¨øÁ­§‹½£F SVcÇ‹»P¹u-â%Õ¦‚C—–¡o\+žêÆí)À ÉH¾§(p…¬V+ôú‹ëò|…OÍì(@ P€ (@ PÀ‡TOò >» ¹KãàØ*P…Ìõ¥È’NuÅèP1 ç¢uøh6cÙšrôRP¶»x0(¡ò[•iBIU›+ù°×Ó=Úû¯,MD„ü½p±ÖûäÄÇýy»¥ IŒéÈL¡“Tžx\B&@U½Ê﬒ Õ†çºý¾4ÀrFmºà‰ µ0f•`EÂ5îùà(‘= ªá³˜!cUé¤MÔý×C ÔVS‚U%U8X¿¦ÔTT4¿ª¢|T5·aOY>RSóѬ"U=mØ)½„2%Mjj&V•¡¹ËqáØ-õÈ_UŠƒmPºJö¯Ú‰îq®•ÞcoãÍ6æ&Dzl«3Ë6<Qzã–"7[ÒõîÇ»ZÔŒ×Ê(4nði·åÓÍÃÂQ€“A@ µÅ ÉdhIÖ (@ P€ À•èØ_/‘µ4vŒŒ#‘œsåè–Ø†êoùýÝ{ É-¨Ã2°cG.¢Üîî“9AÚ{-8a95F~Àì;î”íMøÕÏÌýŸw «î;0Aø@IDATy¨“²îVׇŠ,¾ýfyv.ú¹X,ýrF&ÈP‹ºŸ½Vt¼þмIE€ô%Ù™¿£ »²Cƒ9óùâ”çÇ8ÞÕƒŒB1´" {÷‡h¯«C¡ôÒ!³ :|ØÙ‚Ê‚\-©1>ÓÐ’‡r¡’$e˜'½:*««Q°ü=-ˆvÓGhi¯Ean­ÊDÒDႽÝ뵞ÒtInoŪÖhçr=E§ÿEó€ù®¨žô2úÇ[íC¦‡8SñZqqñÕܾFý ´,"(@?P=I¦OŸî‡%g‘)@ P€ (@ P`BBÔ òv8C£NuóÉ29I%µâNDJÏJ 8“YÞÆ;ÒS Ê8t{O÷0^ü}:ìÓÆîÍ¡‹½É(Gcc §“ˆÁ]ó%o ’¨Òôžu/MÔ(_ŽEÅ+SPQ\ˆ¢SÉhª³ ¹h:ÌO£Òƒ¢­·ãßò3QÝ"ªÄ¤aëÏó7x¬+¾ŽõÞZä.WŒáËÆ]{îÜ”’W†ÂtL³Â¬ 1x¦òWX$íß¹g½ É9brsÄ|ýîx¨°Ï×¶áiÇ”2ªË~_š¥®$Yîõz­hIÔ“ý¬<|/+:c–¸¦¢‘ICY¶H/¤øìG«]޼Vܽ¸îûCߢ¾_V– €_ ¨ É7Þè—eg¡)@ P€ (@ P`âÎöÊÏíåfwxÐØçøè¨š ˆè›$Úð–3Üè.ËÑãÉ5[Pœ»·VåjCb9öê9x÷zD¦v4”æj=G’MEÈþÚ-øðo/¢°¼ÿÛŒÝ[ⴛᆠá…qu P™—¬EYh<^yÂ;7nÇ—ÃßÄòâdlÜ﬑É"lÞþeÔæâ©í÷ *áˆ2ðíhu $acY>‹¡ž$vÔìfÁ?¦µJ þE 8ŽP½4Òr´‰ÚÒýáòœ‚¯;$j[Dâבa¨ÄþîÓê­¶d­LwHÔ[o׊#ýxÏ6ËAüâ‰BH¼ 1Ï 8Kq ¯—_ýA€Ah%–‘ð[ . ¿¿¡¡ÚÏ<ü¶,8(@ P€ (@ \y›æÍ—LkñÚ‘$Œ1IwGK³ìŸ…ðiêÜêfz vHO€(Y{.»¹Õx¢ìTæ^L0boH/øÕ(\±D›ÿ$*3%–7PX}Ö8­'IW—¤Qãz©Å~oÉ ð¡%± Óå¡¶´¡(UMä^„Ü…‘ØùÓ^Y]Ž„h#¦™bP·«=$»OËPŽ\“v5&ãöØèÁ9i†›¨ˆÈM2HÖðEï6“zvmLq|†õä1â‹7Û0<ÃK|g9PÓ:57I ò6?‹t™ÇføÂke¸ßù²'n÷åÖaÙ(@¿8}ú4BBBà÷ua(@ P€ (@ PàÊ DÞ™5-¶ù¿C爬mmU(®“€Er&nÕîm;n–Ïp¦‹Íz‹€¥z*öŒ8z¬·CÃh¹ÿjú¬6—»L.2 3å°ºWli—L€¹³GÞ¢·¡¦(Wö%cûÓK$Å9¨)ÜÃÃ%µ¸w?qlá³7m²so ¼ï QhUãÕÛPBË_±K\¡ÁC=ƒFtJ{‰kj"x 1$¯Æ‹{· qÏ׊»×}S€Aßl–Š˜$ìE2I’Õ (@ P€ (0º¬Y›¢"X™¹5ZÑÑц†ª2<˜+½4Ô¼ßK‘—ÆZ"°âǵ¡¶Ì…Ï¢Yn´[[w"55EUmc[î‘_û·”c]Å´Éyêw®ÇºZ¹“žœŠØ°X¤¥I0¤±¥UÐÖÚ€’¢M’÷ß=,¿Ž=%Ú«Ë ­.3eî÷ÚêeÆŒ.쯗aÂæÍFذ£øfl5'É›øSÍÔÔÔ >ªªjÐlq zŒ8Ø=®›þ°\)@yNªšÑ| ù¦Mè•¶{8Í5!Éð ¼_+ÃÓŽ|wø?Uà«÷Ì‘†Ô××;ÚÜ sÅkÅáÀgßû;Ö·ËÌÒQ€ð5‰^庳úMÅYP P€ (@ P€$ zêŸ;wî’{ì_Î1T…aÙF/-ÄöЛP²¡[Ö5î3ħá©'Cb¤ëöÜLùçeäBü¬( ¦b™Wäg{°ûŸa‰n·y(3”•…¹¥Èø1¶˜7¡ÉqŸÆ$Š —h˜EmEÖ±50—¯“AÀÔb€iãϰÐmÌ,«ôpÉÙÔ(“toEf¬³çˆ ô”¶f-ªs6a™‚KúÇ<ó¬§àŽ–ñe?M¶ö—¾bÑ‚Š--£LŒ–›ñƒ°1Ú]RΙ9m(½>?/[‹§ŸÜ„ò ÎíñÈÛúI'ë µIgh ô~­ ¥y¤ ÿ¯UÍ•#}W6mþ+n‹! wTűv­¯·’q•^®“ñò/xMÁ (pÙ­­­ˆŒŒÄ¬Y³.;H P€ (@ PÀßÔí&÷‡ Pœ?^ nØíöÁ×ÎÎN$&&^RõÚÛÛa0.ùï¬'N ··11ê7÷×ë~³hø† zwá» ^r¶£§«6™»[gC„Þ€ðrÈåî’6°ôaÐë\A˜¡Ìlòƒ?›ŒÎ¥‹Ð˜çBn¸[:p¼7³c£FíƒÝ&ùžCˆüXpt®Cùšµ‰h”-ø4Eºøcs]|ÚËL©Úîœ;‘mp±E»Ö×ÊÅ–“é(0QßW”¥(@P=IæÌ‘>Ç\(@ P€ (@ PàŠ|îsŸƒúAšZfΜ9n éîîÆû￸¸±‡º"ûÔ™"B~dwU–À@èݺ‡Œ8©N‚žB4zc4bGÎÑí:>P'ùºÞLÌëämÿ+ã¥ÚÎW–k}­øŠËáûìIâûmÄR€~* ~%µÿ~Ü{クîºëü´,6(@ P€ (@KpïE¢Ö¯dOUš3gÎhü8}ú´×N›&’K0E=‚ƒƒ½¦åNÿ`ûûG;±”ð Žíú ËI PàÊ ¨¨ët:H®<-s¤(@ P€ ¦¸€ vFí1Å)¦dõÙþS²ÙYi L˜Àg&,gfL P`Š ô÷÷#4tøÄhSœ„Õ§(@ P€ (@ P€ €O 0HâSÍÁÂP€“I@õ$Qݺ¹P€ (@ P€ (@ P€¾)À ‰o¶ KE Lö$™È*P€ (@ P€ (@ LjI&uó²r Àµ`äZêóÜ (@ P€ (@ P€_€A’ñ˜‚ Àe Øl6·uYr<ˆ (@ P€ (@ P€WG€A’«ã̳P€SL```ÚcŠUÕ¥(@ P€ (@ P€ €ß0Hâ7MÅ‚R€þ$ÀIÛý©µXV P€ (@ P€ (@©*À ÉTmyÖ›˜PÎG2¡¼Ìœ (@ P€ (@ P€WD€A’+ÂÈL(@ Pó‘ètºáùŽ (@ P€ (@ P€ð)I|ª9X P`²p¸­ÉÒ’¬(@ P€ (@ P€ Àd`d2·.ëF \3I®=OL P€ (@ P€ (@‹`䢩˜ ÀÅ \¸pnë⸘Š (@ P€ (@ P€×L€A’kFÏS€“U@H‚‚‚ð™Ïð+v²¶1ëE P€ (@ P€ (09xor´#kA ø'm÷¡Æ`Q(@ P€ (@ P€ (àE€A/8ÜE Pàr$¹5C P€ (@ P€ (@«/À ÉÕ7ç)@I.À É$o`V (@ P€ (@ P`ÒNšš°" |D@IfΜé#¥a1(@ P€ (@ L>3gΠ»»[{œ>}Úk§M›¦ý¦þN öš–;ýC€íïíÄRRÀ_$ñ—–b9)@¿`O¿i*” (@ P€ðC´¶¶âsŸûæÍ›‡€€¯µ8wîœL9|ø0âââ2*}Ëu³Fm›ˆ ñNLD¶S*ωhÿ™?¸*†%Ý?¹*çáI(@K`äÒ¼˜š À¸êlcý£{Ü™€ (@ P€ Æxÿý÷µÉ¬YØPAWZulLL̸ç¸j ì=hn:‚³AA#NyAÓ?„ØÈÛ/å­ÍMè5ÌGBtÄ¥(iíèééû˜ 0Dè·íVttƒ=pÆÿgï~àª,ïÿ¿ù"IŠŠ†˜jXBe™µ$¿.Ò¦–ñ­…¿YÌÊ–®é¾éw˯³mÚ7›³––c³E¿õ#›Z9ÊLËŒe…Y¤’‘8•Å£8àïºÏ= ȟ׵ÇáÜ羯¿ÏûȣݮëRDD˜ª>hÌÞ¸Z;Júë‡#úžs­úÊë>Ûvî¿C¹™_)·Dªz÷ æ|+ªdÏ—²w¿ZQauã4JŽóùÞTß‚¼]:tÔ!ßà‹Õ;<ôœïB~n¶JÝ"ú*´Ê0m¹›µ&=_7þh”š úap¶ TýÝÕ†‡ÊÐ@¦(++“Ãá HÒôÔ´€ € €íTàøñãºì²Ëê=zk¹-ë{‹JE;õÌ“O*¯ÚNÓòµSTßðÆÙªŠ´nú“J7_k§ÄȾk¦MÿD?O™£¨ ³¹ª=²}¥ fV{)ØÔ·ÂÔ'G®Þ3Iiæá¶3 ›¦·çŒVÅ3î‚ÍÉš27Uq³–itµ55ìdÛ¹ÿEZ?g¦R*üªã÷?JXù›3÷°º,õ>gîÛâGŸRØŒ?h|_ë‹`Óê9+#z¶æŒïk>WþÞÔ»þJlZ3ÿa-Xçö LPÊKI +Ï·9ù!ÍLÍ)ÿ4HÏ®Z¨èŠï§=[¿4SƒѨ„JóF HÒ¨œT†í]ÀšE·íý[Àø@@@ )¬å³êZb«ºö­2VÙ•|üÔÑt(lÜSúóä«UZäÞ?U<+nhŸýÍ“è0×<_ÇåîTɹ«[}À•Züì³r”Ïpñ ôÕŽ¿MÑ¢téþ[û8ólËH"õìÛ/iÀÞTÝ>å¯ú²`´†ZQÛf=13UÁñ³5sDø¹õŸÇ™¶sÿƒ4ñõ·u—uÏ}T¸íe%ÎLÓì”],9¿ >ýãs;Yß’ÆJ…ú$'G·8*–©ó‘mgž2ÃgpÿÞœ9Ù€ƒüMK’ØÄ§ôÄ=Wiÿ†¥šº UO§Ó„(Éž¥¥&@7û5Ín×ü‘“ôÂêl½”`kìZý›)ÊÐ0-}füyÿ[h@÷)ÒŽ¼ÛÑX* Ðä,µÕäÄ4€ € €´9Ž:+Ð7PAAAn/לŒü¬5šóx¢FŽi^ãõЬÅÊ*°ìZ¿ð!ÍY‘}ÖÃüåýüñ‰Z‘m;{ÎíZ³P÷LI1GyšsÏCZ‘e“={µ2yç»—¯(夾ÑÑÎ=\¬}\úúå8$Ö¬ñQ®¹-G÷ì5¹«é¦oD´"U¨/vZíÚ”úë™2¾õÜãÃ+jä½:Š{n‚PÁýMŽ uR`Å÷Àœ·R^ÖJžÿë;`îÙâÕYÎó޼õzü¡…Úœ½I 2߇^1úR~æjÍz¨ü;cÝãW6:Ï;ïùíS3—RuŸzò=õÐO•j&z¦ý^‰‰sd¾U’]›R*q¼õý©ÄÇM{y•|¥ÎyHÍYí¬¿rA»>JM“‚Ç鉉C¤¨QiÚ iëêÏËó—褙S­µ¬œY²ÍõžrV“»f¾eH,ž©þÌ¿2-Ÿ]€ I£“R!´g+HèúÙöìÀØ@@@<8yx¿ò ò•——WþÊ7!“ò7éñ© ”ž®¤iÓ”4®¿r2Vê÷odš‹¥Êݘ£Ì#®‡Ê®ÖNi[ažŽœ2Z¸'¿®ê?ȵKa·óÀÚ̪qä™™%yú,ï¸{ÎjŽ ôʬf:C‚v›ÚÝä=¨£V‰ü=&(¬ëi׊ß*y«4ëµ™jÜ9$Õt­œ °&‘˜{žº-BÌxDqÝò´rÑT­1Š¢ƒ¹Ú𓦙SžTZN°b‡›É©ºwú"eätTBR’âûÛ´.e®篗£CgE d—EëÚ!}Ô;¢k–FaG ¼!ZW™i´9yšžLNSÇë””/ÛVÓ^â“ʶ¾ ¶ÝJMÏQNfžª|ãÌÅRÈ7o×]©NæÍ•|Õçó=ÌËÑA+Îâœ.c¾ƒ»¬ÊJtØk‚‚Mï ÖkÒ‚tÅ>²Ô, Æ3–r<ÞšP€8\âR5´?–Ûj÷œ#€ € €ç#`=ÏI›§{ÍÝ»§§V­ÕU‡¾5õ©§žŸçZÆJ·ªðý±ÚP‘ѬÇåÉ’\#&ê©Þ2KbIϽ0SQÎ'‚÷kùk£Íƒs×ÌŠ*«¾ço\¦óð:ñÙ»+ízåÍfÀLMz|¾m]'E>¢Kö¯Qâ’ ÅÍxVÓæhdŠYŸËäJšÿŒbÌ4R½Ьaã”’2ŵGL7}vï\í=xâìŽïƒ’ôÚÂ…šÐÚŠÇǘn{{$ܥ؅“47íOÚ’´B3ç„é¡‘S4lòLMŒ²·©Ã¶ÛµzäÏ5s¢YË}Nˆ=Ó¹Vdâ³zib´¹&%Œ ‘“èÿþ+WsF Ö__K‘Ã7¤ÒwÙ±üGXè¹µKûuÄ ,"¨Æ ’–L¦üØ“ZgFøTœŸ’˜g†¤‰Wl1}lpRlÂlýOÒð3{Þ¸·Á1ç+@ä|)¸ XA’NÎþ„Û%@@@8Gà¤97M³šÍÐ]Ë9ä«KÍ_ôEMÔ;«F(ããZ¸ås}±.ùT’kNÈ9UÕz¢ÈYõI•XOÝOZWà¢@«–ZK&%èÎè*Á”½¸l¾V¿û© ‡ÎÖ¯F…+yÂdiØ ýäÒ,%š™Ió«Û§s5o拺î9,›Tëªá¢ù‚„Ýr«+@be1K³u«’5aÒh ±’köFð¸ûÏn~nnöµ£GKiÉÚ±»@Ã;2K\IÅ%Öì +HRdþç‘ÉíWÏæË›Í;¨†_ê ¦¨¯6›ë/©£2Wá¼Ï³dKŠ>3ãéä «÷7貊.†iÄø‰aÎÚ2“5Öl2nþ‹ŠÖ§šn{ê¾áê2D‰Á)Úôõ^$±6u'!иüjj\OjCv.P\\,k³5 € € €ž œ,®ü¼¢ÔÎô•æ0VϽ=OΕ‘ÌRHy/%kƒÿÙ§Ý6·'ß¶o?wþåÿµTóîWeωj²œ9µ÷_:xKí¦ó6.Ô\³âVâ³ÿc¡KYf¾Ù ܵO…ÙÃÚƒÔt~g¿²ö0)|ÿSåM‰93ûdï×_: >»¿‡¿ßÙcçE·ïTEOƒú tÖÑç¡iš2ªbÖQV˜ †üêz¬¢ˆ!æÎ§}©}ŽõufÏ×Gï癥ٺªCE#ï¶ÍúÅôTÇÏÖ”˜ 9v¹BA®±ù8ו;YâšiUQ„wKÀ»±*¢@3]ÕIêñ_œ !€ € €´_R×ЭBÍ)Clج]»2õÊœD³‘Ù÷:=]¹fµ$çñÔ?jõæleo^a2§Ô\MI±¹–§ÞZ¯ì|‡lY¯häÈ‘šµ"»Æ2òr͵8 ­åxþFM™›föuŸ¯‰åKr÷èiÊíÑž»²×¼aÂ%¡êhžs“šZ H·$Ä™(ÉJMyÆùñ¼~´©ûŽ„Û ;çZÅ ×’j÷XÎY;×Ì2X#fèÙiú’T=95Õy!xP‚žþm‚+0am–>,LKÌ%s¿HÒª¡}5|’Ù~ÞJ-š·S=¯Ñ|sÕhö Yª#¿™­ÔEåßAs!~Ú³ºËÚÄV¨]&è¡nÕÿ±hPÔ}šŸ”£™É)šžî àEÆÏÐ/Íþ5î)3ùa%›ÝÙg,{Lg®FéѤXMŸ7É,ãf¶Å‰}DwGU¬Ñå^šcÎ_Àë´Iç_ 5 €Øíveffêºë®@@h×Öã&÷— °þ°Ìz·þ¸¬â=77W11®=-<ËÉÉQpp°ºu«º}uí5:tH………ŠŒaöàÀ£µÔ~—63ÃeŸ‚{õUh5ϯmæi}#÷«)îÿÌ.ÿ}žä÷žg>ßœ»l'Ìt£FòwØm:a/1Õ…¨Þ_3Ó»£Ôl+PÍwÔ¡¼]»tªC/E„û%rØ­©-ùZß}M#ÐÌ¿9›fÔŠ´ö#i w> € € €@[¸ä’K”e–²R—.]êœQbdŽ9¢ï¿ÿ^QQQ­’Ç××WÖË•|Rql±>‡”_kæ7ß EÔ²‘vSô«=ÞÿÝUß@ó½h¼À‚o`\éKÍ_…EԼ篳 û÷½AB Vf’ÔÊÃE@Àsüü|‹Ä:nÌ™$—õGjVàÃz:UÓrQ.ØÌ’V&˜b½üýkXת Þƒ¶<$î[¾»Œ æ ×üæ´ˆmT€™$môÆ2,@@hqV°#,,Ìùjq£CM.Àýorb@ ] x·«Ñ2X@  JJJø«¤&ô¥j@@@@[€ Ic‹R´[f’´Û[ÏÀ@@@@Z©A’Vzãè6´<k&‰ŸŸ_Ëë=B@@@@j’TËÂI@ þÖL‚$õw£ € € € €J€ É…’§]hsÌ$is·”!€ € € €´q‚$mü3<h>‚$ÍgMK € € € €4†A’ÆP¤h÷eee²^¾¾¾íÞ@@@@Z‹A’Ör§è'´h‡ÃÁ~$-úÑ9@@@@Î Hr® g@z °ÔV½É(€ € € € €À HrÁo@¶ `Í$a©­¶p' € € € €@{ HÒžî6cE&°f’$i2^*F@@@@ I’4 +•"€@{`O’övÇ/ € € € €@[ HÒî"c@ .Àr[üÐ@@@@ê-@¤Þd@Î Hr® g@@@@héIZú¢ Ð*’´ŠÛD'@@@@¨$@¤@† XAŸ†¦ € € € €\ß Ò*"€-H   @yyy:tè¬ã'NÈn·«¤¤ÄÙK___¨S§N Q×®]Õ£GuëÖíÌ(JKKeå#!€ € € € €@ëà‰^ë¹WôAÀ „lÛ¶Mß|ó²³³µg÷¿uúT‚ýËœ¯‹œï§ìW&ëø½Uäð’_±yYï:óìÛQ—]®>}ú8&V ¥K—.ÐKª@@@@@æð:mRs4D €À…øú믵iÓ&edd(oÏNEu)ÖU¡ÅŠîZdŽKäë]ó¯ÁKÊ.Ö¾¯j»îk&š\tÄ[ÍËz¿è°—"º\¢˜˜ :T×]w]µå8‰ € €´uëq“ûËš}_VV&ëÝZ®¸â=77×ùÿ¡ÚºãCh¹IZg p{öìÑû￯?üP¶½º¾‡]ׇÙ5 k±|ªyTÛZmA’ê ÷R·}>êö½·.u\¬[n¹E·Ýv›¢¢¢ªËÎ9@@@ M ¸H¬c‚$mò63(@ M$i·‘A €@…À§Ÿ~ªÕ«WkûÿÒ\vR·›×¥ÁŽŠËõ~¯oĽޅ^º$ÇG=ÿí£!}¯ÒwÜá ˜¸çá@@h‹IÚâ]eL €@Û HÒ6ï+£B Ý |üñÇzã7t0'Sw_qBñ—ŸT oÍËhy t>A’Š6¼Ë¤{|ti¶_®»îºKñññ—yG@@ÚœA’6wK Ðf’´Ù[ËÀhÖ~#¯¾úªvoÿ\÷õ?¡ÛMpį–=Fê«ÒA÷6»ðVÄv_Å^ÔW?þñ5|øp÷Ë#€ € €mB€ I›¸ hIÚÅmf´=›Í¦¿üå/Zÿî›JèwBãûØàsþ3GªJ5v¤¢~+XÒ7ÓWñƒnу>¨K.¹¤âï € € €@« HÒêo!@Ú€w»)E6#`mÆžd %™©zyäAÝk‚$M iJ°#=ÊôÙmÅúóÉõšøÐ$­\¹²)›£n@@@@¨FÀ·šsœBZ¬ÀóÏ?¯-¬Ð¯®)Р®Å-¶Ÿžtì´—”Û§T‡zÙuì?)33SÓ¦MSHHˆ'ÅɃ € € € €ç)ÀL’ó¤84ÀîÝ»5mêTùl{M/Þz¨ÕHÜÕŠ:œÖ·ëÕÃêÑGÕ_|á~™c@@@@h"f’4,Õ"€@ã lÚ´IÏ-ø½~õ½nìio¼ŠËk*5[™/öÖ±"où8 Ìñ1s.T¾ò ”J¤â€Ó*ñ7ï§/k&Hc%«®ï®rèxî÷Úÿ«'ô‹G§iôèÑU=õ € € € € €@5IªAá´wÞyGÿ÷Åg4ïÆÃº,ØÑ(³‚"ÙþÊÌ÷×WÖëP€:…„:7OïÞ½»BÂBÔ©S'øùé×^^*))ÑÉ“'UPP C‡iß¾}Ê=œ§c]ËTêz7Ç¥>çß½ƒá¥:Õ©LOÿù:räˆ&Nœxþ•R € € € €T+@¤ZN"€@KøÇ?þ¡·Rþ¤…7V·¥çÕ¥Ó&0òU~€>Èí ûÞg ®¹æýhà@ý²_¿zïbM¾ýö[mß¾]_}õ•26~¡ü°Rí‹(Õáže:ŸY&…!§õy\±o½â Î<üðÃç5v #€ € € € €@õ^§Mªþg@ 'ðÆoèõEÏëѨcºåê’w¤¸ÔKïíé¨åÙA ¹,Z·Þz«† ¦nݺ5¸Îê Úl6¥§§ëÃ?ÔÆmŸ)÷ŠR}Yj–çjø¯X¿b/]½ÑO?¹ù.M™2¥ºf9‡ € €´Hëq“û«´´Teef¾yw8gÞsssÓ"Ç@§@Ú‡A’öqŸ%­Jà­·ÞÒËÏ.ÔcÙ -*VÙ´0 îS¿™$Ž2/½³»£þ¾£“† ¿Ý¹¿GTTT³8ìÝ»Wÿüç?µbõ›Ú}™]»¯,U‘ÙǤ!ÉǬ06ä#MŽ»WIII ©‚2 € € €@³ ¸H¬c‚$Í~ h@ÀC‚$B‘ šG`Æ zæ7s’K O9-óõ’÷ãat¹g’ÍrZÞz‘¢‡Ö=÷Ü£Þ½{7Oç«´rìØ1½þúëzí¯kw‡ –˜¿–jÀ"‡¾f"ṀþúÅÝ9ÇS¥>"€ € €-N€ I‹»%t@ ‚$5Àpš_ ++K¿˜6M~±SW>^©¥þÞò›ÞCÌÆæ5¥}6_=÷eg9z^§I“&i Ùo¤%$kúøË/¿¬w3>ÔöëJt´[Y½»åo÷ÒµëüõÔ´_9— «w@@@šQ€ I3bÓ €Ày $9/> #€@c X{z<öØcºöýM¾÷PµÕ–ø¨Ã/{¨_O³•[²²Z½+H/}}‘þσi„ nW[Îáû￯–,Ö–Ë:g•Ô·g ¼tãGAZüÇ?©OŸ>õ-N~@@@ Ù’45 !€œ§€÷y–§8 Ð( .TÏO¿ª1@b5âST*ÛhçAŸ3mÚ^úÝg럅C´ÀZj€Äêðm·Ý¦—–¼¨ûnЀÏýäUÏmJN„œÖ׃Oɲ"!€ € € € €Àù $9Cj@óX¹r¥¾ýçÝýMn5ù,Õñ?Ò¿ûèx±·~ùqWéŠêùçŸ×•W^Ygù ¡{÷îúýï¯Ç†Ü£Áû˧æÕêíêþËJõ¡}»^xá…j¯s@@@@< Hâ¹9@  vïÞ­?ÿéOšøõ¿åWæÙ^~…}¿è¨þ룮ŠúA‚~ýë_ËÏϯ z×tU>üðÃúï1ëšL ¤òêau6ºcˆC©kVê“O>©3/@@@@@ f‚$5ÛpšA`éÒ¥Šÿn¿Âlv[+öñÖß"{ëÚ;5eÊ˵´ŒÖÒ`³ïý¹†Ô3Pâð;­¬ØYv$@@@@h¸A’†ÛQÎSà½÷ÞÓµêæ½ù×Tæå¥¿]u¹b“~¢|Ðãr-5ã˜1côß éj³ô–w=–ÞÊïY¦Í^»µlÙ²–:4ú… € € € €@‹ HÒâoD m œ>}Z)û›&|³WÞæØÓ´ªß%êýã š^{í5ElÉÒ%…§<ð¦^]u쎚1c†ÇeZKÆÄÄDÝ?àv]ñµ¯Ç]¶w<­Ýý²,I € € € € PÏŸÆÕ¿î \®¬õ¯ë•e«”‘WèÖ—`ÅÆÕ½?¹GÑanç9Dæ°‚–«þ_ª¦åäyÜä¾NônL?=7}ºÇeZ[F+ø“3%GÇ÷fé@oÏÖÞÚ}¥Cÿüç?^}úôimC¦¿ € € € €\P69“Ä‘¿YóÇÑÔy)U$–u¡2Ö¥hzâÍY‘)f–\Ðï·S•+W*zÇnu.*ñHÀáí­”«.Ó#Ó¦©W¯^•i™¼Ì~+=ö˜¢2üÔÁæåÑJM¨{wÿR­X±Â£üdB@@@@³m.HâÈ[¯I÷ÎÔ:÷É#gÇ[é(}ÉtMZ¸‘@I%> Ðôÿ|ó-ý`ÏAz7²‡®;Fqqq—i­  î¨õØŸ$·Ci¼§´ÖaÓo@@@@.ˆ@ ’äké”yò|)/m®žß˜Aðiö(ðþûï+,ë;…Ø=›E’¨OúõÖƒ>Øn¸¬ýI® î£Kr|<³ÃOú>ÒJÒÒ<ÊO&@@@@p ´© IÞúd­ô`IÕ›Ÿ67Y¹UOòšDà½÷ÞÓÐï{\÷›ý.Ñ}'ªk×®—i 'š1÷1›¸û{¶ìÖ÷}JµvíÚ¶0tÆ€ € € € €@³ ´¡ ‰MR×5nÞÍ´5°,Å@ÀSÜÜ\íøüsõ;r£";/î¤#Q}”àQþ¶”馛nÒu‡è²žÍ&9Ùé´¾Õ}òÉ'm‰± € € € € Фm(HR¢¢ó¡*ñléŸói‚²´w7êÛþ^ºoF„–_ÛM‡;ú×JòþåÝ5~üøZó´å‹wÝu—¿󕯇¿ž\Z*˘„ € € € €ž ´ ‰m§Òs<tu¹6lÙYÝiÎ!€@# üë_ÿÒÁðRmë믹?íª[FêÑÉ—kí€.:éWyÆÄ¡ŽÚÛ;LwÞyg#ö uUuýõ×k`dõÜ]Ù¦¦Q¼¤LéŸü«¦ËœG@@@@*m'HÔCÁUFW}z÷¨Gn²"€@}:¤­;¿ÑÑnegŠž6Ûm|¨_ü¼»n^x…f'\ªÏ.ë¬oo}Ú«‹Fýð‡òó3»’·ã4jÔ(õü·gA’âÀÓÚP¨Í›7·c1†Ž € € € €€çm'H¢‹Õ«›ç¯š3¼çÅUOñQ`Ë–-:Ò½T§kø­Sä凌qõà¯zê¶§¯Ð†ˆ®Š‹‹kÄ4rU» dw4r½Uª³ B |xÒ³ Ü÷(“eMB@@@@º|ëÎÒZr©ßà0)'¯Sÿ^ (GðT 33SGÍ|O’£ÌW½/ï«~ýúy’½IòØ2_ÑØé)UêŽÔüU/©ÏÎT=0=Y…åWãg,Öã£úVÉÛ8;vì(k÷­Öi_Di•í^&Ëš„ € € € €u Ôð7Ýul‰9†Þ÷¨L˜¤Þ)8þQ mCñ¢z P¦ؾ}» BO{ÔPè>o :Ôƒ¼ÙÌl›§Ó9œ³?lòdò‡O—p «aÃâ[ñ›%B]|²õ´ ŽÕ´Ù³)¥-xB› ܺ[¥WÝ®[‡v»é»Í£™(–Eè~Ï~]ëZ¦í;²TRâánïUºÅG@@@@Ú“@ÛŠ „ ÕÜiÃ4yQz=îa¬ž~Ì“‡±õ¨’¬ PIàĉú÷¾½:1Ô³™$]xkÈ!•ê¨ú!wc²¦ÎM=3›#xP¢žŸÖCýt7[+fWÁædM˜™ªøÙ¯iRçu•fXõE&>«—&FW­Úù90|„æ,á<Î]3Gë2òôÈâÇ¡=ꬸ„G5zx¸6eüIi9ұǔúÄÝJî«°­rÎi‹ŒÓ¸žÛ´2Ý5Ã-~ÖR=>¢§6&ÿFsS3δ?ÜqæsÕƒ˜˜]üGoy™“µKm©Ìl_r¢s™²³³5`À€Ú²r @@@@v/àÙŸ&·x&»òsw)++[Á·ÍÔâiîc§ç–ÿZ½ŠlÎòyö?R:ˆ@kÈÉÉщ²:ð[có6+J…óSttõÁ çøíYzÖ DŽÓÒ·—kZ¬T¸5Eï¿RñfÒGáº4嚌߬M3?#uóUv½è\+LIOÍ×4kú‡•Š]oµþ´mÖ¬&ð7KãûIQš™²B3xJ³FŽÔ“iÖ¢[ñº)¼<æl$7L›¥ÄØ`³üß:­Ü9D³f%™^˜'Z§Û6-µ$‘ Zœ²TIÃÂÌL”¥Êªå×O—.]Ôï’Hu:VG„¤| …!§e™“@@@@@ vV>“Ä¡ÌÏkÎ’´3M.+aöÓJY!€@ƒöîÝ+ÛEž-µ|Ô[Q}ûËÛ»–® TüvÙ|­Y¿M«þø´ÒÎLÊè¬[ÆÅ*uI†Ò7oÖ¾uæßö û5ÐšÞ‡%üR CMðåêGõvÚtƳùµ¥ÎY!ÓJþ‹5ò‘DieŠ2òRµ:ëN×õ°D=0z„Ô'W))JøådˆСÔdåœô—_@ 49órR5eÊ »îÍ^ͳ‚VJ…J;E©&Ø1ûѧ•ÐÁ¡#‡óu²|yþŽCÕ%ØW¹[Vkj¢{pÅUIáÖ4ÍLܨKÿ®Qu<µ¬Ô.@ &}ûöéd°‡A’/õéÓ§¦ª\çm™úŤ™Ê1ó3fܯG̼‘%ë\KZõ½q¤‚M$yæLgÞ¸17*PÇy¹ûÍ~$Ñò=‘¯CæL7WmµüÌÕÚT3##8^±å3ElY©;5Y óW)iüD¸J9%EëÓwÉ„FœÉ c˜U¯ÜR‘Š*>ùFèáeÏiØ·{´ë«µ*-Uéë>׳fCøh3Q¥¦d™gŽê’œêT&Ëœ„ € € € €µ ´Ú IÞú?V q¬ vÌn-µ¦aq7(2¢«Yƒç°r¶}¢ô­®‡©n¹«jÁä?jàÚ™ ¯r… PƒÊÞѳ IP¡·z÷î]k#öƒß™‰‰]Ä×Ý7Eè­wmåùýÌ?ù!)¥8W›ŠÔÈëÍú[A2«Z)'}õ¡Nfd8gŸ9ƒ$öL=>fº¶š ËS&*Ľå‚\m3ŸÃn»Áü&q¥€`WŽÔßýV]~6Lߦ¦8/\{ÙSäóòLµ¼9rWk¤E¦ï3´ô±i 8˜¨äŒ“ªk›uˤc¡gËm2Ö¿s†jé —@@@@h÷ÖŸ%·Â´K/Î[ça¿óÌ_i¯TJr²RRWz ©¨v^X½«âï P‹@YYY-W¥£Gª¨ƒgA’@›—ÂÂ*BÕW1BIfÏÂu 4aì$­ÚíÊW|ò”9Ñð1Ü'‚cÇh svFˆî[ø¬Æ ‹UG»tCB¼Y˜¯J ïdfœTN¶=ß:—ÚêØ©ã™ ¾á£´xÆ8tÍÐ’y‹Ì’~f»’¤ùúIÅ4³YeüËKXïæšoø5?i˜³ï÷޵$Ò°¤Ÿkp-³H¬¢–I‡žIŠ;HGޱБ@@@@@ ¯Ó&Õr½E^*ÈLÖ„é©Mß·°ó—åI•ÿ²¼é[¥ZÀòåË®Áƒ«cÇ*Q3šÉ“'ëïý¾‘Íì•QWºn­¿^yj±¬=8êJv»‰xø*°Î9q¹š?r’Ö…Åiþ¼‡Õ%÷-M~2E‘IÏ饄¨ºš©þºÃ.[Q©|‚‚Î ®T_ ÊYSÞî0ç<ê¿dõޱc´þ.3æ:’—a¾mEG½÷îš:rr@@hëq“û«´´TÖØYï‡ãÌ{nn®bbbš¦ÔŠ €€u>Zô Žfϲó3Ö´iŒ^åmÐ7IZiýƨ˜:h[Öä~ýõ×Ú¾}»¢¢¢œÁ’ <¨H'OžT©Y Ë“äWì¥ààsæyT[40°êÜj³™“áJz6IÛ¦'kæ$×,´°Ø$ýæ®H¬fLp#è|~ƒz©‘5Öo?y—ÚUVyÓŠ,gÞO› '¯2g`Ås£3Å9@@@@@ ÝœÏ#¾ „dÓ¶Ï› 4CûyÚº§ÀIˆ’46M´boo×Ê}V°dëÖ­ÊÊÊrιúê«Õ©S'×ù`¿bø>fvES<ØNPÊÚ»d³™-Ô}Ì6%­ïןåâã8a,ëž‘SjòXîMaYq¯xG@@@@ µ ´¾§„öÚÒ\1sw?ÿr’¢[Ä}U5OŽ­/vcæs¯«%ÔÝÒúã©IKíwQ‘ <¸%+XbÍ*ùæ›oíœRmÍnð$YKEùøÔ1UÂ“ŠªÍã« óšþQm¥ÍvÒ FY>%ã]õûâQ92!€ € € € ÐŽZ_D®ÙÓh7±¹Û«¾ã›6m:óÀ³êƒO÷Ï5{y}BíɱՋÆÌç^u»îqÅì ×'ϼ[ª£ 9uÊÚ4ýlò÷÷wH ä zxúpÿ´™”bYHç X.–GÉSª~Ç<*G&@@@@Ú‘@« ’8öïÔÖf¼A9Y;eWTÃ6fnÄ~ÆÆÆVªÍýa¹'Ç• óFpŸùaG®ºê*çË:¶’õîíaÜ£ÔL"©piäî¶Úê¬ÍÛK}=›JâSêåtoµƒ¥ã € € € €4ƒ@« ’i·&ŽØäá³]·BèëÛênUã#Pc‹°u~~~²fXËkTêkÇŽå[RéTJN«°°°ÆëíõÂÉ“'U$‡Ê<˜IbÍÚñ?ísÎ}h¯vŒ@@@@šZß“÷æ^ýªKšjw„šn çhm×^{­ÂÂÂjœ¹pÑEɯÈZò­îYEÒáÇϋÀf+P‰™ÿd*sK¶‚ɯS½ö%qìÒö=õ‰î« ·ºj[ЕšýÔ#Îß)ÁæßþâÅw›¼6½·l™Fr¿ŸÙŒ¤Pö4kV‚¬Òé&@2ð‘YJ2³S”·Rïl³–Ø*rÖ[XrJ'¬I)…9 9KOM‹·>(9õS3¯¤þé›o¾Ñ±.žI‚ŽyÉ2'!€ € € € €@í­p&‰k@a1 Jyg´²·­œýÇje}®úuV䀫Ô7ìÌSÑú”&/T#©NǼåežñŸ®#4[f®ëZ¦ÌÌL3k#¶šÚj8娥—ç™ † u$L›¤à­o+yÝV½ðz¦^š­‚LÍOœ®ufFGXÜ4ýz´ûÂ}Aꥎ¦tÇÈhóïßÌôÈ[¯“7áç×ËúEé }H‰“Ј()75U)'ôØø È>dÚÊ‘¿_å¾8?Fjâ}#¥ž´(M[wîÕ s¾>sI8 ‡Ìr[y¶ÜVp·,s € € € €Ô.Ðjƒ$Îaùš›ÑCÍ«öAr.¬@‡ÔçÒeÛ¡ã,u¤G™6oÞ\¿ IQ¡¬-׃ÇýRI£Í/…Ñ¡Z½nºrÖ~¦Ã÷ë‘ÉÖÌ’H%Íÿbª™‡æp[~ËÔ“õžµ¤ß0Å ®Î(.)ubº þÎc‡£"„âüxÎë²OyýýU%–rNþª',‹ÃÆÄ“äc¦©\dóU¿~ý<ÉN@@@@Úµ@ÓÝ®m<4¢À€’ïÙ’[G»—iÓ¦MõkÝÇz(\ù‚VlÊÔúWV¸ö x±^nHL ë©¢mï™å´jñ+Íâ[n©ÔÄÈY›ª?Ö?RÌn$ cQC(¹ö°ˆ[½æ°Røåd•Ï•³VûɲÈïåY$$ß[£ÈËË3ëjä$ € € € €íD€ I;¹Ñ  -­‹øxÔ k¹­]÷jëÖ­åwf ŒÒïž}D‘Á9ZòätÍ3›¶‡Å&jéOûê[g„ÄäÊKWJJŠV®LÓÊ-‡äšRÞDÐekf˜˜—3¹Ï+q©È'uQG÷a‡Ôk&IAA>þìå÷¬ÔÛòvÏ}ërÐ[–5 @@@@êð:mRÝÙÈœŸ€õ°ü½´a¬]Ö¾#u¥Ÿûé§îÔÏþ󺲞sÝn·™s ¬aÈ9%ªœpØ”_`ö rîGRåj³~üÇ?þ¡9ï¼ ­×WšRc†¾ —žü’…¸€ € €Í!`=nr•––ª¬¬LÖ»Ãá8óž››«˜˜˜æèm € P­€*«-ÇI@ ^!!!º:ê*]lf:x’¾,Õš5ktℵÍyýR``PÃ$VSf¿£Ð ±ºòî»ï*×Xx’:ؼ®‹ x‚E@@@@Œ€gO+¡BAàÆoT÷\÷µ§j®ÔZrëpçb½õÖ[5gjãW6lØ Ìã9*èæÙ~$ݾ÷‘eLB@@@@Ï’xæD.h›o¾ÙI¼åíÙ3íàµÜTQQ}¶Io„޶*¬±ïŠrxÜ›°=Þ²ŒI € € € € à™@« ’Øm6ÙÍÚ•$h}Ý»w×ð!7¨›‡³Iò{–iw@^{íµÖ7ØóìñÚµkµéà6ºÄ³ˆR§/EuèÍZ¾çéNq@@@@ö%Ðj‚$»6¾¢Ä‘#5fìX¹ýv?Kë³ÍÎÊ&Ùw­ÐøÄWdmÕœõÊCJLÎtžoÈ‚]™Ú¼ËUoCÊSjiþ÷ÞéÙ’[VM;†”èÕ×þ¯öîÝ[{Åm誵™aJJо½Úó€pøN_ýÇüGR`( € € € €4½@«’äm\¨ÉsSš0KË^[®×–=«Äþ;4oÊÚ˜ïPiáæ;µ‚#†iddpƒåö¿3]3Wíipy "€@íÖrPƒ.ÕEG<ûõs¢óiýûJ‡þò—¿Ô^qºšœœ¬Í¹²öeñ$ù{©çnÅÇÇ{’< € € € € P.àÛò%rõòÜ4{J “†–w7Dç=«œ‘“•¶i¯†ô9;ŠÀÐ…—TIìÊ\ó¦Ò>ÉR@x´Fß=^}C¤¼Ík”ݱŸ”¹JiYEŠŠ©»FǨ$k½ÞüÊÔÕ1M©k¤ŠViîf­~ë#í:.õt³3_ÐÙæ8BÜyçÚòæóúj˜gAko’wÖoTÌÛok̘1 h±õÉÈÈЫ«_Wö(Ïg‘\ú­ÆÅß©ó Ž„ € € € € xö§ÜW× rõ™©6aäÕU*гfÿ¼Ñ•Îïû`®æ­ÛmÎ9´~þM_j$á:˜¶DS&ÌR¶]:ºíUÍ:Ys7™I¸”²h¦~»z—ŽîùFÛ™¢[?Ó—;óUZ°IOš©Õѫșoì•ÚãÔ_`ܸqŠ*ê¡à£žý *3Ù¾Z¢…/>§ï¾û®þ ¶’'NœÐóÏ?¯í±%*ñ?íQ¯­Y$½³}e™’@@@@@ ~-~&‰mÏV*Rѽ=™˜‚å'Gî»&X"=²øïß×”ýÉ-šuûýuC®&Z5E&iÕÂY³B®.þLsöQÄ”)š}à=Qüßš—%{Ö+Ê3×ÇÜ©„˜pºö:­ßjÂ/R‹‡3}$!Ð’î¹çíXñ'm¹ÙµT^]}=Õé´2¯/Ö3Ï>£çþøœê*Òê®/X°@›:íÑa³a½§)b»îŠÿ‘ÂM0˜„ € € € €õðìϸëWg£æêÕß=r”µßL©”Ê\ýŠVgZaŒsSÑ‘#ΓK¦Œ‘µQôH É0grwP‰y&|m”3@beòó·B%~Ö¡NYÏk‹KœÇQwiF|¤RfNrÖñØÒ¯Ô­_O$N~ p~Ö̇˜ÀH…î÷ü×$Ö Ãvû膋®Ôþç¶z€ € € € €À…¨ß“É Ô˾ãçé©’ùzrÉL­[RщAšñܯ4ÔÄ?lû͹`×y×¢YÖq„~½xš~1eîM[à¼9n–&Ç„hÏWÞŠŸgËH½”R—hRâ­ZqŸYn+[ ¦'ª¢Ùq³–*ÚZ‹„"ðÓŸþT›&¦ƒáGUâÙfåVÃ;®)Ñâ-oÊþ´]O<ñD£ô¥¹+ùë_ÿª?¿ów}qk±Š=»¿ÝKý·øjòï'7w—i@@@@6%àuÚ¤V3"‡]'ÌÆé¾ ñpo‡l¶"É'@A Œ ™v­*‚=l³ÕˆÒQZ„Àš5k4'ù}v[‘Êê9¿­ÏV_Ýxþë¿þ«U-;õ¿ÿû¿JÍ\ã\bËáW¿_ÃWoô×Ô[ïפI“ZÄý£ € € €@Uëq“û«´´Teee²ÞÇ™÷ÜÜ\ÅÄÄT-Îg@šM ž#›­_Õ7äh‚#¡ õ8@bUã«   †HœUš:TS8‹Àù Œ5J÷Üx‡ú~é>¯Ë³zwrèïúT?›ú˜>ûì3Ï ]À\ß}÷~ö³Ÿé¯»ÞÕ–áŪo€äÒl ‹!@rï!M#€ € € €´Ö$i;<þøãé;P½þíSåJÝsÍfîk¯ü^ÿõ›ÿÖ_þò—º \ +V¬ÐäÇÑŠ­Ú1¤~›´[]É÷VìîPý⿸@# Y@@@@Ú–@ëZn«mÙ3¨"°gÏMýÅ4}tÍí^VåjÝOz)j³Ÿb"õ“ŸüDÆ «»P3䨶m›^~ùe}´ç m»¾D¶àú-¯eu±ƒÍK×®ó×3Oþ^×]w]3ôš&@@@† ¸/µe³ÜVÃ-)‰ дIšÖ—Ú@ žúåìYÚüƒb^\ÿ@‰Õ\÷ï}tE¦¯âݨ{î¹Gƒ ªg/'ûîÝ»µ|ùr­þà]å˜eÁrû”ê´Wýë¶6jýÀ_ÿýàãºãŽ;ê_%@@@f HÒÌà4‡ Ð`‚$ ¦£ 4•Àúõë5÷¿×æ[Šu"¤þ³.¬~y™øJØEdùêÖCÁ…n¸¡©º\©Þ¯¿þZÿüç?õÎÇïko¿Rí½Âaö©”ÅãþE^ò¡¿¦Ý¤ÿüÏÿô¸@@@ )@äBêÓ6 €@}’ÔG‹¼ Ðlï¾û®æÿi¶Ü\¢ã œQbuÖËÄXB÷{+ü;_]å®üàºé¦›Ô·oßFKnn®ÒÓÓµaÃmÎß!kŸ”¼KKUVÿ-VÎô+à” |䯇‰'ž9Ï € € ÐÒ’´ô;Dÿ@*’THðŽ-Nà½÷ÞÓ¼?>£ÌaÅ:Ò€=Jª¨ã /õÊñQ½f†IpO]sÍ58p úõ마ˆˆªÙkýüý÷ßëÛo¿ÕöíÛõÕW_iÇþåõ.ÓþËK¼L˜{ƒ ½t <2a’~üã»_â@@hñIZü-¢ƒ €åIø* €@‹øøã5÷÷¿ÕÖkìÎDcuÖÚ½s¾·:öVÈa/]tÜWá½.Q÷îÝ¢   ùûû;›+))ÑÉ“'uìØ1:tHßïß§£þvëZæ|˜÷“nÐ~#ÕçâƒÞŠþ—Ÿ¦%=ªqãÆU—…s € € €@‹ HÒ¢oCp Hâ†Á!´LmÛ¶éé§ŸÖ'÷jçUŽF F¸ÖZ–ËÏìÿá_äz÷+v[ç‹M¬¤$à´JÊß‹Íñù,£åÞnÕãÞßùhà¶Žšõ_3uóÍ7W½Ìg@@@ U$i·‰N"€‚$| @ U=zT ,PÚÞMÚ:´Äl„Þ° Ý[ê`}RÔf?Ýä×_Ó§O×W\ÑR»J¿@@@:’ÔID@"àÝBúA7@Z.¾øbýîw¿ÓÏã&êú÷ür¨íüúêTà¥ëÖècô / ©õ›ÀE@@@@O€™$gIM ÐL[¶lÑ’_ÔÇÊÖwѳ VëœUâ]&]þ¯®ÙªG~ú°âââšIf@@@¦`&IÓúR; €@ã $iY¯ÝýK•ÛÇ¡Rß–Ó?÷žX›À÷Øë£Ë³|usŸÝÿýºêª«Ü³pŒ € €´ ‚$mâ62@ ]$i·™A"Ðö¶nݪ7ÞxC>K×÷‘å^QªSA-c.¿b/õÊñQøN]­»ï¾[7ÞxcÛ¿)Œ@@Ú­A’v{ë8 Ðê’´º[F‡@ 6;vhõêÕzgÍ»:Væ ˜ä÷*Ói¯ÚJ5þ5kÖÈŽÕk—ºo¸IwÜq‡bcc¿1jD@@Z˜A’vCè €@Ij¤á´fãÇkݺuúàƒôUÎvY’üže&pRÚdËqY±‡ä{+tŸ·ºçú¨·ËõƒüÀ¹!{Ïž=[3'}G@@ê%@¤^\dF¸€I. >M#€@óäææjÓ¦MÊÈÈÐ_nÑñ`‡Žw9m^e:f^'B¶,WÐ1/]tÔÛõ:â¥ào]uå@ÅÄÄèúë¯W¿~ýšg€´‚ € €´0‚$-ì†Ð@’ÔHÃh‹‡CÛ¶mÓ7ß|£ï¾ûN»víÒž}¹*òqÈá'•øŸ–Ãß¼û¹Þ­ð‰_‰äköñ+./ÿÖK—_~¹®¸â õïß_TPPP[dcL € € €@½’Ô‹‹Ì €PÀ÷¶MÓ €@³ øúújðàÁΗ{ãP~~¾ tâÄ :uJV@ÅJ>>> t@BBBÔµkW………9Ï»×Á1 € € € €´.‚$­ë~Ñ[h"=zÈz‘@@@@@ ýx·Ÿ¡2R@@@@@@à¬A’³!€ € € € € €@; HÒŽn6CE@@@@@³IÎZp„mIÀž­9ãGjä㩲Ù35kdùq[#cA@@@@ó`ãöóâ£0´X_‡öšÞåIÝ5,RöÀ Û]:† € € € €Í/@¤ùÍiv+àÈËÔ[ï}§^×F)?ýe鲯êæôzê‡:ªacïÖ¿ZþB®¸E£bÂŒW6­^¯Ý¶ ûÑ(…VOhÏݬW—¯5q‘P]7¬‹+SGóæðUDì(uéØG>γveoz_k?Ùªã&†qâFÇ)ÂÄP²7¾«/ŽuTTOéË-9:Q,]uûÝâ,¹kã ­JÏTÑEÑŠ¿õ2íÉÜ­ž·Ä+&ÌtÊž«õo¾«Ïvå+à¢^ºáö;5´¼œ«3Õü´™2kÖë«o÷©(à"õ|«âGD)Б§5ÿØ ]¥ÎùŸé“ìBt¬ñ÷P˜ UôóŠÐ}úá'Æ.R#'Ü¥˜pAÕ(s @@@@j¼N›TíN"€,`ËLÖØé©uÔ©¹ÏŽÒìéK¤àqZ¾bŠBrWkä¤ERX‚–§$É®¨\#o£&%ÎU^åÓRd¢Výáj=ÙY&8r‚mUžéJdâszé®%Z}®Z2,ÑÜUC?ƒ5+åu#þ]•Ï € € мÖã&÷Wii©ÊÊÊd½;Ž3ï¹¹¹Š‰‰iÞÎÑ €nìIâ†Á!4±€Ÿ¿³àØG´jí;šoÍ1ñ‰øÙzgír% ²>åh[IŒ­K…ïë‹i×g®ÐCܤÑÕH¬RÙï­p%<¥µk×jÙ¬xë´tÒ¼Ìôg˜¢›¿üdÓÉNCŸ¨Å«R´ðw›P‰IyŸk§ÍÄe¬™'&ÅÏZfêIѸ`ëÓ}›wLR]’¤ç–›kËõH¬ó¢üýLûo.v;Ââg™±¬Õòg­‚J]ºÎ´XCr˜€H|œâgéï/-Ô3?+ïsqÉÙ>+R³SÞÑÚw–ÉÉ•—¢5Ù¶3ýŒ›¶Ô9Þù ^¡RßË®¡1N#€ € € € €@U‚$UEøŒM.Ð-*JAòUhW× ‹‘?b>…(z¨3J"ùuÖˆû‡™~ê£õëµqc†9¤øë]A•ê:¸ßnçék†]å|¿1΄L*z8O:éúá7(àØMk6s¿÷ììë(O‘Š»>܇é¦x«–Bí=´SY9ÖåX ékÍe QTT7ë„39̲]VºáæëÍXÌÕÃMN“r¾Ô»uPM ì­k÷ÒÁôec6–Oœ—vn¦ÈºÖšâ®Ñ£]>…§LtÅ™"[„óhpÜpç{NÎÕÔœ« ?@@@@@ B€ I…ï Ð|ÖL ·T\Rþ¹<Ð`] ¿iŒ3È‘¾džR¶šqc]Ëv¡¡®€Åác§\5äë[gíYúõä'•–ž¯¤Ù‹µ|ùbŹ&„œÉâ~àç_eñé¡HgŒf‡v쵂víÌ9ÛBI±5eE:~ÌÚ-Þ¤¢#:b½÷V·@ëàÜdËú»¦ÎKQ††hþ²×´lö¸s3:¬ågïËwá;¤=ù®€IQ¡³5…u﬚;·nÎ € € € € ÐÎX¸¾>-V ÈlRn¦b,°&‘˜46þz×A ?{ lÖ¶ÊQÚ“¿R@â_›læÔ–Bê«ëSµ®öŒå•éê["•bÚX4ù¥:÷©h¡Ï03óÅ\[7ïÔ«dŒN¼ûW³p˜‰‘ âZê«"£û{‰ÙÞ¤à.]Í„—CZŸö¾ëªkU2×qáJ=>¿“"öiQšµCI˜®éÓYß9¯jÑÔ'e{àj}ùW×^/}® w•ã' € € € €Ô)ÀL’:‰È€-Ð%¸C¥*ÝcÖ…`?ká+_ÝroBy¾8¨m‰É:ôÍg-G•£•)ÉZ羈ïÙ…´¥II& ¡­Z0u²f.ùL®‰$îër¹»ºÐ¥ƒŸ¢“žÖìÄxÅÆ^¦ðá‰JŠw-e劚¨Å3Æ™ºr”²`‘Vn-Tذ$-~l¨«‚j~ ø¡Ìj^…)š:iªVéæìKNzV¥}LlëR´(ÙÚ%X ³ç)ÆmçúÈn;”¼(Y&Ð7C¿A¤jN!€ € € € P­€×i“ª½ÂI@à‚ Øµyõëú(}­Ò2ò™ð¬^JŠ–ùÚ¸n³s/v÷®•˜y±fOkë‡Ý¦fSŽŠµ¹lZŸüŒæ¥¦›I Zž’äÜüÝÊWT꣠ O§rhÍœ{´ =È*æê'7ëONV²‰N$-~[ }ËëqØe³:ØIAÖd=‡²6®Ó×j\g»m–̺tè-Š ”ÝfS©O@yþò,öL=>fº¶F&iÕK&XTP ŸN!rViB(©UrÎ =÷öBõ--0KruRH“Ïs„ € €RÀzÜäþ*--UYY™¬w‡Ãqæ=77W111²«´ ÐÎx¢Öο –)Pª-MQšs¬azô^ q&‡JL@¡Ä¿òÜ“b·=N|ƒâ÷°}£?Y“¼Æ ±Ž­|õûhf¶üŸŸiEú<¥Î,×âVföƸÙúQE€ÄU±‚*uÀ!™=Wl&(âÞëb³ÒV‰¹d¥À Š€ŽësÅOg\åP¡LQôq›>b>9/æëd©KPÈ™qU”å@@@@ê`&IÝFä@ `·Èn¢Lp ~ÁŒªuÈVpB%~4ÓÂaSîÞ}*4“E‚»õV¸™ ÒTÉff”˜mØC*]\­ÙÍ5kCw+xr~>MÕ{êE@@ö,à>‹Ä:f&I{þ60v@ e ðl­eßz‡@»4³#'üàkfvTž…q^¨¾A è{^UxZ¸¶~š15ާ½! € € € €mO€ÛÛÞ=eD € €ÿ¿=;´€aöÿ×}!¸2/™C€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€"ÉßO]D€ @€ @€‘$ ™ @€ @€ @€ú$È.úDIEND®B`‚aws-xray-sdk-python-0.95/setup.cfg000066400000000000000000000000311321403223000171210ustar00rootroot00000000000000[bdist_wheel] universal=1aws-xray-sdk-python-0.95/setup.py000066400000000000000000000025241321403223000170230ustar00rootroot00000000000000from setuptools import setup, find_packages from os import path import codecs CURRENT_DIR = path.abspath(path.dirname(__file__)) with codecs.open(path.join(CURRENT_DIR, 'README.md'), encoding='utf-8') as f: long_description = f.read() setup( name='aws-xray-sdk', version='0.95', description='The AWS X-Ray SDK for Python (the SDK) enables Python developers to record' ' and emit information from within their applications to the AWS X-Ray service.', long_description=long_description, url='https://github.com/aws/aws-xray-sdk-python', author='Amazon Web Services', license="Apache License 2.0", classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], install_requires=['jsonpickle', 'wrapt', 'requests'], keywords='aws xray sdk', packages=find_packages(exclude=['tests*']), include_package_data=True ) aws-xray-sdk-python-0.95/tests/000077500000000000000000000000001321403223000164505ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/__init__.py000066400000000000000000000000001321403223000205470ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/000077500000000000000000000000001321403223000172505ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/__init__.py000066400000000000000000000002411321403223000213560ustar00rootroot00000000000000from aws_xray_sdk.core import xray_recorder from ..util import StubbedEmitter xray_recorder.configure(sampling=False) xray_recorder.emitter = StubbedEmitter() aws-xray-sdk-python-0.95/tests/ext/aiobotocore/000077500000000000000000000000001321403223000215555ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/aiobotocore/__init__.py000066400000000000000000000000001321403223000236540ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/aiobotocore/test_aiobotocore.py000066400000000000000000000067061321403223000255040ustar00rootroot00000000000000import pytest import aiobotocore from botocore.stub import Stubber, ANY from aws_xray_sdk.core import patch from aws_xray_sdk.core.async_context import AsyncContext from aws_xray_sdk.core import xray_recorder patch(('aiobotocore',)) @pytest.fixture(scope='function') def recorder(loop): """ Clean up before and after each test run """ xray_recorder.configure(service='test', sampling=False, context=AsyncContext(loop=loop)) xray_recorder.clear_trace_entities() yield xray_recorder xray_recorder.clear_trace_entities() async def test_describe_table(loop, recorder): segment = recorder.begin_segment('name') req_id = '1234' response = {'ResponseMetadata': {'RequestId': req_id, 'HTTPStatusCode': 403}} session = aiobotocore.get_session(loop=loop) async with session.create_client('dynamodb', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('describe_table', response, {'TableName': 'mytable'}) await client.describe_table(TableName='mytable') subsegment = segment.subsegments[0] assert subsegment.error assert subsegment.http['response']['status'] == 403 aws_meta = subsegment.aws assert aws_meta['table_name'] == 'mytable' assert aws_meta['request_id'] == req_id assert aws_meta['region'] == 'eu-west-2' assert aws_meta['operation'] == 'DescribeTable' async def test_list_parameter_counting(loop, recorder): """ Test special parameters that have shape of list are recorded as count based on `para_whitelist.json` """ segment = recorder.begin_segment('name') queue_urls = ['url1', 'url2'] queue_name_prefix = 'url' response = { 'QueueUrls': queue_urls, 'ResponseMetadata': { 'RequestId': '1234', 'HTTPStatusCode': 200, } } session = aiobotocore.get_session(loop=loop) async with session.create_client('sqs', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('list_queues', response, {'QueueNamePrefix': queue_name_prefix}) await client.list_queues(QueueNamePrefix='url') subsegment = segment.subsegments[0] assert subsegment.http['response']['status'] == 200 aws_meta = subsegment.aws assert aws_meta['queue_count'] == len(queue_urls) # all whitelisted input parameters will be converted to snake case # unless there is an explicit 'rename_to' attribute in json key assert aws_meta['queue_name_prefix'] == queue_name_prefix async def test_map_parameter_grouping(loop, recorder): """ Test special parameters that have shape of map are recorded as a list of keys based on `para_whitelist.json` """ segment = recorder.begin_segment('name') response = { 'ResponseMetadata': { 'RequestId': '1234', 'HTTPStatusCode': 500, } } session = aiobotocore.get_session(loop=loop) async with session.create_client('dynamodb', region_name='eu-west-2') as client: with Stubber(client) as stubber: stubber.add_response('batch_write_item', response, {'RequestItems': ANY}) await client.batch_write_item(RequestItems={'table1': [{}], 'table2': [{}]}) subsegment = segment.subsegments[0] assert subsegment.fault assert subsegment.http['response']['status'] == 500 aws_meta = subsegment.aws assert sorted(aws_meta['table_names']) == ['table1', 'table2'] aws-xray-sdk-python-0.95/tests/ext/aiohttp/000077500000000000000000000000001321403223000207205ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/aiohttp/__init__.py000066400000000000000000000000001321403223000230170ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/aiohttp/test_aiohttp.py000066400000000000000000000133631321403223000240070ustar00rootroot00000000000000""" Tests the middleware for aiohttp server Expects pytest-aiohttp """ import asyncio from unittest.mock import patch from aiohttp import web import pytest from aws_xray_sdk.core.emitters.udp_emitter import UDPEmitter from aws_xray_sdk.core.async_context import AsyncContext from tests.util import get_new_stubbed_recorder from aws_xray_sdk.ext.aiohttp.middleware import middleware class CustomStubbedEmitter(UDPEmitter): """ Custom stubbed emitter which stores all segments instead of the last one """ def __init__(self, daemon_address='127.0.0.1:2000'): super(CustomStubbedEmitter, self).__init__(daemon_address) self.local = [] def send_entity(self, entity): self.local.append(entity) def pop(self): try: return self.local.pop(0) except IndexError: return None class TestServer(object): """ Simple class to hold a copy of the event loop """ __test__ = False def __init__(self, loop): self._loop = loop async def handle_ok(self, request: web.Request) -> web.Response: """ Handle / request """ return web.Response(text="ok") async def handle_error(self, request: web.Request) -> web.Response: """ Handle /error which returns a 404 """ return web.Response(text="not found", status=404) async def handle_exception(self, request: web.Request) -> web.Response: """ Handle /exception which raises a KeyError """ return {}['key'] async def handle_delay(self, request: web.Request) -> web.Response: """ Handle /delay request """ await asyncio.sleep(0.3, loop=self._loop) return web.Response(text="ok") def get_app(self) -> web.Application: app = web.Application(middlewares=[middleware]) app.router.add_get('/', self.handle_ok) app.router.add_get('/error', self.handle_error) app.router.add_get('/exception', self.handle_exception) app.router.add_get('/delay', self.handle_delay) return app @classmethod def app(cls, loop=None) -> web.Application: return cls(loop=loop).get_app() @pytest.fixture(scope='function') def recorder(loop): """ Clean up context storage before and after each test run """ xray_recorder = get_new_stubbed_recorder() xray_recorder.configure(service='test', sampling=False, context=AsyncContext(loop=loop)) patcher = patch('aws_xray_sdk.ext.aiohttp.middleware.xray_recorder', xray_recorder) patcher.start() xray_recorder.clear_trace_entities() yield xray_recorder xray_recorder.clear_trace_entities() patcher.stop() async def test_ok(test_client, loop, recorder): """ Test a normal response :param test_client: AioHttp test client fixture :param loop: Eventloop fixture :param recorder: X-Ray recorder fixture """ client = await test_client(TestServer.app(loop=loop)) resp = await client.get('/') assert resp.status == 200 segment = recorder.emitter.pop() assert not segment.in_progress request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert str(request['url']).startswith('http://127.0.0.1') assert request['url'].host == '127.0.0.1' assert request['url'].path == '/' assert response['status'] == 200 async def test_error(test_client, loop, recorder): """ Test a 4XX response :param test_client: AioHttp test client fixture :param loop: Eventloop fixture :param recorder: X-Ray recorder fixture """ client = await test_client(TestServer.app(loop=loop)) resp = await client.get('/error') assert resp.status == 404 segment = recorder.emitter.pop() assert not segment.in_progress assert segment.error request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert request['url'].host == '127.0.0.1' assert request['url'].path == '/error' assert request['client_ip'] == '127.0.0.1' assert response['status'] == 404 async def test_exception(test_client, loop, recorder): """ Test handling an exception :param test_client: AioHttp test client fixture :param loop: Eventloop fixture :param recorder: X-Ray recorder fixture """ client = await test_client(TestServer.app(loop=loop)) resp = await client.get('/exception') await resp.text() # Need this to trigger Exception segment = recorder.emitter.pop() assert not segment.in_progress assert segment.fault request = segment.http['request'] response = segment.http['response'] exception = segment.cause['exceptions'][0] assert request['method'] == 'GET' assert request['url'].host == '127.0.0.1' assert request['url'].path == '/exception' assert request['client_ip'] == '127.0.0.1' assert response['status'] == 500 assert exception.type == 'KeyError' async def test_concurrent(test_client, loop, recorder): """ Test multiple concurrent requests :param test_client: AioHttp test client fixture :param loop: Eventloop fixture :param recorder: X-Ray recorder fixture """ client = await test_client(TestServer.app(loop=loop)) recorder.emitter = CustomStubbedEmitter() async def get_delay(): resp = await client.get('/delay') assert resp.status == 200 await asyncio.wait([get_delay(), get_delay(), get_delay(), get_delay(), get_delay(), get_delay(), get_delay(), get_delay(), get_delay()], loop=loop) # Ensure all ID's are different ids = [item.id for item in recorder.emitter.local] assert len(ids) == len(set(ids)) aws-xray-sdk-python-0.95/tests/ext/botocore/000077500000000000000000000000001321403223000210645ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/botocore/__init__.py000066400000000000000000000000001321403223000231630ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/botocore/test_botocore.py000066400000000000000000000064431321403223000243200ustar00rootroot00000000000000import pytest import botocore.session from botocore.stub import Stubber, ANY from aws_xray_sdk.core import patch from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.context import Context patch(('botocore',)) session = botocore.session.get_session() REQUEST_ID = '1234' @pytest.fixture(autouse=True) def construct_ctx(): """ Clean up context storage on each test run and begin a segment so that later subsegment can be attached. After each test run it cleans up context storage again. """ xray_recorder.configure(service='test', sampling=False, context=Context()) xray_recorder.clear_trace_entities() xray_recorder.begin_segment('name') yield xray_recorder.clear_trace_entities() def test_ddb_table_name(): ddb = session.create_client('dynamodb', region_name='us-west-2') response = { 'ResponseMetadata': { 'RequestId': REQUEST_ID, 'HTTPStatusCode': 403, } } with Stubber(ddb) as stubber: stubber.add_response('describe_table', response, {'TableName': 'mytable'}) ddb.describe_table(TableName='mytable') subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.error assert subsegment.http['response']['status'] == 403 aws_meta = subsegment.aws assert aws_meta['table_name'] == 'mytable' assert aws_meta['request_id'] == REQUEST_ID assert aws_meta['region'] == 'us-west-2' assert aws_meta['operation'] == 'DescribeTable' def test_list_parameter_counting(): """ Test special parameters that have shape of list are recorded as count based on `para_whitelist.json` """ sqs = session.create_client('sqs', region_name='us-west-2') queue_urls = ['url1', 'url2'] queue_name_prefix = 'url' response = { 'QueueUrls': queue_urls, 'ResponseMetadata': { 'RequestId': '1234', 'HTTPStatusCode': 200, } } with Stubber(sqs) as stubber: stubber.add_response('list_queues', response, {'QueueNamePrefix': queue_name_prefix}) sqs.list_queues(QueueNamePrefix='url') subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.http['response']['status'] == 200 aws_meta = subsegment.aws assert aws_meta['queue_count'] == len(queue_urls) # all whitelisted input parameters will be converted to snake case # unless there is an explicit 'rename_to' attribute in json key assert aws_meta['queue_name_prefix'] == queue_name_prefix def test_map_parameter_grouping(): """ Test special parameters that have shape of map are recorded as a list of keys based on `para_whitelist.json` """ ddb = session.create_client('dynamodb', region_name='us-west-2') response = { 'ResponseMetadata': { 'RequestId': REQUEST_ID, 'HTTPStatusCode': 500, } } with Stubber(ddb) as stubber: stubber.add_response('batch_write_item', response, {'RequestItems': ANY}) ddb.batch_write_item(RequestItems={'table1': [{}], 'table2': [{}]}) subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.fault assert subsegment.http['response']['status'] == 500 aws_meta = subsegment.aws assert sorted(aws_meta['table_names']) == ['table1', 'table2'] aws-xray-sdk-python-0.95/tests/ext/django/000077500000000000000000000000001321403223000205125ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/django/__init__.py000066400000000000000000000000001321403223000226110ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/django/app/000077500000000000000000000000001321403223000212725ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/django/app/__init__.py000066400000000000000000000000001321403223000233710ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/django/app/settings.py000066400000000000000000000033351321403223000235100ustar00rootroot00000000000000""" Config file for a django app used by django testing client """ import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DEBUG = True DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:' } } ALLOWED_HOSTS = ['testserver'] SECRET_KEY = 'doesntreallymatter' ROOT_URLCONF = 'tests.ext.django.app.views' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'app', 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', ], }, }, ] MIDDLEWARE = [ # X-Ray middleware for django 'aws_xray_sdk.ext.django.middleware.XRayMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', ] INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'aws_xray_sdk.ext.django', ] XRAY_RECORDER = { 'AWS_XRAY_TRACING_NAME': 'django', 'SAMPLING': False, } LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True STATIC_URL = '/static/' aws-xray-sdk-python-0.95/tests/ext/django/app/templates/000077500000000000000000000000001321403223000232705ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/django/app/templates/index.html000066400000000000000000000001341321403223000252630ustar00rootroot00000000000000

Django Test App

Hello World

aws-xray-sdk-python-0.95/tests/ext/django/app/views.py000066400000000000000000000012711321403223000230020ustar00rootroot00000000000000import sqlite3 from django.http import HttpResponse from django.conf.urls import url from django.views.generic import TemplateView class IndexView(TemplateView): template_name = 'index.html' def ok(request): return HttpResponse(status=200) def fault(request): {}['key'] def call_db(request): conn = sqlite3.connect(':memory:') q = 'SELECT name FROM sqlite_master' conn.execute(q) return HttpResponse(status=201) # def template(request): urlpatterns = [ url(r'^200ok/$', ok, name='200ok'), url(r'^500fault/$', fault, name='500fault'), url(r'^call_db/$', call_db, name='call_db'), url(r'^template/$', IndexView.as_view(), name='template'), ] aws-xray-sdk-python-0.95/tests/ext/django/test_middleware.py000066400000000000000000000047131321403223000242450ustar00rootroot00000000000000import django from django.core.urlresolvers import reverse from django.test import TestCase from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.context import Context class XRayTestCase(TestCase): def setUp(self): django.setup() xray_recorder.configure(service='test', sampling=False, context=Context()) xray_recorder.clear_trace_entities() def tearDown(self): xray_recorder.clear_trace_entities() def test_ok(self): url = reverse('200ok') self.client.get(url) segment = xray_recorder.emitter.pop() request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert request['client_ip'] == '127.0.0.1' assert response['status'] == 200 def test_error(self): self.client.get('/notfound/') segment = xray_recorder.emitter.pop() assert segment.error request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert request['client_ip'] == '127.0.0.1' assert response['status'] == 404 def test_fault(self): url = reverse('500fault') try: self.client.get(url) except Exception: pass segment = xray_recorder.emitter.pop() assert segment.fault request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert request['client_ip'] == '127.0.0.1' assert response['status'] == 500 exception = segment.cause['exceptions'][0] assert exception.type == 'KeyError' def test_db(self): url = reverse('call_db') self.client.get(url) segment = xray_recorder.emitter.pop() assert len(segment.subsegments) == 1 subsegment = segment.subsegments[0] assert subsegment.name == ':memory:' assert not subsegment.in_progress sql = subsegment.sql assert sql['database_type'] == 'sqlite3' assert sql['database_version'] def test_template(self): url = reverse('template') self.client.get(url) segment = xray_recorder.emitter.pop() assert len(segment.subsegments) == 1 subsegment = segment.subsegments[0] assert subsegment.name == 'index.html' assert not subsegment.in_progress assert subsegment.namespace == 'local' aws-xray-sdk-python-0.95/tests/ext/flask/000077500000000000000000000000001321403223000203505ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/flask/__init__.py000066400000000000000000000000001321403223000224470ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/flask/test_flask.py000066400000000000000000000050631321403223000230650ustar00rootroot00000000000000import pytest from flask import Flask, render_template_string from aws_xray_sdk.ext.flask.middleware import XRayMiddleware from aws_xray_sdk.core.context import Context from tests.util import get_new_stubbed_recorder # define a flask app for testing purpose app = Flask(__name__) @app.route('/ok') def ok(): return 'ok' @app.route('/error') def error(): return 'Not Found', 404 @app.route('/fault') def fault(): return {}['key'] @app.route('/template') def template(): return render_template_string('hello template') # add X-Ray middleware to flask app recorder = get_new_stubbed_recorder() recorder.configure(service='test', sampling=False, context=Context()) XRayMiddleware(app, recorder) # enable testing mode app.config['TESTING'] = True app = app.test_client() BASE_URL = 'http://localhost{}' @pytest.fixture(autouse=True) def cleanup(): """ Clean up context storage before and after each test run """ recorder.clear_trace_entities() yield recorder.clear_trace_entities() def test_ok(): path = '/ok' app.get(path) segment = recorder.emitter.pop() assert not segment.in_progress request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert request['url'] == BASE_URL.format(path) assert request['client_ip'] == '127.0.0.1' assert response['status'] == 200 assert response['content_length'] == 2 def test_error(): path = '/error' app.get(path) segment = recorder.emitter.pop() assert not segment.in_progress assert segment.error request = segment.http['request'] response = segment.http['response'] assert request['method'] == 'GET' assert request['url'] == BASE_URL.format(path) assert request['client_ip'] == '127.0.0.1' assert response['status'] == 404 def test_fault(): path = '/fault' try: app.get(path) except Exception: pass segment = recorder.emitter.pop() assert not segment.in_progress assert segment.fault response = segment.http['response'] assert response['status'] == 500 exception = segment.cause['exceptions'][0] assert exception.type == 'KeyError' def test_render_template(): path = '/template' app.get(path) segment = recorder.emitter.pop() assert not segment.in_progress # segment should contain a template render subsegment assert segment.subsegments subsegment = segment.subsegments[0] assert subsegment.name assert subsegment.namespace == 'local' assert not subsegment.in_progress aws-xray-sdk-python-0.95/tests/ext/requests/000077500000000000000000000000001321403223000211235ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/requests/__init__.py000066400000000000000000000000001321403223000232220ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/requests/test_requests.py000066400000000000000000000055271321403223000244200ustar00rootroot00000000000000import pytest import requests from aws_xray_sdk.core import patch from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.context import Context patch(('requests',)) # httpbin.org is created by the same author of requests to make testing http easy. BASE_URL = 'httpbin.org' @pytest.fixture(autouse=True) def construct_ctx(): """ Clean up context storage on each test run and begin a segment so that later subsegment can be attached. After each test run it cleans up context storage again. """ xray_recorder.configure(service='test', sampling=False, context=Context()) xray_recorder.clear_trace_entities() xray_recorder.begin_segment('name') yield xray_recorder.clear_trace_entities() def test_ok(): status_code = 200 url = 'http://{}/status/{}'.format(BASE_URL, status_code) requests.get(url) subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.name == url http_meta = subsegment.http assert http_meta['request']['url'] == url assert http_meta['request']['method'].upper() == 'GET' assert http_meta['response']['status'] == status_code def test_error(): status_code = 400 url = 'http://{}/status/{}'.format(BASE_URL, status_code) requests.post(url) subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.name == url assert subsegment.error http_meta = subsegment.http assert http_meta['request']['url'] == url assert http_meta['request']['method'].upper() == 'POST' assert http_meta['response']['status'] == status_code def test_throttle(): status_code = 429 url = 'http://{}/status/{}'.format(BASE_URL, status_code) requests.head(url) subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.name == url assert subsegment.error assert subsegment.throttle http_meta = subsegment.http assert http_meta['request']['url'] == url assert http_meta['request']['method'].upper() == 'HEAD' assert http_meta['response']['status'] == status_code def test_fault(): status_code = 500 url = 'http://{}/status/{}'.format(BASE_URL, status_code) requests.put(url) subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.name == url assert subsegment.fault http_meta = subsegment.http assert http_meta['request']['url'] == url assert http_meta['request']['method'].upper() == 'PUT' assert http_meta['response']['status'] == status_code def test_invalid_url(): try: requests.get('http://doesnt.exist') except Exception: # prevent uncatch exception from breaking test run pass subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.fault exception = subsegment.cause['exceptions'][0] assert exception.type == 'ConnectionError' aws-xray-sdk-python-0.95/tests/ext/sqlite3/000077500000000000000000000000001321403223000206345ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/sqlite3/__init__.py000066400000000000000000000000001321403223000227330ustar00rootroot00000000000000aws-xray-sdk-python-0.95/tests/ext/sqlite3/test_sqlite3.py000066400000000000000000000025451321403223000236370ustar00rootroot00000000000000import sqlite3 import pytest from aws_xray_sdk.core import patch from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.core.context import Context patch(('sqlite3',)) db = sqlite3.connect(":memory:") @pytest.fixture(autouse=True) def construct_ctx(): """ Clean up context storage on each test run and begin a segment so that later subsegment can be attached. After each test run it cleans up context storage again. """ xray_recorder.configure(service='test', sampling=False, context=Context()) xray_recorder.clear_trace_entities() xray_recorder.begin_segment('name') yield xray_recorder.clear_trace_entities() def test_execute(): q = 'SELECT name FROM sqlite_master' db.execute(q) subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.name == ':memory:' sql = subsegment.sql assert sql['database_type'] == 'sqlite3' assert sql['database_version'] def test_invalid_syntax(): q = 'some_query' try: db.execute(q) except Exception: pass subsegment = xray_recorder.current_segment().subsegments[0] assert subsegment.name == ':memory:' sql = subsegment.sql assert sql['database_type'] == 'sqlite3' assert sql['database_version'] exception = subsegment.cause['exceptions'][0] assert exception.type == 'OperationalError' aws-xray-sdk-python-0.95/tests/test_async_local_storage.py000066400000000000000000000017721321403223000241030ustar00rootroot00000000000000import asyncio import random from aws_xray_sdk.core.async_context import TaskLocalStorage def test_localstorage_isolation(loop): local_storage = TaskLocalStorage(loop=loop) async def _test(): """ Compute a random number Store it in task local storage Suspend task so another can run Retrieve random number from task local storage Compare that to the local variable """ try: random_int = random.random() local_storage.randint = random_int await asyncio.sleep(0.0, loop=loop) current_random_int = local_storage.randint assert random_int == current_random_int return True except: return False # Run loads of concurrent tasks results = loop.run_until_complete( asyncio.wait([_test() for _ in range(0, 100)], loop=loop) ) results = [item.result() for item in results[0]] # Double check all is good assert all(results) aws-xray-sdk-python-0.95/tests/test_async_recorder.py000066400000000000000000000015351321403223000230670ustar00rootroot00000000000000from .util import get_new_stubbed_recorder from aws_xray_sdk.core.async_context import AsyncContext xray_recorder = get_new_stubbed_recorder() @xray_recorder.capture_async('test_2') async def async_method2(): pass @xray_recorder.capture_async('test_1') async def async_method(): await async_method2() async def test_capture(loop): xray_recorder.configure(service='test', sampling=False, context=AsyncContext(loop=loop)) segment = xray_recorder.begin_segment('name') await async_method() # Check subsegment is created from async_method assert len(segment.subsegments) == 1 assert segment.subsegments[0].name == 'test_1' # Check nested subsegment is created from async_method2 subsegment = segment.subsegments[0] assert len(subsegment.subsegments) == 1 assert subsegment.subsegments[0].name == 'test_2' aws-xray-sdk-python-0.95/tests/test_dummy_entites.py000066400000000000000000000035021321403223000227470ustar00rootroot00000000000000from aws_xray_sdk.core.models.dummy_entities import DummySegment, DummySubsegment from aws_xray_sdk.core.models import http def test_not_sampled(): segment = DummySegment() subsegment = DummySubsegment(segment) assert not segment.sampled assert not subsegment.sampled def test_no_ops(): segment = DummySegment() segment.put_metadata('key', 'value') segment.put_annotation('key', 'value') segment.put_http_meta(http.URL, 'url') segment.set_user('user') assert not segment.metadata assert not segment.annotations assert not segment.http assert not segment.user subsegment = DummySubsegment(segment) subsegment.put_metadata('key', 'value') subsegment.put_annotation('key', 'value') subsegment.put_http_meta(http.URL, 'url') subsegment.set_user('user') subsegment.set_aws({'key': 'value'}) subsegment.set_sql({'key': 'value'}) assert not subsegment.metadata assert not subsegment.annotations assert not subsegment.http assert not subsegment.user assert not subsegment.aws assert not subsegment.sql assert not segment.serialize() assert not subsegment.serialize() def test_structure_intact(): segment = DummySegment() subsegment = DummySubsegment(segment) subsegment2 = DummySubsegment(segment) subsegment.add_subsegment(subsegment2) segment.add_subsegment(subsegment) assert segment.subsegments[0] is subsegment assert subsegment.subsegments[0] is subsegment2 subsegment2.close() subsegment.close() segment.close() assert segment.ready_to_send() def test_invalid_entity_name(): segment = DummySegment('DummySegment() Test?') subsegment = DummySubsegment(segment, 'Dummy*Sub!segment$') assert segment.name == 'DummySegment Test' assert subsegment.name == 'DummySubsegment' aws-xray-sdk-python-0.95/tests/test_facade_segment.py000066400000000000000000000031761321403223000230150ustar00rootroot00000000000000import pytest from aws_xray_sdk.core.models.facade_segment import FacadeSegment from aws_xray_sdk.core.models.subsegment import Subsegment from aws_xray_sdk.core.exceptions.exceptions import FacadeSegmentMutationException from aws_xray_sdk.core.models import http def test_not_ready(): segment = FacadeSegment('name', 'id', 'id', True) segment.in_progress = False assert not segment.ready_to_send() def test_initializing(): segment = FacadeSegment('name', 'id', 'id', False) assert not segment.initializing segment2 = FacadeSegment('name', None, 'id', True) assert segment2.initializing def test_unsupported_operations(): segment = FacadeSegment('name', 'id', 'id', False) with pytest.raises(FacadeSegmentMutationException): segment.put_annotation('key', 'value') with pytest.raises(FacadeSegmentMutationException): segment.put_metadata('key', 'value') with pytest.raises(FacadeSegmentMutationException): segment.set_user('user') with pytest.raises(FacadeSegmentMutationException): segment.close() with pytest.raises(FacadeSegmentMutationException): segment.serialize() with pytest.raises(FacadeSegmentMutationException): segment.put_http_meta(http.URL, 'value') def test_structure_intact(): segment = FacadeSegment('name', 'id', 'id', True) subsegment = Subsegment('name', 'local', segment) subsegment2 = Subsegment('name', 'local', segment) segment.add_subsegment(subsegment) subsegment.add_subsegment(subsegment2) assert segment.subsegments[0] is subsegment assert subsegment.subsegments[0] is subsegment2 aws-xray-sdk-python-0.95/tests/test_lambda_context.py000066400000000000000000000024311321403223000230450ustar00rootroot00000000000000import os from aws_xray_sdk.core import lambda_launcher from aws_xray_sdk.core.models.subsegment import Subsegment TRACE_ID = '1-5759e988-bd862e3fe1be46a994272793' PARENT_ID = '53995c3f42cd8ad8' HEADER_VAR = "Root=%s;Parent=%s;Sampled=1" % (TRACE_ID, PARENT_ID) os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = HEADER_VAR context = lambda_launcher.LambdaContext() def test_facade_segment_generation(): segment = context.get_trace_entity() assert segment.id == PARENT_ID assert segment.trace_id == TRACE_ID assert segment.sampled def test_put_subsegment(): segment = context.get_trace_entity() subsegment = Subsegment('name', 'local', segment) context.put_subsegment(subsegment) assert context.get_trace_entity().id == subsegment.id subsegment2 = Subsegment('name', 'local', segment) context.put_subsegment(subsegment2) assert context.get_trace_entity().id == subsegment2.id assert subsegment.subsegments[0] is subsegment2 assert subsegment2.parent_id == subsegment.id assert subsegment.parent_id == segment.id assert subsegment2.parent_segment is segment context.end_subsegment() assert context.get_trace_entity().id == subsegment.id context.end_subsegment() assert context.get_trace_entity().id == segment.id aws-xray-sdk-python-0.95/tests/test_plugins.py000066400000000000000000000005411321403223000215420ustar00rootroot00000000000000from aws_xray_sdk.core.plugins.utils import get_plugin_modules supported_plugins = ( 'ec2_plugin', 'ecs_plugin', 'elasticbeanstalk_plugin', ) def test_runtime_context_available(): plugins = get_plugin_modules(supported_plugins) for plugin in plugins: plugin.initialize() assert hasattr(plugin, 'runtime_context') aws-xray-sdk-python-0.95/tests/test_recorder.py000066400000000000000000000024271321403223000216730ustar00rootroot00000000000000from .util import get_new_stubbed_recorder xray_recorder = get_new_stubbed_recorder() xray_recorder.configure(sampling=False) def test_subsegment_parenting(): segment = xray_recorder.begin_segment('name') subsegment = xray_recorder.begin_subsegment('name') xray_recorder.end_subsegment('name') assert xray_recorder.get_trace_entity() is segment subsegment1 = xray_recorder.begin_subsegment('name1') subsegment2 = xray_recorder.begin_subsegment('name2') assert subsegment2.parent_id == subsegment1.id assert subsegment1.parent_id == segment.id assert subsegment.parent_id == xray_recorder.current_segment().id xray_recorder.end_subsegment() assert not subsegment2.in_progress assert subsegment1.in_progress assert xray_recorder.current_subsegment().id == subsegment1.id xray_recorder.end_subsegment() assert not subsegment1.in_progress assert xray_recorder.get_trace_entity() is segment def test_subsegments_streaming(): segment = xray_recorder.begin_segment('name') for i in range(0, 50): xray_recorder.begin_subsegment(name=str(i)) for i in range(0, 40): xray_recorder.end_subsegment() assert segment.get_total_subsegments_size() < 50 assert xray_recorder.current_subsegment().name == '9' aws-xray-sdk-python-0.95/tests/test_sampling.py000066400000000000000000000040431321403223000216740ustar00rootroot00000000000000import copy import pytest from aws_xray_sdk.core.sampling.sampling_rule import SamplingRule from aws_xray_sdk.core.sampling.default_sampler import DefaultSampler from aws_xray_sdk.core.exceptions.exceptions import InvalidSamplingManifestError RULE = {"description": "Player moves.", "service_name": "*", "http_method": "*", "url_path": "/api/move/*", "fixed_target": 0, "rate": 0.05 } RULE_MANIFEST = { "version": 1, "rules": [{ "description": "Player moves.", "service_name": "*", "http_method": "*", "url_path": "/api/move/*", "fixed_target": 0, "rate": 0 }], "default": { "fixed_target": 1, "rate": 1 } } def test_should_trace(): sampler = DefaultSampler(RULE_MANIFEST) assert sampler.should_trace(None, 'GET', '/view') assert not sampler.should_trace('name', 'method', '/api/move/left') def test_missing_version_num(): rule = copy.deepcopy(RULE_MANIFEST) del rule['version'] with pytest.raises(InvalidSamplingManifestError): DefaultSampler(rule) def test_path_matching(): rule = SamplingRule(RULE) assert rule.applies('name', 'GET', '/api/move/up') assert rule.applies(None, 'POST', '/api/move/up') assert rule.applies('name', None, '/api/move/up') assert rule.applies('name', 'PUT', None) assert not rule.applies(None, 'GET', '/root') def test_negative_rate(): rule = copy.deepcopy(RULE) rule['rate'] = -1 with pytest.raises(InvalidSamplingManifestError): SamplingRule(rule) def test_negative_fixed_target(): rule = copy.deepcopy(RULE) rule['fixed_target'] = -1 with pytest.raises(InvalidSamplingManifestError): SamplingRule(rule) def test_invalid_default(): with pytest.raises(InvalidSamplingManifestError): SamplingRule(RULE, default=True) def test_incomplete_path_rule(): rule = copy.deepcopy(RULE) del rule['url_path'] with pytest.raises(InvalidSamplingManifestError): SamplingRule(rule) aws-xray-sdk-python-0.95/tests/test_throwable.py000066400000000000000000000015241321403223000220520ustar00rootroot00000000000000from aws_xray_sdk.core.models.throwable import Throwable def test_message_and_type(): e = TypeError('msg') throwable = Throwable(e, None, True) assert throwable.message == 'msg' assert throwable.type == type(e).__name__ assert throwable.remote def test_stack_trace_parsing(): # sample output using `traceback.extract_stack()` stack = [ ('/path/to/test.py', 10, 'module', 'another_function()'), ('/path/to/test.py', 3, 'another_function', 'wrong syntax'), ] throwable = Throwable(TypeError(), stack) entry1 = throwable.stack[0] assert entry1['path'] == 'test.py' assert entry1['line'] == 10 assert entry1['label'] == 'module' entry2 = throwable.stack[1] assert entry2['path'] == 'test.py' assert entry2['line'] == 3 assert entry2['label'] == 'another_function' aws-xray-sdk-python-0.95/tests/test_trace_entities.py000066400000000000000000000102751321403223000230700ustar00rootroot00000000000000import pytest from aws_xray_sdk.core.models.segment import Segment from aws_xray_sdk.core.models.subsegment import Subsegment from aws_xray_sdk.core.models import http from aws_xray_sdk.core.exceptions.exceptions import SegmentNameMissingException from aws_xray_sdk.core.exceptions.exceptions import SegmentNotFoundException from aws_xray_sdk.core.exceptions.exceptions import AlreadyEndedException from .util import entity_to_dict def test_put_http_meta(): segment = Segment('seg') segment.put_http_meta(http.URL, 'my url') segment.put_http_meta(http.STATUS, 200) # unsupported key should be dropped segment.put_http_meta('somekey', 'somevalue') doc = entity_to_dict(segment) assert doc['http']['request'][http.URL] == 'my url' assert doc['http']['response'][http.STATUS] == 200 assert 'somekey' not in doc def test_put_metadata(): segment = Segment('seg') meta = { 'key1': 'value1', 'key2': 'value2', } segment.put_metadata('key', meta) subsegment = Subsegment('sub', 'local', segment) segment.add_subsegment(subsegment) subsegment.put_metadata('key', meta, 'my namespace') doc = entity_to_dict(segment) assert doc['metadata']['default']['key'] == meta sub_doc = doc['subsegments'][0] assert sub_doc['metadata']['my namespace']['key'] == meta def test_put_annotation(): segment = Segment('seg') invalid = { 'key1': 'value1', 'key2': 'value2', } # invalid annotation key-value pair should be dropped segment.put_annotation('invalid', invalid) segment.put_annotation('number', 1) subsegment = Subsegment('sub', 'local', segment) segment.add_subsegment(subsegment) subsegment.put_annotation('bool', False) doc = entity_to_dict(segment) assert doc['annotations']['number'] == 1 assert 'invalid' not in doc['annotations'] sub_doc = doc['subsegments'][0] assert not sub_doc['annotations']['bool'] def test_reference_counting(): segment = Segment('seg') subsegment = Subsegment('sub', 'local', segment) segment.add_subsegment(subsegment) subsegment = Subsegment('sub', 'local', segment) subsubsegment = Subsegment('subsub', 'local', segment) subsegment.add_subsegment(subsubsegment) assert not segment.ready_to_send() assert segment.ref_counter.get_current() == 2 subsubsegment.close() assert not segment.ready_to_send() assert segment.ref_counter.get_current() == 1 subsegment.close() assert not segment.ready_to_send() assert segment.ref_counter.get_current() == 0 segment.close() assert segment.ready_to_send() assert segment.get_total_subsegments_size() == 2 def test_flags_on_status_code(): segment1 = Segment('seg') segment1.apply_status_code(429) assert segment1.throttle assert segment1.error segment2 = Segment('seg') segment2.apply_status_code(503) assert segment2.fault segment3 = Segment('seg') segment3.apply_status_code(403) assert segment3.error def test_mutate_closed_entity(): segment = Segment('seg') segment.close() with pytest.raises(AlreadyEndedException): segment.put_annotation('key', 'value') with pytest.raises(AlreadyEndedException): segment.put_metadata('key', 'value') with pytest.raises(AlreadyEndedException): segment.put_http_meta('url', 'my url') with pytest.raises(AlreadyEndedException): segment.close() def test_no_empty_properties(): segment = Segment('seg') segment.close() doc = entity_to_dict(segment) assert 'http' not in doc assert 'aws' not in doc assert 'metadata' not in doc assert 'annotations' not in doc assert 'subsegments' not in doc assert 'cause' not in doc def test_required_properties(): segment = Segment('seg') segment.close() doc = entity_to_dict(segment) assert 'trace_id' in doc assert 'id' in doc assert 'start_time' in doc assert 'end_time' in doc def test_missing_segment_name(): with pytest.raises(SegmentNameMissingException): Segment(None) def test_missing_parent_segment(): with pytest.raises(SegmentNotFoundException): Subsegment('name', 'local', None) aws-xray-sdk-python-0.95/tests/test_trace_header.py000066400000000000000000000030431321403223000224670ustar00rootroot00000000000000from aws_xray_sdk.core.models.trace_header import TraceHeader TRACE_ID = '1-5759e988-bd862e3fe1be46a994272793' PARENT_ID = '53995c3f42cd8ad8' def test_no_sample(): header = TraceHeader(root=TRACE_ID, parent=PARENT_ID) assert header.sampled is None assert header.root == TRACE_ID assert header.parent == PARENT_ID assert header.to_header_str() == 'Root=%s;Parent=%s' % (TRACE_ID, PARENT_ID) def test_no_parent(): header = TraceHeader(root=TRACE_ID, sampled=1) assert header.parent is None assert header.to_header_str() == 'Root=%s;Sampled=1' % TRACE_ID def test_from_str(): # a full header string that has all fields present header_str1 = 'Root=%s;Parent=%s;Sampled=1' % (TRACE_ID, PARENT_ID) header1 = TraceHeader.from_header_str(header_str1) assert header1.root == TRACE_ID assert header1.parent == PARENT_ID assert header1.sampled == 1 # missing parent id header_str2 = 'Root=%s;Sampled=0' % TRACE_ID header2 = TraceHeader.from_header_str(header_str2) assert header2.root == TRACE_ID assert header2.parent is None assert header2.sampled == 0 # missing sampled header_str3 = 'Root=%s;Parent=%s' % (TRACE_ID, PARENT_ID) header3 = TraceHeader.from_header_str(header_str3) assert header3.root == TRACE_ID assert header3.parent == PARENT_ID assert header3.sampled is None def test_invalid_str(): header = TraceHeader.from_header_str('some invalid string') assert header.root is None assert header.parent is None assert header.sampled is None aws-xray-sdk-python-0.95/tests/test_traceid.py000066400000000000000000000004031321403223000214710ustar00rootroot00000000000000from aws_xray_sdk.core.models.traceid import TraceId def test_id_format(): trace_id = TraceId().to_id() assert len(trace_id) == 35 parts = trace_id.split(TraceId.DELIMITER) assert parts[0] == '1' int(parts[1], 16) int(parts[2], 16) aws-xray-sdk-python-0.95/tests/test_utils.py000066400000000000000000000005311321403223000212200ustar00rootroot00000000000000from aws_xray_sdk.ext.util import to_snake_case def test_to_snake_case(): s1 = to_snake_case('Bucket') assert s1 == 'bucket' s2 = to_snake_case('TableName') assert s2 == 'table_name' s3 = to_snake_case('ACLName') assert s3 == 'acl_name' s4 = to_snake_case('getHTTPResponse') assert s4 == 'get_http_response' aws-xray-sdk-python-0.95/tests/util.py000066400000000000000000000021451321403223000200010ustar00rootroot00000000000000import json import threading from aws_xray_sdk.core.recorder import AWSXRayRecorder from aws_xray_sdk.core.emitters.udp_emitter import UDPEmitter from aws_xray_sdk.core.utils.compat import PY35 class StubbedEmitter(UDPEmitter): def __init__(self, daemon_address='127.0.0.1:2000'): super(StubbedEmitter, self).__init__(daemon_address) self._local = threading.local() def send_entity(self, entity): setattr(self._local, 'cache', entity) def pop(self): if hasattr(self._local, 'cache'): entity = self._local.cache else: entity = None self._local.__dict__.clear() return entity def get_new_stubbed_recorder(): """ Returns a new AWSXRayRecorder object with emitter stubbed """ if not PY35: recorder = AWSXRayRecorder() else: from aws_xray_sdk.core.async_recorder import AsyncAWSXRayRecorder recorder = AsyncAWSXRayRecorder() recorder.emitter = StubbedEmitter() return recorder def entity_to_dict(trace_entity): raw = json.loads(trace_entity.serialize()) return raw aws-xray-sdk-python-0.95/tox.ini000066400000000000000000000016621321403223000166260ustar00rootroot00000000000000[tox] envlist = py{27,34,35,36} coverage-report skip_missing_interpreters = True [testenv] deps = pytest > 3.0.0 coverage botocore requests flask >= 0.10 # the sdk doesn't support earlier version of django django >= 1.10, <2.0 # Python3.5+ only deps py{35,36}: aiohttp >= 2.3.0 py{35,36}: pytest-aiohttp py{35,36}: aiobotocore commands = py{27,34}: coverage run --source aws_xray_sdk -m py.test tests --ignore tests/ext/aiohttp --ignore tests/ext/aiobotocore --ignore tests/test_async_local_storage.py --ignore tests/test_async_recorder.py py{35,36}: coverage run --source aws_xray_sdk -m py.test tests setenv = DJANGO_SETTINGS_MODULE = tests.ext.django.app.settings [testenv:coverage-report] deps = coverage skip_install = true commands = # might need to add coverage combine at some point coverage report coverage html [flake8] max-line-length=120 exclude=tests