././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.019809 filelock-3.6.0/0000755000175100001710000000000000000000000012650 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1645122371.0118089 filelock-3.6.0/.github/0000755000175100001710000000000000000000000014210 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.015809 filelock-3.6.0/.github/workflows/0000755000175100001710000000000000000000000016245 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/.github/workflows/check.yml0000644000175100001710000001113200000000000020043 0ustar00runnerdockername: check on: push: pull_request: schedule: - cron: "0 8 * * *" concurrency: group: check-${{ github.ref }} cancel-in-progress: true jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - uses: pre-commit/action@v2.0.3 test: name: test ${{ matrix.py }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: py: - "3.10" - "pypy-3.7-v7.3.7" # ahead to start it earlier because takes longer - "3.9" - "3.8" - "3.7" os: - ubuntu-20.04 - windows-2022 - macos-10.15 steps: - name: Setup python for tox uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install tox run: python -m pip install tox - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup python for test ${{ matrix.py }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.py }} - name: Pick environment to run run: | import codecs import os import platform import sys cpy = platform.python_implementation() == "CPython" base =("{}{}{}" if cpy else "{}{}").format("py" if cpy else "pypy", *sys.version_info[0:2]) env = "TOXENV={}\n".format(base) print("Picked:\n{}for{}".format(env, sys.version)) with codecs.open(os.environ["GITHUB_ENV"], "a", "utf-8") as file_handler: file_handler.write(env) shell: python - name: Setup test suite run: tox -vv --notest - name: Run test suite run: tox --skip-pkg-install env: PYTEST_ADDOPTS: "-vv --durations=20" CI_RUN: "yes" DIFF_AGAINST: HEAD - name: Rename coverage report file run: import os; import sys; os.rename(f".tox/.coverage.{os.environ['TOXENV']}", f".tox/.coverage.{os.environ['TOXENV']}-{sys.platform}") shell: python - name: Upload coverage data uses: actions/upload-artifact@v2 with: name: coverage-data path: ".tox/.coverage.*" coverage: name: Combine coverage runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install tox run: python -m pip install tox - name: Setup coverage tool run: tox -e coverage --notest - name: Install package builder run: python -m pip install build - name: Build package run: pyproject-build --wheel . - name: Download coverage data uses: actions/download-artifact@v2 with: name: coverage-data path: .tox - name: Combine and report coverage run: tox -e coverage - name: Upload HTML report uses: actions/upload-artifact@v2 with: name: html-report path: .tox/htmlcov check: name: ${{ matrix.tox_env }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: - ubuntu-latest - windows-latest tox_env: - dev - type - docs - readme exclude: - { os: windows-latest, tox_env: readme } steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup Python "3.10" uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install tox run: python -m pip install tox - name: Setup test suite run: tox -vv --notest -e ${{ matrix.tox_env }} - name: Run test suite run: tox --skip-pkg-install -e ${{ matrix.tox_env }} publish: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') needs: [ check, coverage, lint ] runs-on: ubuntu-latest steps: - name: Setup python to build package uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install build run: python -m pip install build - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Build sdist and wheel run: python -m build -s -w . -o dist - name: Publish to PyPi uses: pypa/gh-action-pypi-publish@master with: skip_existing: true user: __token__ password: ${{ secrets.pypi_password }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/.gitignore0000644000175100001710000000027100000000000014640 0ustar00runnerdocker*.egg-info build dist *.egg .eggs *.py[codz] *$py.class .tox .*_cache .DS_Store .idea .vscode /pip-wheel-metadata /src/filelock/version.py venv* .python-version test.lock test.softlock ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/.pre-commit-config.yaml0000644000175100001710000000313300000000000017131 0ustar00runnerdockerrepos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: - id: check-ast - id: check-builtin-literals - id: check-docstring-first - id: check-merge-conflict - id: check-yaml - id: check-toml - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade rev: v2.31.0 hooks: - id: pyupgrade args: [ "--py36-plus" ] - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: - id: isort - repo: https://github.com/psf/black rev: 22.1.0 hooks: - id: black args: [ --safe ] - repo: https://github.com/asottile/blacken-docs rev: v1.12.1 hooks: - id: blacken-docs additional_dependencies: [ black==21.12b0 ] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.9.0 hooks: - id: rst-backticks - repo: https://github.com/tox-dev/tox-ini-fmt rev: "0.5.2" hooks: - id: tox-ini-fmt args: [ "-p", "fix_lint" ] - repo: https://github.com/asottile/setup-cfg-fmt rev: v1.20.0 hooks: - id: setup-cfg-fmt args: [ --min-py3-version, "3.7", "--max-py-version", "3.10" ] - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: - id: flake8 additional_dependencies: - flake8-bugbear==21.11.29 - flake8-comprehensions==3.7 - flake8-pytest-style==1.6 - flake8-spellcheck==0.24 - flake8-unused-arguments==0.0.9 - flake8-noqa==1.2.1 - pep8-naming==0.12.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/.readthedocs.yaml0000644000175100001710000000034400000000000016100 0ustar00runnerdockerversion: 2 build: image: latest formats: - htmlzip python: version: 3.8 install: - method: pip path: . extra_requirements: - docs sphinx: builder: html configuration: docs/conf.py ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/LICENSE0000644000175100001710000000227200000000000013660 0ustar00runnerdockerThis is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.019809 filelock-3.6.0/PKG-INFO0000644000175100001710000000410700000000000013747 0ustar00runnerdockerMetadata-Version: 2.1 Name: filelock Version: 3.6.0 Summary: A platform independent file lock. Home-page: https://github.com/tox-dev/py-filelock Download-URL: https://github.com/tox-dev/py-filelock/archive/main.zip Author: Benedikt Schmitt Author-email: benedikt@benediktschmitt.de License: Unlicense Project-URL: Source, https://github.com/tox-dev/py-filelock Project-URL: Tracker, https://github.com/tox-dev/py-filelock/issues Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: Public Domain Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Topic :: Internet Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System Requires-Python: >=3.7 Description-Content-Type: text/markdown Provides-Extra: docs Provides-Extra: testing License-File: LICENSE # py-filelock [![PyPI](https://img.shields.io/pypi/v/filelock?style=flat-square)](https://pypi.org/project/filelock/) [![Supported Python versions](https://img.shields.io/pypi/pyversions/filelock.svg)](https://pypi.org/project/filelock/) [![Documentation status](https://readthedocs.org/projects/py-filelock/badge/?version=latest&style=flat-square)](https://py-filelock.readthedocs.io/en/latest/?badge=latest) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Downloads](https://pepy.tech/badge/filelock/month)](https://pepy.tech/project/filelock/month) [![check](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml/badge.svg)](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml) For more information checkout the [official documentation](https://py-filelock.readthedocs.io/en/latest/api.html). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/README.md0000644000175100001710000000157600000000000014140 0ustar00runnerdocker# py-filelock [![PyPI](https://img.shields.io/pypi/v/filelock?style=flat-square)](https://pypi.org/project/filelock/) [![Supported Python versions](https://img.shields.io/pypi/pyversions/filelock.svg)](https://pypi.org/project/filelock/) [![Documentation status](https://readthedocs.org/projects/py-filelock/badge/?version=latest&style=flat-square)](https://py-filelock.readthedocs.io/en/latest/?badge=latest) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Downloads](https://pepy.tech/badge/filelock/month)](https://pepy.tech/project/filelock/month) [![check](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml/badge.svg)](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml) For more information checkout the [official documentation](https://py-filelock.readthedocs.io/en/latest/api.html). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/codecov.yml0000644000175100001710000000013100000000000015010 0ustar00runnerdockercoverage: status: patch: default: informational: true comment: false ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.015809 filelock-3.6.0/docs/0000755000175100001710000000000000000000000013600 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/docs/api.rst0000644000175100001710000000010700000000000015101 0ustar00runnerdockerAPI === .. automodule:: filelock :members: :show-inheritance: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/docs/changelog.rst0000644000175100001710000001446600000000000016274 0ustar00runnerdockerChangelog ========= v3.6.0 (2022-02-17) ------------------- - Fix pylint warning "Abstract class :class:`WindowsFileLock ` with abstract methods instantiated" :pr:`135` - by :user:`vonschultz` - Fix pylint warning "Abstract class :class:`UnixFileLock ` with abstract methods instantiated" :pr:`135` - by :user:`vonschultz` v3.5.1 (2022-02-16) ------------------- - Use ``time.monotonic`` instead of ``time.time`` for calculating timeouts. v3.5.0 (2022-02-15) ------------------- - Enable use as context decorator v3.4.2 (2021-12-16) ------------------- - Drop support for python ``3.6`` v3.4.1 (2021-12-16) ------------------- - Add ``stacklevel`` to deprecation warnings for argument name change v3.4.0 (2021-11-16) ------------------- - Add correct spelling of poll interval parameter for :meth:`acquire ` method, raise deprecation warning when using the misspelled form :pr:`119` - by :user:`XuehaiPan`. v3.3.2 (2021-10-29) ------------------- - Accept path types (like ``pathlib.Path`` and ``pathlib.PurePath``) in the constructor for ``FileLock`` objects. v3.3.1 (2021-10-15) ------------------- - Add changelog to the documentation :pr:`108` - by :user:`gaborbernat` - Leave the log level of the ``filelock`` logger as not set (previously was set to warning) :pr:`108` - by :user:`gaborbernat` v3.3.0 (2021-10-03) ------------------- - Drop python 2.7 and 3.5 support, add type hints :pr:`100` - by :user:`gaborbernat` - Document asyncio support - by :user:`gaborbernat` - fix typo :pr:`98` - by :user:`jugmac00` v3.2.1 (2021-10-02) ------------------- - Improve documentation - Changed logger name from ``filelock._api`` to ``filelock`` :pr:`97` - by :user:`hkennyv` v3.2.0 (2021-09-30) ------------------- - Raise when trying to acquire in R/O or missing folder :pr:`96` - by :user:`gaborbernat` - Move lock acquire/release log from INFO to DEBUG :pr:`95` - by :user:`gaborbernat` - Fix spelling and remove ignored flake8 checks - by :user:`gaborbernat` - Split main module :pr:`94` - by :user:`gaborbernat` - Move test suite to pytest :pr:`93` - by :user:`gaborbernat` v3.1.0 (2021-09-27) ------------------- - Update links for new home at tox-dev :pr:`88` - by :user:`hugovk` - Fixed link to LICENSE file :pr:`63` - by :user:`sharkwouter` - Adopt tox-dev organization best practices :pr:`87` - by :user:`gaborbernat` - Ownership moved from :user:`benediktschmitt` to the tox-dev organization (new primary maintainer :user:`gaborbernat`) v3.0.12 (2019-05-18) -------------------- - *fixed* setuptools and twine/warehouse error by making the license only 1 line long - *update* version for pypi upload - *fixed* python2 setup error - *added* test.py module to MANIFEST and made tests available in the setup commands :issue:`48` - *fixed* documentation thanks to :user:`AnkurTank` :issue:`49` - Update Trove classifiers for PyPI - test: Skip test_del on PyPy since it hangs v3.0.10 (2018-11-01) -------------------- - Fix README rendering on PyPI v3.0.9 (2018-10-02) ------------------- - :pr:`38` from cottsay/shebang - *updated* docs config for older sphinx compatibility - *removed* misleading shebang from module v3.0.8 (2018-09-09) ------------------- - *updated* use setuptools v3.0.7 (2018-09-09) ------------------- - *fixed* garbage collection (:issue:`37`) - *fix* travis ci badge (use rst not markdown) - *changed* travis uri v3.0.6 (2018-08-22) ------------------- - *clean up* - Fixed unit test for Python 2.7 - Added Travis banner - Added Travis CI support v3.0.5 (2018-04-26) ------------------- - Corrected the prequel reference v3.0.4 (2018-02-01) ------------------- - *updated* README v3.0.3 (2018-01-30) ------------------- - *updated* readme v3.0.1 (2018-01-30) ------------------- - *updated* README (added navigation) - *updated* documentation :issue:`22` - *fix* the ``SoftFileLock`` test was influenced by the test for ``FileLock`` - *undo* ``cb1d83d`` :issue:`31` v3.0.0 (2018-01-05) ------------------- - *updated* major version number due to :issue:`29` and :issue:`27` - *fixed* use proper Python3 ``reraise`` method - Attempting to clean up lock file on Unix after ``release`` v2.0.13 (2017-11-05) -------------------- - *changed* The logger is now acquired when first needed. :issue:`24` v2.0.12 (2017-09-02) -------------------- - correct spelling mistake v2.0.11 (2017-07-19) -------------------- - *added* official support for python 2 :issue:`20` v2.0.10 (2017-06-07) -------------------- - *updated* readme v2.0.9 (2017-06-07) ------------------- - *updated* readme :issue:`19` - *added* example :pr:`16` - *updated* readthedocs url - *updated* change order of the examples (:pr:`16`) v2.0.8 (2017-01-24) ------------------- - Added logging - Removed unused imports v2.0.7 (2016-11-05) ------------------- - *fixed* :issue:`14` (moved license and readme file to ``MANIFEST``) v2.0.6 (2016-05-01) ------------------- - *changed* unlocking sequence to fix transient test failures - *changed* threads in tests so exceptions surface - *added* test lock file cleanup v2.0.5 (2015-11-11) ------------------- - Don't remove file after releasing lock - *updated* docs v2.0.4 (2015-07-29) ------------------- - *added* the new classes to ``__all__`` v2.0.3 (2015-07-29) ------------------- - *added* The ``SoftFileLock`` is now always tested v2.0.2 (2015-07-29) ------------------- - The filelock classes are now always available and have been moved out of the ``if msvrct: ... elif fcntl ... else`` clauses. v2.0.1 (2015-06-13) ------------------- - fixed :issue:`5` - *updated* test cases - *updated* documentation - *fixed* :issue:`2` which has been introduced with the lock counter v2.0.0 (2015-05-25) ------------------- - *added* default timeout (fixes :issue:`2`) v1.0.3 (2015-04-22) ------------------- - *added* new test case, *fixed* unhandled exception v1.0.2 (2015-04-22) ------------------- - *fixed* a timeout could still be thrown if the lock is already acquired v1.0.1 (2015-04-22) ------------------- - *fixed* :issue:`1` v1.0.0 (2015-04-07) ------------------- - *added* lock counter, *added* unittest, *updated* to version 1 - *changed* filenames - *updated* version for pypi - *updated* README, LICENSE (changed format from md to rst) - *added* MANIFEST to gitignore - *added* os independent file lock ; *changed* setup.py for pypi - Update README.md - initial version ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/docs/conf.py0000644000175100001710000000265300000000000015105 0ustar00runnerdockerfrom __future__ import annotations from datetime import date, datetime from filelock import __version__ company = "tox-dev" name = "filelock" version = ".".join(__version__.split(".")[:2]) release = __version__ copyright = f"2014-{date.today().year}, {company}" extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosectionlabel", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints", ] templates_path = [] unused_docs = [] source_suffix = ".rst" exclude_patterns = ["_build"] master_doc = "index" pygments_style = "default" project = name today_fmt = "%B %d, %Y" html_theme = "furo" html_favicon = "logo.svg" html_logo = "logo.svg" html_theme_options = { "navigation_with_keys": True, } html_title = name html_last_updated_fmt = datetime.now().isoformat() autoclass_content = "class" autodoc_member_order = "bysource" autodoc_default_options = { "member-order": "bysource", "undoc-members": True, "show-inheritance": True, } autodoc_typehints = "none" always_document_param_types = False typehints_fully_qualified = True autosectionlabel_prefix_document = True intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} nitpicky = True nitpick_ignore = [] extlinks = { "issue": ("https://github.com/tox-dev/py-filelock/issues/%s", "issue #%s"), "pr": ("https://github.com/tox-dev/py-filelock/issues/%s", "PR #%s"), "user": ("https://github.com/%s", "@%s"), } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/docs/example.gif0000644000175100001710000056006000000000000015731 0ustar00runnerdockerGIF89a    % +2= "+ . 63;&4(>$%%$')&)+*++"&1*.0#*?.35.4925616;49>;;;CHLSCKR/8C6D+?^ b##j:@F?EH?HIEEEAFJCJMKKKFMPIMRFQRKSUNTXMYYSSSQVZTZ]ZZZW]aY]bW``]ae_ch\hhcccadheimkkkglpimqnrvotxoxysttruyuz}yz|=="Lvz}}!! NETSCAPE2.0,  H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ6xAȍH^Γ;|ԡg|n W|x/o?|xr,`Nα u~@qv}W^z a؜}z(w7uw]xڧ!sű $z7b{̭8!ݑޒ4G9q(#EVݝ(_IFwjuTڈ9]kV%!cu}cVɢw (\ 0Fݓi7(rYO_H g)5!uzj*ޠ_DlIdJ]n貌Z&Af}zfZnVmB;dӆ}^ʞһ_^+Y2W9u2:eͦ뮻j|.玺_ .}Jl,\푇&\( *y6kvcA^lyI`Ogc=kl39?&+7Ԯ[M=șθ&|NJܪrkbl3ofKcF{7Z.N4aW-͆"|eC׮%֍]]$( :?3:u3}j3y_b#cH ʰ8BLT#G>6r GaC$C4^}r\4p@4ȥ f@%0( d2 L)Ќ4Ltd1xb3%Hs}l94~M+P' Qp@&4SBM&uӜENh(:ElVԴf8sQ gH5Rs״hBYQmsM\gLSR(INԦ"5jK/I< @i7:M= iC 7`j`)Q1؁Zw0˶21keћTEj:JRV}`JҦ^~&P ؾ.4)`J¢lG3٤5fi :;%OXdP׬^um{Mm8 Fӂ67Eb3* =bU>DͰ*H :K58xT*4x̂I+uBq+ڝ>t-,`MP .v~La u  3{upw^2%QF5U',c3ąůOc X!VpM\%pJ'X&wSYƷ@+awXU`8pw^A$ Bu[{M2,ZlvVcMbjVBεs\e2um=lm#Q 3g?n&yQ2M 7nOb9e[ x:ΑA->E8@oyqs]^5NhO1y8szJ;о}n$'썏┿c|.9/%OpCMh䗀@anX{Gb 6q2u+E/;??u䋡 Yփdh}q#^MЃG'陿uЇR=7zGwIsPUMpmV`k?}ftgg+Vp_q1^%Ur@|_%)PvPe(XgYv7hL @2WFhPUP:8XgHD(YVRXK`HJȃlׄh86@8fH^T&(ixiP|^`mPb`}Kp ߷b|`}*pjg urzHoAChTg@pv n|rz:V@mxfȄ*'G*z0hvfnyuq{f}Pn|s0vs&ox8׆`uHlvnFPXo؃~x/]3pVp<g`n@FGA^Hω}hP Z6>H9i3}x .9ɖxɕɓvd(b_Iux|}ǃuH@c -Y9\xYvP~Zv kJV eץgg(ׁoiuqePe]`ŔN٣x 6hQ(IJ`R8YXyk>JVaZؤVSڦS=RZdir ZazXjVjm>b0y{yu&wP73U=/ptpyKy`:g(ȓeϩP nRiIP5p~gYgjv|@ `q0ya^iW0KjVP\ ʫk*LڤXl2늕抮zv9^Z[yj{J:{*kJ^k8':XP$jʒj).V0Jj?WOVU}٥u,(j(J4z_]Ym:?zyA(kgڒdeʳv:ʰaTV`v 7Yb\U,Jn. zзyX_PxyG)p`U)7k q_Ph' ZV^]%~6({oz)`[gw ``z@ڳ (M| UK RZVevۣ_h`˴+rJ{I;K[[(H+\Pɱ a496yu6W*-uvuXp{+GkˏꜯP.~<ܔ>߾΍]ܱm.^ënn]\=_z ̂`:ؤUX@[^ -!_^^)`)幭!'g.3/7o_5963O?F$nɞ׮?ol'pPl\)O!MeOtlMR_(O-ۉWcYOQ/)LOgM?i0VmO_}7_slXGOeVo5eɸ_o3j)pP,_PU<ܥܗNoMO-;o湿¯nMOr%'?#@@V  5+&!'NHQƇF3^,)F;cʔ=h9CE/#z9hL*dɑcǖKC$!R}j2)͢!tr&ρ=lV PHkLn7VRLQJ D9N ƍё ʕ-_ƜYe;pЁphџ9Yj֭]:ulڵmƝ[פ[pō ߉rg7]ur CO|8E#)cǏ;eYzj׎]>nǟcr0v@@4Cp8n>cB o+D\[5Q\whS[w07`y׶.Heb nŒǏC:G&[96p'a #a.@8`0 =`A w4C0C>;ˍ1AdP,z `3B{xb%7 drh9+a |,'d3pwm !<=b8Hn& @PO7*T!@`7\X?!V +fc B}!5=Jd:/`3K=7a$7@; ܤ }P.kt,8/*C`i?\X@.֛ pw ~H*72AwZ[w+@] DaŚ v+d6= J91HqtoKyJcZj5J C+|\<Ȱ-ë́8a]$o} Rx+X"gBz=sTemSgPn>p }H*2@P0cpn$/0Bp4h3E 2d6J`Ձ '6'϶8IvW1eXYfdeW 9. z !ʃxK[mpԮY|N|(@AƐ` -$1pƊ:3~A*k ڞߐQ VKy_YAh( r Ʃ` rQtz zqTW1n[*Cl]3^oM&6B,vCWmƸ*\C0dꥍ 5SqC`P55AГN0)P7@x>tHB=Y<'*b3 !*rʿB| b |x2j*t;aKRMun+~3?X~~z 8afIZ~I*I,5~*7&@ Ca:[>9D <@2 5 ?;)A D 4ѕl-KnY  ><4$\(x'| 7)΀@)H!$/q TAB%tC$ C9:;<=>?@A$B4CDDTTUdVtWXYZ[\]^_`a$b4F_  flFhdhtjtlnFoFm,jPjY;5ڊYT-^rb]W4p6-VW\4_s~U'3x[W{a28A @deE P0@fUN{fUV2h`0ueV镖B>Hhi^)tVƃ,ӹk{]߅YLj oY .H4WC16QɠM4gT,6ʣQpLNXHn|.d&TFeq 5NV/X,Ԗ)T\mimhm7%g|f&nm=m,H2n=?VnL6TLt`ﲞtQB% Po-pW_SpM7Jt )#3 1)xFa >`5/0W$1,@ XXv).!=r38n3`&#vOen(vwYrhmm0ȟm2mna:g8z7s?s˟x$tDgEGuNoP& pM p=Qgy8uToyX_EXFo3>wxXb7~g.У,V~RLFm3^XPC'ExUps{Ke~Jut6Z]y7uy}ğM^yUѶWg&|q/ȨlX72xi"D,(Ƞ 2lPbj8#H Fɠ$V\̘&Μ8# BB)h&UhϢlPP =+ aQ(k,ʹj\TTi.޼z/.l0Ċ3n1d&Sl2eA7s3hΕ#.m4ԪWn5زgӮm/ܺC{‡/n8ʗ3onX7t˾S9ڷs;ƣP|׳o=#.=? 8`AQF : J%~j!z!}u`.")"-Van%5x#9#f2GA 9$Ee>VM:$QtJNǤYj%]@eeV^yey&5i9'uηdoI}'馞(*iJ:) 裟j)z'駥z*٦*8z+'+ ଫ:,*띁#ثf.;-WZ-ݪfB{afJ.]>J{/x[(/o|0 WF[ib.|1%hVn|2-+<3.5s7˘3A ` I+͟%"4QW^Yk-(c5a}65}id_m6u} }7}7{=8{+&ں83tK~9zQ{Kv9鑇N]⥫c>:엷[ۮ3U{;v[+unmwtK??=YWoh{v=%Y嫏.}|j?WFgKR #-N`(82 r^$`CHBP&wnd! uBŰÚ s,z/!=#ew? "G@c0N(EKPs;h(j'0r"X&4Zb$8ʱ/[bh=~j2" (H!q\Q!>q]z\$$HRB$& tLr?@@vjiUvg|P/U²q-s\겗/y` ier-bd&4o)ˁ2, 1MdsތL7)MSa':/UN3K8)Ͽs:U{޳'LF%-P>cσӟ CΈJ B+MjTF/Ԛ-5Oh6+F\)HHPT45Mo*̜ԗ<. T[ u 3S3t ~i$j-5HD\Wɰd-k$ϊEul[ ׸qt]*ǼՍ|Kf%UQ7X3*vblcjqe/j6l? ZҎ{\}rP'j82gNsJ 0WywNk8{8z|Hwu+`{ڒzo+SX.rnux=sjU7n]gXwc3~x7߽JyO=e&gٳ5/zDKoɗ?b#:WӻI߾yѣ?va#AWC=$[}q^ٞ= -`YHް͋9 ظa_ꩠ\ ` Rƈxi٠j`aRR .%!z_``Jι`_jae !X\a^!!am a"a#*#F""_bZbߴ[qb'NѠ!b a&` b*j"+ :bҰ`&۸/"0M="1],)6c؀0b5 ڡtx6"ԥ6r6h9N:cXbN}2`<#>"H./2$ X;#DD*Q%BbF2!JcD~СEI Y,bJR d.XKNFܡMGbdI$Q &O@`A:%LP.!U%dG.dVjO`TFT~%CYOHKR&LJeM%x\e]ڥe?$_^ )f\Be^"b&3_bdcE[d _^[ft'^%zMʥa."ijb&3fifWk~ZfXeKb&nL^.f6eoV ;' cZ"%sfZz]PFHJ&vV%M\lfwfgLubxދn^gzzN6{6Pp#qQ$Wf%У;%c (|2"h,{ngg:Xc%'FZfdNB&gfNex@1(> sҧ.Kb(,.JcgR zhy~ 2)hyfʐigkn)&z$& *iZ',Ă H(@xEdlv~,Ȇl&Ȗɞ,ʦ+ɮP,5$,,l>,-,ml"N-V^-f}vmn42mtzȚ-ڦƪF-۾-j-`Fp\V*Aܪ-.l.l- NTbn ~.n6⒮B(IdT.@j.l.jBkvjl.///:njênT\n@ I"oֺ m6/t.-+Bn掁.n~T/ pz聱*@ p,yAڇ-/w"n@lo0 L0pn80GSgp wcXj WG,-\F_iek_q&0b/ `x Bl/q@X(d 18Y|1|A'AL At A1XAAi(π$2AA1*nRe1  49 LA0L3A) 䁱 2L0e 2 DA( + |3' 33 [3`Ҋ^[/v7{@32k4*#lA| X=r ,d@ &PF, TA+r}|@WOF LF9PJ416Lsn7c ˫_`{ BY@<  D|@ Lu(?+vBTOFXA$lSF1 A Lj>cG7+naw4:A (qFtN6((H̲e,J&w}qXS eW ~7~;/WW>_~wsS~>駾C꯾G~w}l/dC<|*=:T}?Pc1-Ą n6?ks|wȰ9Ά{swOP 4???@|8`A0 !F8bE1fԸȑcGA9dI'QTeK/aƔ9fM7qԹgO?:hQG&UiSOF:jUWfպkW_;lYgѦUm[oƕ;n]wջo_xqǑ'W>4 !,      ' 8* % 3= $$++44<<&7$$$+++%)+.3633316;49><<<&+22KCYj##b x((ALU\!c!#k#&t&){)6D,>;;AAGGLLOOPPTT??++..1133//559977>>;;??AACCGGJJMMOOPPTTy}v"L+.13/58;>}?ABGMKOPTw H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ"bȁ 9r /Μ9qʇ'.yȡ}ہ̘cԡKb<ǹ~x{đs//ú\|vi'{)~2 q^}W9_r W-܄W^0_8"x)^ px1X$ #y睑] !%ziH!x!H]q1y xj<cNGdZW\ꦜ- $N8F'yF7}nu5 ؜y v)1|ߥH& 417q5A{HBezI붏^J+J~~jfkH|K.U.e^_r7ޫ隋k=\n2y~9/lp.p.R(^[<` xsRg.j?q݂+tAW']!V]>/v u0猭ֶzf nCL;J䦻m8J)5T=ݒȶa<0UҢ ux5lij#)#\s]⼖V]'2 L<6:]elv g*c-ZV+9d+?;3=}g p%jY*O9iZd7qI@~ZպqWE vB ;v֊aqU=^/ʊM@d<( v*47hဆADbt7&;bHD*Zqe,cE~SVZJfDqz(76vb 3Q1 RBD P2G:A&5) rT(GC'L 8FYkrfV8JTRv*w^̡!bҌ1HZƜJ`l&laʧ ILAH:?Mm; Q>QBUOa%YЄsdC9rl(w XrAeVK|JW+ ViL} (A Q59:hP|Ѓ¼饄3ԝ:UM jS(B^DŪ;T/25\-hW*U®=-*WӴ.UB? keZJUtN¦k/Њ\ⱐE+0TpZJ8fsr6$A/+إEYZ~5Xg`[Z6]- s&kEkzղ^ն5qwڹʶąSiZ25aKb79mO[v7Z%nQ(޴T+jk^׭pUjbIU*iȻ%-kz֝ⶶ-rBR`n=-{TgiEqgL"GPUj*0\׺qs,x2?m؉v~`)~e<$ A{8N=,` '`\1/}/l-4#nV2 +/y@3z7;&35 SYs%?OUwW9M^9.GYk}/ll.R cB^Λ∸xB˩6.TspWw׭w;wYg{p "@Mq8わ|/x 3/sGOs>4s TE)d z׿#p cO+v"ȯy;Dy*v*sC?| Bvo菷?Q [Vw(b?{b{n$}$W4bbp DGw X@G&؁dAgr(bxz'}7dr0dc#xmAX8؂G5p5Mhm׃TxhXHBH6c'Q=c8wb-hP Up$d[  U灈 m@C@ o TEp 2 A c|0 @& g mT IlX ` 0P ` @dy !ЊZ  | P Pr'W6ԗMȅ∋%x`/xOȃg|۸~gฃȂGFULxTf׍5y61@ُ^'p;(Vp GbxS<ȃ`aX  8 8bTpN1-@U7AYLu 'iiF5gHf(܈  IHHen4VY8hiHِ'ٖGمkliG p 1 [ywV P R v2 yP  qF@` 2R0Ap LR #0F P X P Vp 4A a U` N 08 G '@Y1Бiȍٕ9lifW>؟s\[aXQ ny)AL؅6(W` &Ḟ#A١qAY` GE żGYcgN 5A YCJ94ٚ#Ix@ PYJ|GڥIUɤ MzPPɁA:Aڎo^ڥZ(IAXZʧM6o:szdgDUP 1b V Yq@ C D2@0~~rLg Y N@| ئ G0Py| 0ȉMJ 0 @S# #U`.X OP0) )W yJatzUzwO*S\:n:[*j]jIr*Y}ZRhZY';^ru #8a*v +}4 'Z9Z}l:   NAhPzd:ڧ|: KLٳjدix)ʠLKJc!H똧DKE+V˗AMPqA Dj W g11 pp JQ` ? IJȅRp 猡 ,)WT r  8 0 LФx@ 0 A y k ͨCzc;{EkfKX;]qxI[{k1h[l۱#i`{JX#y)qi8Et Z7,} G* 7+ :M UMZLj$d+6|*,.L1,8A\HH<4F@l@h ʛ#Hq.q+22p 1 Ф  Sr<*`0 TfI~ʪ c|Lj<|O?\;)WD|ʰ^*˶|a[?\ܯ UM** e  s]:m: W|a H gL,Jlg*LjGJ̶ۮKN O<) <*Ëlؖ ` OɘQ aav22@  Ap| AO@> '2 rF wċÎN` L2` /b' j)|LblN*\<| ݥ ubگy}3 F/ گ YPaUXLA ל-}C`α ̵CX˴۫=u vϭT̽ɾMܭ}ې|gzpU mξɑIկL ݻMLcf^b_?=OC >Dvqxr/OYgNz/cFkTnp_YOH/oF_ mlC߯pp!o@q=O^T?odoA_}gOA 48 c |H$CTj:B rtPHJ#e [&TH&C|X H"5PLSaP2kS)ȠL~!¬V݊cRT6)G{yRҴ\Uި$DOD1E4TED;cQgFqGz "}D2I%;rʁP42I-K/ԏMJ2G`4d4<1sN;sEn4P6TPElQG CB!2uRRr48͍<{4UT=NUW_X+KuCVLYwu7]$҄8״VT+"8]">p{E7baVH;0KbBhe$}sV$T$ ra5 nAl Xb,[dDƧV6PSn*¢`Y*n~䓧DxStw]$/b6KL;jE%Jd%n)b~=Z$`y4>TmV(YĔ8O2Iy:}pS&i] "Z >d%"VZ$Jb%T*Va&J@jɌR@piE< F e)U'!fK%p*(\8́ E1LhUK1U Qv+#"Rh'… F1.8aFm DC;9TdB&[@p!GDk T5,G+d0Lz&ѡ@)}tEEAPl8(j, ZrY<9pSPP-g9SY疌!)0 !D\sTq B Q,eC.7ens1(kP &rQj X[4 B)zgz pb[W>Ԣs@nqTdt@*V!K +򬨉Z¬q,6 v۠?Q\cjYⱣ)xSO (*X9 inu{H4x@ή@6:W8lP=mi`)nsl'. Inp1O+~ Q FH&dO1cѪ(BZ8!2nY3 U| q.&4+΢\+%xbsQī8< n+F[5@m.+WMA1,4pj{m>BX́-%, \ &5`bea.P9[nDqxBD,vVl`cLIrqU"b8y4 Aja> `+ӟMsJ/Nd+([N,'D WbLpI {rA-"='lIaL&Elٹ{=`&WfR+ș:30E  UΨ.bhr`h%b1 "xLff"*v$fi*~K_ 5ԤKZE폗 [&< eNU>s{'"kc_7:dL2ǑFަ&zzԵus)]{~عً^vo%ӽorGտ~?g /  XDT=[ #>PL$ȽMOȽ  ࣄ 8dT ȀM, \P @'$ SL$R>!-B T5dÄ4,+l9:>!8#>O=LЂ')Bc(0=((DRhG\M`I>OL>PE;dV>ONAOXHAIlBĂ ȀH@ `E$JHCXE# 8d$#$Wl|񻥆=AԂx)ck$E''܄w$AtB @-D>I@ >ƅdHؘIhB@RzG,PI! R؄HDJ C!`%XE#IIKđDņɝA,XI(DK(OȽhdJIJdPLĮ >O`=H@F#KɷCPCR_ԽKJ,F-(IJETFݓܣ#LC$%ÄɤILLLxƁ0ʄHR%h%@LD*tǛ\EKALXK X[ pKD5`\$D̽ZKL=NԽlNxNƼMMP@LtILL 5 ±MN,EZMJ=a=܄NN->I=DR }A?= ĂsIԀE|C ̽ A-XKQ̄ lJRIdě$yJ@%~- HPKdP(A%C-\=%Q,A` C%+5MƣALH4A @be iU?X\ЀMAQԀ0dM8TbpK)0U^11(.X<20e?300bn<_^3+Nenf83bF~<.ffb6n01(S6sl`1`kg}\Eb#m60@g3v\v<033X>1pf\&07aMY^=,x1bSi .&jYc2 pdeV.a1g^/>cg^Kg(&33`g1Ifi~l}&ncl&d@bl^<>f֖RCu6eg2[h}0ff^<`^^<4.zFnVgXhJie^nHDFlo jKo׆vޑU;fp|2.0n.nedv2|Ja ^.vknt a6a^np]&onfl3mRq]>K(`-ʈ&.k+6q&3600dg0&i=&gcggk@/xc/.,'3X>.8,ꩾL65@t-aQ2ex;p/`c2^sHoc3nHg`s=7Ff[&r>i6mWggyikrfjA'b\oO`kv{Seck ai#Xp<r'iWww_$kUw@.~wԥLvIY7 hWbwBxD)x 6_FaGyNO^yy y~e9q_WyWzuP {?ߞzm'@۴-Unxw't ޳aZܹ+y`G­^|/|Oȗ%zcßxHG=з#HHܧ<{_+Gţcz*FH}EpE0F@HFΫDF HGP% V(攄OOK,h „ 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$JVҥL0rΜ:wSgK7 E@O-* R$Dm `H v,VB,P@pSn6%޼z/.lM1/#F?'ShQ=dP$F,D5"%`8eHȰ,/n8ʗ3ooL11I"ڷ :)!"Rj@HRM)6 Pms * : Xc0]diؓw=DrQyFy(@kg_mJAtI(!=#A 9^6m$P,dC$pAXie0#WeՋI ;PЁ' A]Ai uy'ya16LI29hwND$ag$T"0kD"b|I\-@@:j|*:+FJ2CO ' :,{, (d$ ${-j--hb!@24mJlrW.:-Β˝/˧^/ +09ӟw( iЩgV! CQ_)2%+Ĕy f$ GD}2E}4C"(C)"d:/W2Č+Jh7Y!ZL, 4z(=D):[)=dGH~Y%"Dv(m"& &c!u--/)@7U$;n7!$V&_!nF|A*|W>12Y2(j[["Q:btֈUd# P;9& j f_ l!or9͑ RTrtkۛArᲽӌH̱J.p2( e>R9G)3Gö8y;MUԫ dKrA@LkQJ{B2_\/~PghEA7On\O (=@3yc] /* \%Rt3R1͎c WFWd hD7!XOCTu[4䙰ӽL»-S^bMWjFjT(A Btokב#@L4%Ro =a!oȫDk t-@4)JCOTB:ZgAJA{WB9N i e\p$JH0mڨbȇ,  "z $u]r@*Rws߲_KuBeyBI8ف"^(ԴciϹL?4%R~IDt0ԡAiTQw4}|wΦAk]n4}I4DfXk8}bn+H(dBmB^ْB0!_TMդ$itJ<߷UE)$)4 DAhА MKh\lKf rn5e% &aaTU*!V^Ĵ f~!NPU&O!Tt"!ʨ9MڡỀKm""&@w]*$NcxW"Q'~"uĀ)aVv"("+ ab+"-Ȋ"Fb-"/*#rb/#1> 0nb*#36q3V5ŲH4!5^7~cHtӈqa."8:n8"v:ƣF(1D)A0au߁|(|Bϗ>&QVN.]A9!ċztf{2l"@hig8DR*2-9vާB^DM_*ݘ>\QRW1͞$q_|n W2ċ g@X. (i @'}¦N$h0zUe^s"N p`,%& >$ a|߀`% ` aɮK* 8*{B @BiwOBjObr7!> %0$D,lLJXn-4>(or#:>CghlzKfH*eDu4 .ʡ*>.a;^*c~.nr.v~LA@h4@*WVnѴc\AADCծ2V+C\ 3U6K /F KP8kjPAEl!D C"b>YbAf[E&#B^BT@$QTBV,B&tJ'Z ^V(H^ x"0f..D#DB ,Ą Zhi-pSoƢx$@#!B [}-/\`BΛZQΪ'_qX")~L|:B0#Fy61$[Í* b' #0Z.tV @I!e C ;#5 x¶ZmvZgr F.G.S#4,UihU1"#͎ ej2.w ޯ0/VOi/sd_9133? `K635n)Ѡ0#W37)z4{nE4@J$ED(,E}:fMAγFĸq q-zh¥Yvst2|- Aze-E''E>3 g"{ChW.-kK&%P(x]ēn+Q2Dn]L8FtoNFdC, vLze $kD|58(BHlA+r,i@ A;PU,QCKM%e8?Kn"x @{\|o P.+<>@ )K+Dgtd6B*Al6~A Dlfg6|ADJAӂ6.)h36nA*e/&`!% At vM'|a- 0 z'!v7dugwj%tPfm{P&=^ a``TZXpEC$"Q 0'w@zCA}N ȁiAfBہ@ 6BCwg~ x$~A@ āHxf¶B>*P&X#Ԁ Ƹ\]6ðy^Z @n )䝟)em_[cDGD4 TC,bgi@'I&St~z+Zx@yl5B8D -Z_݉7).i FHy^n*pA5QC|{Wn DyN#DNAoF`3A!-ߦ2H)E:d:A@}jcCnAꩢrjw@G?z_|B6 ԺE[_].Ry?T̎/W ľDsFj])=RzS[z$$B +7|}IB{HS+' zO7xهcJFV h1A5CzA@,xA uB@@V|Nx@9i 0o\bś=!'?7 WICeRB@j < aHXdbAPL!J$AgO`*]J`TLy2eHqMI| r|`Gң47k7S9;< KD=?MA-1y ȍ=.R= ܄1/ZuWaUYi6$-LbP V] E [#}:Dv2!.ٌP5TOB,MVq-sEL2dBuya+c}=헰M453=,NXn2=w峴_{#)IqsI&2M !$Jw0,Ꮓ^a@`Ɓς|Q'{ KHB=tX \aTPi6! sQMχe4+ jmz* hIXq9PTz N0( Ә8Ǧ4.t$e/ӄ4 G9J0C#LюHQUgI7b5't'KKYΒ igF55l,kLaԈKt39vaWT&4 Mmn\)U^sٔ"Q ` 'nΓib0ꝬL 3N HT6܁ M ,T= "Qn2L>wIN2f5L! /r}`@D )_>AF=HyIӜ,M fЈHDbM" Lʙa ozӇF/ j:hzaZѕΧu+=.UfDlCD؍#|*qt*xd, A;Ѓ=`!wxA |6hU)[6P:RXyGH(8GPj]08a@J\` nћ^ZWk_+ư!Ir=R>luѳ6)Y\>٠z!aFn+6zH$Dv Pqߔ~P`:{A p<@@dB-!f|c†$[=L '#D 0A& .QĘ!0D<fBv9T#+3H#TޅI~SE8GG(p4!)Ё [i01< ϙMlH2D(3jUMI碒_2 ꠇh׽Ɓ[ZIc%Lvvz;lb϶9:Kpӷnqu31v'i{V7)ܝo}2W1iouimӟ!~z7j{[NLN?rk 1Nm|59߾˅<'́tUm0.t/}fV{pU>+>ʸ:G󫏝 :W3yt"wKu?rx7 Ag>x/U+3򕗓 7b77YBa!f1GO|wޘ|} K?}Wӄy^}>?~׼;?ҿ~{4ZXt>飏/0vO!0&/1 /,0AL.B-|k*LaT/mڈ }P 3-orNp p ОJ&0p 0(p0 ub@W & p>0hOp0o.j0!Qʭ#yK=q.MєfFO' a3)WXK0qq'3wQdc{gwz)p˱zgi]Mn >= !& ^ !-&/#F#IRiP -Kr% Y2&_DrQ&qrc\Hno2'rav 1(4Q(u1Qp)q*E%+$~+r\H!(r-i%62v*-r3|/$&r/cEڼ)/ dZ.0Sz48Q-s24&2290I23[q3;4S> 4]s&iV[5ms0JfSk6}h/"89K441O975sp#%s:s(&;{89[z̭&s< :r05m7Q:9pS1>;s3%?rf8ǒ@}3@??M#S,3Bm Pb'1tA[NHNO:?T>[.CO:O2+%-EQ46 -R)e<BTJ]|t?L%3rt+MK GIs4A.0-tFt/)"4PqRRiEMQ#,HiI'5'{K J3 S2TYTe0@c?a{ Ck;tG0$wń餹e$F"[Db=bsb14?@|åzw[#Ia!Cv7a~ȥKhz|I!*<|\0)W4vI-82?b ZX@  C7ɷ \~HމnBTHچik\#>X2H{Q[pE%ܽ(H12N<S1>}0dB\BϹqZң5,\ @v\0ȏ%a/C;z$(rvb>F0\b|$&}q0GsNoA sF$ ǹ{b(!—E`sxn7]Ωs.Cr'}2ow Z]{"`h6( փ0]ɊwY9͜13 lGOB"de <&zA3  4tb$AhADK2t^nUG]Z-,;CХ| u,n9DJRaRz驅SP2CѥPy]NĝuqD %ٱajU$DFAG*y 2aK SvybYĖ'A5CD ( PJgB:8$ ,Ej ]h%U$ KC5I~[ީeEuh~v\rCxbѶI&pIh l:KE*i@*tnSO C`"Z&0uZV&RR IRВ BЫB\y0lœJ&֞PT 0'GlDj]&KQCC=eӌE'*AjmSqb:HQBQmT kv iᙞ EOмJKOvhVEnBb*h(l Vך.K* w %:b1p1Zo =]1)?h]`%!'A,)ItJu' DIMچ{+K\E9Jr3 @[H"hx3f]Co68)wo!L/8D8>Q2OP! X%O,! E7" .  %$(FIUd+K'ȞDgHiv6Ad*#-E+̀'tAH 52 O4UQI(}ze 6 (ԕ8B*& ldH.TPd&lPiM$L+DR" \b-֫t Y΃؝#h=Ǚ}7,$P,w?+cy)[_276R Z"0mX|0 ~tJk|64rKZ'nDm$2/.@b IXD`7]jLJ,u" p17o 53d";'nHYq_LL[MZqPR:ܤ~0Bvo$xxZ@b&h!XBUޛ夀9n`۱"]%;#Ї UfuFwAAnp@svG!C@ s_{ސ]3 -R E-qY_͸JX4?AKh@To>M<} Pw4|PsGY~^%R`3$D5OMX){5@Y綐O?_?}`jgׯzoDc ؄|~F{O7HS%|(Q*%Gŧ~w'g}Q@l @Z#@% ߷B G0C6Mv'/fS8S; 1}a(-%cQ9HpxXe&'Sw|6%vqEfy"P-U~0!{X1G9F$mA PTsV6C[;0/+Uy,:f;`Uc+I){(PS1PyWt SW!JGj.yl|8E# (=ĉpw*B0P RPi ȈzaxKq%8IK HHs'o93$cQg1U{؇P H1F4{51fzbgj!H!eHrx161"`HnWVrX+rA.vr13.uCfdbKqoheAu!$3"#pE3R9wk!E!% #r%Y CWeDa8'Fă±1Pqy']%=VWz6&*yift5eؐ=?pIlmIrJYV˱}n('Ar-q(B>rp ѣ%)+(E0ƌWQI $)[p #R-ߒulQp4',;gጵi2A0 QGcI#y 9QwnUF4.S Zy0voW,;$ M5s3= Qm9F**99'P5d Bv36 %Ցn d!&RsS(Tq"=3wE7ߓ :!!Cƕ7PB!1ß2ms/( K:'wV'*R Vf1j(Fq Yr9HacbIk1V.4QoP86>sD\FMd+"=rktAKi4P4DqI Cl7PFf$ BؒD2 G|<03 Ī$*d+|J QMWjvLSeSPOTk'Z kD"kWa ?H CzK}1/QM4+z"0 **Y07Eh;  T^3ȱ4.0DKMO e~Þ]QY[]S̐UPb^kmo`<b<r{} ȁ,Sǃlȇȉȋȍ \ȏ,ɐ<ɕl{|NjA]Qpɟ ʡ,ʣLʥlʧʩʫʭȞtʵl˷˹˻˽˿˴ 1:lnjɬ̹,N ́,̛\̲٬ κ<Ͳz@|S`,Ll,<Op E E:PG m\}, m"]$=&-(* -,024}0m~z,H\Δ].0SёKԤ|M }Q =ѝEmFTM֋1PM0 Y NJf`gxMz'|VXEpF :1J9׷ pܘ !.<ސ>.mE:ؐMn6 Y 5LP 0? 8ⱚ  ܲ@1 >} 籰Py5)5I 0p`\Ȗnղ]f >N{܊Q YL P X > ܸߊ |X^ ` t.A `nD p mW1όұ@ @|p P 1ɟ =  P5`_(ڮݮAo<WF}SMN'$M|  5 Uƍ@P |P5M`A01J` nA}no}  nD 2yΉ~և2=?<Ưʱ` P &0 zpM _1Pf: t1R`B' $+ j %E5nG!E$YI)Ud(wͤYM9kbtAqP#/.UʁSQt:ժ4`kɪ_Ŏ%[Yc^m[k3rPw 2_&k(fcȑ%oe̻Ngc-dV,*Ufڵװ+gskqսoqk/#FL^uٵo9s180AyկgK߿ˌbhbTpAt Iσ&p &CCpDK "E?alE=qF_ıD)`Qw1dQQ4%IDF'G%lI28rK0Q0$'0(- 6sG,krF7 SO1Q 51PȈ =BAL3ՔK7?Hi/OK5TTSUP3A>UcuVZkmV1U[{W` r5XdUvYfuYhvZjZlv[n[pw\r5\tUw]vu]xw^z^|w_~_x` 6`Vxava#xb+b3xce;cCydK6dSVye[vecyfkfsyg{gzh6hVzivizjjzkk{l6lV€!,        % ( $%*4;= #+*47;?2###&*+*++#&)*.0"'5+04.5925616;49><<<&7EHALS[a!d!"i"DHLT[6D##U,^ߧz qt|u]x'!sm7xAHvu|y>`|7ׁ8t͝W_彗v+ 8$|x݈(} ވ]f%r+/y7:xb]u2ZG}X2i^"j]uЅ"y ahue~JXvSK6g)e"tMvy 1WwƑgݩ9b & &9u](B(BrRܱǒj!߱vw').ākk/hMTޫwnwK{~Rwg+zF #Wj|<2,g1Ļ.zr/ʷNLB<&Y2'ȵ,V14)n⋭58j-쵽23-+w(arJh^^/ql}Czm<,~{0>4.A*$ߎn\)/j*KHqC} f-3q3Dsj]g#T'm+Mxg:ȳ,9~Jؗi'tD6n{vڝ ̕^8TߝFG,jp,3x5qOi$4qn>ԅ n8̚'Є3eo'V(1m#3 =Fp:v1oe" ˆ.<\ G*+h?bFE\+ŸpOű JA~` 2H5|$*$3^&RO.)QiEU}C Ӽ$ A J(B.qY#E B8WL2f:3V4iiК4e6cMsݔQVC#K>z3 mp 4 gƀa ڤ:h4]$(&yM0 }2#MNRT=%&(zp.2mhB{t55ˊ"4%edL+Ӗ x,vSX (NQMBNH\.QїԦ)0ORjn5+S(H1RzBH*ME5+XV ]kWJUi5)e([ZU4m-l[X֕[U%\.V&]56x=-Y]WlF6iRiOwU{N0  MpT`%DQFk]f^nxjTvw[T~u[z+[Mk^+] 0Y]c+` &p{SK`>_+7]bUkeؿ!o _71{{ax8Ivlwq&wBnwq UVm+^jR :u^ -O_ $(8[E3:Ɏ+ңt\MӑnO|I'ƲM_TE01TmTԍ87XyӔ1=\Ǟn5.;Vr5"ؾl-C5cz!,~(DY`ŵS=EŦ H`Snj@ЇC>)$^ۓww K' C$i?{޹p{E|<)~`C Ɩi>6"?A>!;> ;m<ݾb!Ӻ;Zκp  psw(PsS/]&U/|>Q|C'>_ͷ>}_og?8@~8h׏K  U^ ` exdr!t L)p8K``]8p eu' P P ؁S g Li&c lVx pr0 ă>8Kj@ 0 5x ( @ V7K7|w}wlukHm~.X}u~x~twGݧ}~~c 5x|8P0 h[[ us6>\TH|| =eVSKHXig|}|7s|q7'G}88'7HuxȌѸ8Kq ZK H0g@@{ ) H څ9 a&H~TsD[D` Z  V؁ hX| /9Ta`8TdH@h帕lȈscɎfҸ_ jnyjuَ8~g^'8KS7K[5w6w9gqgkXoSH)g7iIkً'i})|d{YLJ|hbk{&ɛɈʈyŚכ II҉Ygm` v8 pa e|PY9giΆ6KP >~P疍v V0䈌` `ʸ&g l࠵y8wwo๛@ʍxٚ?*I} |)A*]$$КI~yV Ƨ๖`:h ٥ʩyoj陭|xTylʜ f !JYsTn%Pw{ 'ɜ'7zh]*gʸ}کF:Lʈz |)骰n:‰ꂰ W@f x|9 M0 ( j+@'` 0g}` y1Wx)gpf  Y` F z&ב8pskvԱ M*ߙعiYH m:7:=תipMJJ:ؙGYe`8pXSS8' i4{%oZ qijFG۸uڸi 9[sv*J;GZڹ;X){u[ȴ۹'w'5yap 'D0g!W'pBx9 Zx)|doo_w//&m 95Z 'Li?k  kݹ̺)]M 58l`kal;5zKizW@p[: J9o fz lʺ[ \y\ -|k;OlH*| VEm=Օ+_ О`lɚ&h9P}̫|D"x'&b;kP c 0 ŋp1-\± Pk`&٤ڢ ۼK&` D ĭč%O߼Ԏ]^ݗ܄ν7V-b= l߭4}ݣ]ݝ 0ݺ͝5 mJڽ˩p ' pR*Ғ{ƙ@A= Nl-O<ƍ Zl<%l\͸|=:LFl<.Q~Nnt\ON\-4[^`VP%ޝO1~G ӥ=Dm,9N;-~xJ {&PШ[mıg$]Jѓ:N]^mJܯ.޶)]ڎ-޻.UmӶԳN(~]߯~ǎyn^~\cnmn . 0Ȯ~~ӭ>F>N^n > /ڽoͮ$-(ko%#F  `4=n>W^َ".(^/n%oXڽWON!qԅ SO>nN^loYi_n_?ꠟs/o6?y/xj2vOfON M ?'> >$0?/^؟ڿ?_oa M(1aB@ DPB >QD-^ĘQF=~RH%KBRJ-]4SL5męSN=-TЄ/5JTRM>UTUiTXV~VXe͞UKmݾW!֜tŻsޕy|o` 0+%_8杜1 Lgҥ=JqiYM5ٱFny;[up's}훠2uХ_KBQ@"T5[uWCIsaX'a["6&-mÔU0|9OpTL12N(~=VUFy2Mh$mW(}eSf6O|F1FYE?(HŁSTS$?L@UNT\CRQ%NSLcVL9SdDI|nc @NYeE:RQdPV!% H`DW!,4`av0RPr2hOYᕊ)AEqHIX1X!* yw O0R #.6`q}Wm^SLsp( jx)U),%B + r z^&8Va0"VcPE:P{ ׊Xw~B*P\tBjDCGu)y&Z "Z { *X"xEa!M J'#`Ͳ4Q hB+pPBWh :‚SdHF $k& deq(g9O~J>+*ѣ)v T[D Q9@RaFXh),@BDL++ MI@ =!r*N} d0qH$a"|H4&X3U@06`"}mx+$ \"}LD 0 i  *0 XE$\΁xS %PB~T H#),LhOq,. 4#('SR"JRD(Ryrc⇏WjdL :SGiNG!%ֺH)ЊH1U"q ( eH ^&-mpXREEM@]bB(NEL A"?|ܺQ0W*ցBJ -^%2],UQWxB -YeqsUrLUdX֫Ȭ5ex[iUM"t )(2 sB0`6bcrGHhQ8_` ^kbku'>ɐBމ G8٬ue d291"8xVPA|OW k\&iH$+$"v-/JO^WV ~ w-WLٷQD+V  D,W1Ep>?k0E jߤtK1 M"7T|&V S*)4>"!VQC\#2}H)49QU~df70O&z%{$I C|;m!%vDjVTGa-r#HlƤ6Ћƃc \18ΐ 7̵woW1x5q;E>r'G9q(r/yOr7G:q0> c"t8t7Ozԥ>uWWzֵuw_{>vgG{վvo{׍w{w|?xsy tTo.y[<3o1z?"G}Uv? /Acǽe{~i2AcɏcSwA s?_8 Կ~C*`_A_$@P?ۿ d@L L@? A $A LApA@ A  BA#AP%d?(3X=AHC;c:-\./æ3K5=X>C惾3=˾룾>>7=#+ `H\K N"4@ @ ?@\Q@Z?RĀ]]At@AԀ A $TFA&F03@31`:BB"lĻBC@pn.T:w xy׋4LC#˽483DKC=l=t4 DBDCCDTDXsDFDI\?JDLDF O@PdIRlRDIT?b XK Id@] JAaFTD|A"B#A%'8{vTBƬx(;˳:s|(3`GxGz˻KG!|c5DC7HޫH~45THC狁A>DCHȎ|DɐA|@PIlIPI4E@VZ@`DA FADBJ!lFDƬ\/P3/BDx/sdF0CBCxCH#P:+BxOFΧ3l)H*4CxC@HG@30T:\:P: NO#O+vle;r=5K;̜ܽC=c>"ˉHQ4ЄL A3SԿPlI|ؤIpE(DBBJ`ԧ1T(T)(Գ8P# }XsT:O}DhԧFTIT/JBk֩8tR C&5Ȅ\ȅ,SCʴ>K̊HqF|DGLXMMLMO@1uF#FV,A tSE8X ,^D @ΣA۴@ ԫ’=@FE}> Ge%>N)\K]:BTdGqB|U) l:8U;A>HGJC`P }PfMKt:-֘Dlu۷ֽ<?pmC'%W#yEL Gq]?TʌyQq|UR*R--S, EMdX\Έ8R :SSѥe|tF?D5DUT1:h='S/UYDBoT:m:uxB(4} UrBP(%$BH-o<[E^]۹LQK=u۽\̄=yeqe`=܆lž>G{Wϔ\ Re\5eR\@m9-]pSԽ? \ J] 4 ܿfJEՁ/`ޗP@U e^oޝUAM֥35dTNXUڮe߮PbEڪ3 ^ w_VE^=p3H&H6I&IdLdJndPKJ&eR6SvUfeVeePJ>ZdXnH~eYNeZNKSa&bcPef{lmNjhpqɞfpnVjvfLEÀzzg}fvffvfouf@@h^hFF^@TFO#b!xZ6&Fc@Qx'VTFO`/x@_ cFXcO-Hx53h[F^<L.KdIeOXFN嶶VW^\vkLdNdSN`&fR.NkdNffFlɣ<8Ʈvgm.h&qg hnw mng|}VmhVg~퀦mFphU }-$iU U@ЩoTOFt\*@`N50%=T3Pf>XDhYCmU:#(=Nd;>dLd.pdKG뻾d&ekeX\l>6`&l>fFsfk>NglʶqqmNgɖlրKgqvqoVihC04䰃V+0spkGZ6NNkQkpVM c8&7qPFfVlglھplnrlGx&!g#gn'G3}u#h0u1^723gfk崾^:w pJeX6k/kqatbtB/teVƣfFfGGiuf FA"@OSo66umFhKW_[:B#=_;`(p Oe ckv\e=?/_v`opoq'wƫ<Ȧqu~wqZlrtgx"Gu./^W/<sgl;f?y5M.lvTpl':t' ޜRȶоyz{P7KmOzfxG٧s{{`6~9k9{>:鐁~ᥫx: պ~;C: (a;WcҺj;?&H=Է<ݫ}{?>3+>[mЫ~쿆>,ۿO>3z<=2\ _#P x ^4$A0`Ai SȮ҄AxaL<*M \B?\ !SUq$|H 2YC, p@ͰMHEA[K"w"v]_ #CE(RJbث1:ȩE"ߨA=WC C3E<9FT$a5򒞔S&ɨV򓦔8IR򔮄T&7yNY*J[rO,'^O%%wYe)4.)M7=GK9m.鉄iFmr@LVV  CY:H-ҳb'@6(Sw$g@*"g3 }piPN"D9̋rF|!sю4MdCGZҕ T4 %55Meb( iӝ(!,+ӡ'Rb'QZjQ3U*TEojT7/Qձ~4T3êR5;2WjUbvhMjҩ]BQn|`ub#z lIʊ C2ȞLjJZ&2+ms*-pCX6Mp(2NU-Xܻ $tw&j;zwlN;хxիX>oK[tbwկq(>W0uoXhERͪC7gk^ַTL') v'[Hk/)b 8&+| 8)^պ"gpv|`&'UUZ'5ɤ]%+᮲Xɸ gC)dd}Ke65,fƎճ`%:pLBwo=Xs.5s͘GONLqŨVnW]j1l,kn1tx=2' D l {$͠ hGuԎ0F}Z{۠MM`XQLoӛt'ݚqwc=v?Fޅoz;f߂dG.hҺp.&@Cs=HR83s8C^$4! Y0M\ ap[< )MN\6}99Qnt=/Ϗ>+=4wԏug}V]/{.vfwz۫S[nuJo^r+(I_<#/S F` BϏ~9/z'Wzϧ^M?ۛ>}]{>}ys>{O߷>}{g۷|_/?{=}?EfX &. 6> FN V^ f`~A} >Zh i Ơ ֠ ޠry`iT\ș` j &a(`^@ 8V```Zn!~! hAa!ơ!ada "!!"":8$:L$%Zas&v'~b!=b$R)^ "+ ,a-B`,`.&"/~"vZ#F$b%^"#(&#q261#yb )N"1c恣7:/[0F)J"%Zb68#>!4iAc1*A6:ڠ9.6CFd# "h=# H *XA* 0Œ#Zi') 3Z4~@R""\A#HJRb$l@tB+z^V%V< ReR1h )j#8GcxH.!!A+ @K2K$~̤dZI2&-dZ#[e6B$YzCUZeVvBW~e@Xv&lNYJ hAib$eef%piKBh`i05a\ 㕀O2A-$AF [y@\el," h jVgWV}k𩧀ER5b*Qƥr#vRB$#@B P*$+ ,q*&  !(&@h$B+JjBFh(bB"B*B5)(  #t((X]g9aUFA!' g (@l%`ct&TfUf&Xœi} Tj\'Xp,*x% U'iA&dT@Xf*&@%zfƁZcG: A^%K&'$ B_#h)_*4 (+! i(,$F(ȴ,@ę#(>A}iir (j2 wf.Ad lin'PB礞{iAh A&)j&t@lBWn%z^%yvB@tœ%iĦ)֘5,l PP! H &@,JK@# h F*AJ"'B~hh B#$&<)khP(h@r,yQ|mR\ժg؁) d)jim'~F́U%T"`% * (U^豁%l^g^^!oZ֊u(*. Xg8Υb.#‚޺(,ʭ֭&B+@K+DAhBd*F#*+yA($B(B)$j)hPgB3]vj$,#*e VAhA'@\B, 0A'j&ljȁjdBZjiZ%tzlq''A&A@ oMhXdG X ,z.}A*F#B$h JX#̘đ} nhJ*"(EFF!D.&(B@TnhX*bFx2$g\.#bf"['/hrlAl%ASndz~|:kj^z|@AVh(-xB͎@&geX4 XZ6t1G2,0((+*Ph䎞*dji!@ }B"@(aiH2), +čB|&T.PiB>t$H.t24Yց6j{^{iҁ P)'t^Bۼ+>' VKy^i~Ɨ~Ԥ&x ^. 1'\-x {i,:탆u*[~|Oĕ @#?23\.|쏇!>yU#abe[NJqwδ@CG@HA`A ,($p! :pC,0aA%DR$ɑ CFJĔ93BL7DXgO 9 &MCN5d0S2UW:m*VЦU X``peجa5lvp `xQNre 0cV@gϟA=tiӧQVukׯaǖ=vm۷q}Do߿xqǑ'WnwgKB9e 5Rǃןg]! B 2v,g9gΞ @ ~* Ā):J( k,$l)2J2 p2 2( 2,P f0-[0 *Ѳ3,3v R!K{#!R%l'tR)\+l΀I,( :h0ZH3:󪓀 N>DʩD'F|Ǵ2/(0)4 B ]|1OӤKPZjҰZDUfö+r/,3mD )#mgVZب8,6-ֳM"^Ṙ0躆􈂒-I4koLSP/(~֣j7R͕Vu)K;K<,Q*q/jl+6P; GWx_3B amMDb;Hvɡ5rکsVkz9nK׽wY݇6#xr#馓ނ!}B~&^8ĭ(R0JBG{\2GjARx5C:t+2tr)f@-f-[Ǻy*w9u퉟=3$h>P`Q ʤH6$뱷zkk8^LͅR:SjS<~zEr%^nU'dp+~8t hRbX_R e{>Ė@m^h &03YDF;DFWN% (ЁDx 3#aCO0D"a!CD3`DD33 hb0P0@ xA vyQH<0CLMG0`zs#!l7$0-`KalDm ix&  ^I%r.M iδRI[@eW `-t H,"=j*, :zdRlUN̕h3;<9#~> V|'( /DpKT,P"jTyATZg RFi!:zP|`QTF88 XJXɓؒ=4_Hy.R@%tXKvHN2!߀)b>O~ ),' F!UeSJ],w4S*K4/! ݮƕ DRcg6CbT546G@H!RY <ͥ @:C3;E#PFC@xZlcPOC0jnG*)qy 4#ԢTNA{VZs` np X  0 : \6[@VRxFWxxq܀| “V#@ \,y5ؓYh (SlLrl$@Ji)C& n`4@h,PiH\)V4PdImEp>,W:,TLB=3&zb!64xD! A,v\t) t( (yzI3p#!TZtiwYu %Beonӥ}b S86  !vwwb mtc,q-aX -hTR8@%O\J)X Lrq&C9~>amf7|-'K<74Ŧ#tJ1Lw8# P`G3qi"g|pz=/|H_)24D?;V}4j4m  Svך;p%̂so84VE{f1)XAo1IwRߥFq?q'_<ț-#_^`J.f՞pnVgӰ0sSTd#;}F4Q-t eDM@B|fӋ @D`b@x< 3Z긞F fگi J0  `4Ȩ.epI8H- R)V LL&N}MM06O*B flP@n,^o'-Dȶ(nlN(F0*N@ɌʪelTT o@  L)M:РYAШh&+fxBC͹!@ `m >Q:ih@ !iDCqcq0w9PpqͬM $ E VPl߶PL/P>p)/@c0.,ro>8l6N/Ahlo@/rOL@@"2@Fa6Qrk CPQ" 2  /+!0(R*!7r.>na K)`N/,β$.H,dmsK(aC@\35)q37)Q*2K6 08q+ú8LH,$P$ v iL.p'\bo/Ah+*Ț,03)2S11,K1=$W"g +?P kʼo!@@pqn'{2O'c55O6uCAjx7_* O+1&l,O 1p#2@rP ܑ;|lIy c/+*3s$Q"G=S# PB 2WL2rشX%%"QU.C POCO5PusDDM{Y@PێQiTRuR)RiPAHP&Q-5TEuTITMI0u<9Ta5VeuViTysTSU]TkW5XuX-U7Uy8^XYYUڂ#APyuYu[[[jZ3Z[[U[5]u]V5Su78е]^5_1USU_6`v`_u^`vaa`a}a)b-bWbm79&cA6dEam-PvdYe]\UWcc_fq6gbekhVzCgvhV5~veviih;5imijٌ OjjkW g9kM lvmVDߵP6Ymn6vWivnooKCRuUvop7q7f#VnWq!7rvli k%ws9wm1USg37m;tMjy\rvtOuawhon7[7vq7wI76*SDVwwx58Qw3xy[Z v cy7{U855nmWsw|YwJ,gt}Wr|v7QWzvW|w XPSww*35q-X{ Ay/A`WC8Uz5}xexkZ9>=ᛞ㧞Ͻ;;I-ɞy=~M[8{P>! *~ [8N ޾!%9ZBW]e^AEߒ7~K?O-$4b5f_ZjIn?Hr?OMcNW= @ ? €? ???ß_ϟ˿",4H`B# cE .1lj R9$D$ Jg\1E/Ic`|ƄҥL0jөTZ5֭\z 6رd˚=6m֤Kc} 7ܹt27޽| 8`!B-/둷5uw?W]^` *( =8Rt^|]Ș]!߈hUTS`.c5aqG>ƏaH}FYdH&Y-sN"Si>#2~ fbU6yRn:J9'Dzg~ hg .pI':"> iޅ6⨗t&Ra*zjjjEx-pʛm4Z !y9'@t +A,ߚ9ɩj oZDD枋nkx)"Wxj֜dD&8P~c;U2Ž7 izB]5!*$ Hb#pur[BWm|1.yEhFO''AP^E==X 7|t֥iAtjО栬2z\sVbW@a2,8T[̕)1{5Vh7ۙsy;oZDW$"6_Վj"E%| Z|PT fug Kr=uZb81UW% 8xK2\YRsN&΅wݾ t8䑫! aBTHE&knQI'!zo0%,! tr a갆l 4gA>!؟, >@yr0JCÿ) (ݸE(L?,~^$\ S@e&{SUB&hݬUdJC)Za&q b>4 *47!8P(ʱ 8*RqQ6>G%Ά26k-P`YB: +-Ѵ`5w.&VpeK):='X\9"eĈU\eh@ od(ʠDB*<"" &XB)8UpkL^)boJ9E(H| p+pWHD@ 6"<r%+`x׉e#X: !HKx <29DJP!/b0> uȋ.a]D4X4zr*Ŝ,YĢOɩމ:Xbp +`?Ÿd9PJ"aF ⥢XJZ- )\SȴmRgR(¥sk)@ȱ)O"\lQq`KX`%'FKn#\B,Qn #&d&hTv\ޅx*(RaCֈ&,`)+RU4"+ Dg2S@\ԧ+Nq]KJIЀP!B!R"zKQ;4b19nY`hڵpT,wwXC 5ܟJK/YR;$ ) ڊ`vp!l⅕x+a˜- j~%̵is+E2mBfSAϚ@rۧ"g׺6z>aaF`mnW@ TR 4k&ݹ6Fטlf9nw B}4A&6q˽d.,:2k4H! HiNpaϙhE^> %骂GR$B,!OiS)HDNRQhː "-(ȸD *A!FS- hlze؊Rzι#9 6Xu,(`.MX$ _!p K J.k wC]y7v)"D$fބ\|e#V Rhy&B((ޮ .e)PUh ms)>QILHз=^)#+ \0XB!''$ X{ x4>A0<<<&7AH@OUZa!Y#!d!"i"DLT\6D##U,gr{maܲ2,k*="b,[K.K΅$ e8/<a(Ϭ{xKω7gy^K^9|F|vj^]2 Fzi9N& u La6YoX@ql#:1h$N 7&>`h2#><8F qpHqiẉ1Q X -*0#8Ed0"dNzr`<(GIRnҔLeàQHD-܀CXv2 $9Lr:L4sAc4L}M3X4>,6gNUSkTf9IjKYg6MzRӝ#\@}SߌgM`A}[|?Vh+"NTA$`4)0 H`#IJRA5̃4]*P|޳'Kmў:s`+PfLh5yL: :Uh3i@{6)SuRTfRӟaEVӡՙUk8R4jWyzMnV*\Q,^םr2 f)/8jUEۃ~ MI 5Z'VO!ef6uɎv\[ͫqaT7O-no\&wqK\2W<ś[*]*xk]wy]疷ݭ/u]W_iqëWaBqϝ.ua50z+bVx•1tKb0a7ݯkc'bmE+R#26%2r@B~Ѓ D9F=aws| 9mNp57ŕf'}v|:ΈV3} H!Q*"hgsW:%L^U̥FkTֿ^u3]lefs {@%:dL=+ CCɮDMzη}{&wyqDR 7oA{ T@CTBjQ\75WNh~̑q\cX@GmV\ome& CJ]={ַ{+s 8#^C'Pވ_奂U="7'ЎR|sKx. z CGT@("L@dو|d$̄@<> 4LS] |=_/Z'o{'~/+>O}F_}7ҟ}'Gp_?>]}_"` TB ~| P >pewW iS y  ~ug a Pg ` p 'g-h BP^B evWg{CiSU  ?xBp P7PЂ)xnwxӧk}G~ڷ{}~}|}'w7s4l'pHWx4'|/yWm(~`7x~ô eP^7UYpyE ~gR>|00.fEQY~x{w_8UQ}X'~wWg؎{踎7H|8hη~}(7@7`(|:8 g0U 'sc lȀ7 YYOkw>W^ 7p G hiva yN0 Yg`e` %r GxG=`7ygsx g')i|hxHxݧg}|3WxcԷ h{|)h{!銍y_c w  H'yX7ȗ8Hufz zSXQHQH|{%7` Pȝ}ǗɍiٞYٝziǗyG቞۩{~% Z緋{u Wg瀟PzP{S% : z?pXx g7pKP% kl6`wqN0Uz%p ~[ؠ7` iuqBpN B@S&d xvj~rclg@ ԙbYۙ٩xi'Ш Zz iYI *w{!99gzY0U)|40ZHK{:U0{ ZF';*~{:)|ʘX'ן9|Iܙڮ'9湞4*Z7@` kdڀeut镘 7  E``hApFSPaP deL[! UtUth `(^owkga +X8 UK \X i鰃YHٯiǬ{幙ߊz(  KpGZ kbt7x0 ˪B J$%|zz։;b {쇽Z[婝kiX {{K֩Y ዺۿ龈K z {#zpeZ?Y %X{(prlG[7lYwIjo{o ( ֩kw۩y7:& JK =lMz&p _ { d[{llD|o|t|۽x yЧ^۠^еgyg%%A@?@% boFƭKʽھ<{竝zʖڪY| n:|K~z۩)Yh @X 9%Pv9vu$iɊUjLjgO 9ˆƍʼnHP {4, vf :,Z_׼|İpH`]יO$& 8s^& &Q (z=lCB-@}o|ʝ<|;+e|\_еOzBmJlJƚeF" ʣfIN{um׉֛u}E˺ԹlCl|ȤL*NMٳٌȸlڦכج-کmM̊}ڎ<ʈP欑 /~ۛ[|^{+K؊mX>]ـK^>P{.ǥ=?.ٔ-˙>>~mיK~ ʹ=խ^^訝N𜚥d] K`gvcF0-Ȧ#$0]=&ԅ}}n,^-"6ٟ:.'&O.A_ז==#LM/-PgCP ,o>>~ Wc"ߝo])OK_u1VE*O?O^./?Ot΅-~S4QB0@980a  >\xb '&(č6X$H$~lhdJ;0KK\ys!̐ IRЌuhPCddӝ3\ fR(F4fPT'pɕVk׶dśW^x/&!Ĉ# 6Wbƍ/&Xdʕ-C|Ys+wZhҥMFZj֭]C~]ėCVu㾽}9slō r͝?][Wֽ^x͟z ^|ǟ_~/@So@TnoAΒ;? ,LM9 C?1D5?6dETEXEWq /Qq{l.G TmG"w|HB(K:' 4Ҵ*J׺Ԙto90S3_$-M*ŴNXLs锏L.ғ\PQcHcJOԾLQ-= NT;PUTSe\} VU+7dU2D礱UL5U7ͯVߌMH\5Yhi-SﮝvFn7,õl᳌oo+^]7Qd٭nqY;/ɔ+H-6L KP`d%aNT0T/n @%Zܐ7wi%V [DÁKAXA(Q"`dneRqW501-bԌDUB1"3z9Qpd!BKm\pCbN(Ad&>C7:ňP)kDXCȉQa @PrVRJ͏.;!B+;;4yެc4t>2h RR#xKpNZ9" 㔄 ]%K[ E__ȏF\WD"*:IE)8 yP+"E"ЃRሰ@@E'JG *|EBA( /s E@"!(!JCjXCBPT 3(PA㥫zC@: Q|Zjc`!8@jM R /h|!'tf @\q8ݠF Pq1AFsBH  `78E(P?0Ju\(@DF zkf\}ppF z1[ΤWBl!D--8?/wTL B =3BX&eBTE8 O:c}vbCx=B@SlS%B+plx/L!R4DskΰU ,A r3h.q&T0bHV"-Gl:SnJۙҖ@y5K\@ VQ *"7X'Wd)Ԙ f MO ϛXll @/"3Ѥ@KDh-LDL Rb) "!NțqT(z^^;Lњ-%&։pә.|>wk>ՔRbL0ORbL |4DUA ӃWq&yr  *JNez[1Fj]!>E9Rm ZX_}(A/~f oǿ~Rʹ* <#4@0AɝɞC\D3jDHS$EJ,FM\EOD_ ţDPũLUWDX\J^tŢŤd\FxƶTjFi|FnGqLǽvv\HǑƊ,L\H$HHɠ$JK,Fb|ElJSŨK$˪ŪLLE[J\tJFJL\Ƶ\KjϺOG|t͑yG(L| GzTL HH| Ll̔L= L|ǑL PMD&(#l\AM`CM+$(%UCDKEJ"mTʪ|(WJ O`$OL F4FTKftf|F3ƻFnr˿GLDPЃ20N UauVN@Q-U.(QEtUW,ҒG\EAXV0_QZ0U|F_-WrUfv͗wuDKW\Mܯ\&MEY|RҬ]|ńRWl0XES FnmqXSvoGuL@Uȗ5CH]݀3̓$L( 2DXQ 2hڮZQXF`dۦf Uc=f]5TQ&Zc9^AeAG;=>A%We5sAH\fN)#]$^R,E̥N$|\l\a5`⸌FnKFbXL L gÜe5FMȅϔHĀ%MvVF(nfn0X>XՃ$@ >C詭ZC`h%DUD.Q[\0`-ѕvnMD`}Dn]gons D YΖ(j^jjTjjhkqj뺞jk/칆fq>k^kq.Ŏ0n6>n%ӦӖr,gm%&ԺԀmm-_mVm5r)vn>deae`>V=0ZhD:v`E0=XQP fEA"`Yd [pBVjfplbcokF.Vk~q6qGovm^>lf'r!7l(rqBƖrlnrm6n*wrvr0FB51W/2_wV6non`fACHAx y\W7D Wa1^/j]fgjvjvykljfz ls/wt7lsqWww_Am̮(mrGxm(/پn7O5xGsy.=$NN>Bggȗ|7tZ /ky꾮q'kifG}_qh'qr}ĶD%վl/yrO'{Xb{ p{ܾ{78nl>uL"LUuɇ/pd/vv $H`Xpa'JhQ  rĈ1# 6l8ʄUlRʐ2gf2I3BV0BPHhiSCVj*֫SF5*ԨӰM5X@ ,m[ЭkWԤ_zڷTB3n1Ȓ'Sl2̚7s3ТG.mzsԪWnزgӮm[wGP+(`` *O`ʕ&;BlD:GN}cwK,0zK._Is?oZذV=VXV`TYeaZlvIW`\e^yuavi!8"%x")""8p1ǀ?:rqww!i'uǜxАMGB奧K0Ԓ{3RdXQ>EPyU~NVx锄 _B~~HXb-*(:(JF#h4$ 4;(/xΊl/`lpd` ;Vv`b 0*b( ȓ<)߾>> ;s@э0jd:`@0|h#@p1|| r7OS[3p *$4 \5CCCEPMLTת0y "-f~*aNسHD `D<02fD穱y b>9RbӘD!cc*F h"#)IR2~^"i$8I寒$(C).^25&#Q|KS(Z%,s]2o4%-kԻ^<(`F5[d)iRS&33hʚ&8֌fl6)u /YNqz'>`SJ'4)ЁtglHE}7P(F3QXJTFC*ґ#hl>P.e_GR6b*ϙF>)Pr i7ԥ2UR:='RԩR%zj6jխr*3mձk0Scn5+Zi̷ҵ[)j׽xͤ^*foD%JK2vb+Ɋ%,e3YdZ~,hCx=-j=ݙ6}-7R-n%Z6v{)i;2x͈R[5t꺼(w+^iy~/zNkz+_xʒ$0x.,`ׄ0,.z ~0S0:8QY sx;,bS\ƨ!V1Z869{,A2[y8NNi'SyQ瑫^7M2~9f똏Z3im7OYvn*|=W5Y3/_(E5_ C,e4>D:,Q754 j*ԮkHGz's]tԶnpu-쐺&ҳNea3[9vU-e:;vAc}jY'{6Y^KŬtӝm^aNaynuӛ]mt׻%m~略e ghMMn(m@P<8UTCH>[W.s^లNuI.y'3}G_8Wr.ΥNs$Wxtn1A{vȎnI_}o[o_Gx _?|u]Fמݿ{QO퇰54s?R>p闿W_I;_qۿQoY]jRe_^ h8afq} ^Н fu`^ VAX JXn _.Nh&a25: FW f! NZ  ramu!v}_͑aw!b`E  R` "ja #$Fb #b"˩!'Z'.bJ =!"!+$b%%bujy.!("&0(2>&3^a.F#е0bM#,.` 9_[.F9*!;q*0c!:"ݣ#@FZ7A$)$5CDd55V8 ,rG21,$Q$Jk*vK2k|Ae$>ndNBޠKjK~)TRR2PRY%VVn2XHzecJ5u%u%4U <&#ꙁ2ť\%]֥]{]T_%`&k|aabA|`(aNd&dZdf&e^frfdzfjfgvfhnh~fg"fk&lkfe&ifmfdvoe&jfi<&mgn'k.gjptjNgmZsRuF'wJ&gwR'oO &{{ f_|'}'\~''((&.(6>(FN(V^(&?֝ h['({`{^Aƨ(֨(樎(n(u()&.i( (V^)fn鋬Mi]6iaݥ:)%@gr))h^kQ$*ĩN*Vj:>**n>"0.A $檮鮚hgʪ +*6>+j(HVપ&,ALꯎ+ع K+0"A櫾+iZb*(A"@纲\6l\ Ħ+Vbj A+ɖɞ+.l +"A!<&l,l6B*XB A+ kA'Ќ`zN*RR[~~+n$dȁl&0$DjAp-ʪ5.mjb"0~,‡ A-֜m+إji@ *BMԊ@,b"0 -h- o-:nڭFjkl+@nӦ!0.5.]!)xAL_r.:*PAi("* ̮ndgn~o@+02oP,ڪ0A  ޯ"B*Jz'n B*)|Ц4)BojTA'JB.))"(kB*t@ B+!)A( "B*͖᪂(/kp2 A h0΁A&H@~غ+pB&P /n A&dBܩ'A&\&\4pDB($P2NB#sBXA\B&\8r%PA\%*rpAjk$A~ f9A( k!`Bm t!@ * A*d,Ҟ)k )x B !@.G)=ABMʽ}.+ZM쌗f=AӋ@l\KrLxʼ'7pA('hA +pa~RB^@P~Z&L8xbほb=`jm \6@!F$J T(DC!F8BbE)^h1F;~,!T84jIȆV2(J*Rj{Jef*RaZ)%tJeh/ (U)C8:[U *TH1(K$ ;"qc?~$0SqgϟA=tiӧQv3)+!K%>@<:ȀMbt(8/3!1ѬH>ChFFxr `7K1Hn$LA UQ1SQ0D( ]MO:AW-$!=O _ XH4*Ce-Mk5m 4a W\bW ;w\qwWyݒ{4uT`8vMXnamX w_9X +>c/DI.8{'&7`eqyaEF矁aW!*p5.y]k6!A;vHj.NL3b!ʺ}.rZXm(m5K ᅲ%S2 /Bq/H|Wq!|r/q1/']3G-q7q,/ _]W}/hy]ɑ||_ȅ|z籗=y_>|7лwя{'d,A?4\KGBiCbl %6>j;`@GkH`jjk gX"kcB4yp`\(q'LpG0ZHH!Pj4E&!>qGDϦ@(iY E0b苉*#2mtэs#xG=yHAo  >t# HHN%1IMn'A)H [ PJ@\V)YB@(\`.eKYF/WK^3ͤ-]X1K_:&,!pR49pu@ 0p d̀|?ᩁ g?+ztg?zl(E+ь" 2 O.EG?Z|th6p sEF+Җ" OJE5QT.U$e֘JUҴ,k)ej|WZN`eYÚլ2Фf-cL Ds7n}UpVmN)ЀJ1O}ӱ$hdwZxVIq:0iQXv3FkX^ Hm T2eoˈ5q1ԧzɔR]4V.s[5:sXf2VҲlU ՗%^NX9`3g? }ꓞmlD3'd/Ї>(G/P, 8ihO:O̶t30g4iF[:;֧)pJ rC ud%/LUrUVuTvxK ]WTe%{讲@5޺n{J޷o: +󞘅* *ul҈(Yё=)js˺Ĝ5^l[ʖ}pMP2v 4E1K'զ5iSS^8Y][4PH6!mu!?@ 0*I0t ] =m/J%T;X7}M,TfW:񲒕nus5io9U߰|,<,'pty/t@i[# 'yGQ rx—.Ek|ĕk=:-= }C CD"1AxP!P! A侍P(2 Y ď` =D6ۍ X>?gc(F$MC@R*M˗R Ȍҭ I |I-jM4o l4hP*ZJ8ϟjZ0J880<0LJ.p XnZOO|.HhN@> xLH  dnʐ `Pg :QN@ >to@}.(1a  !/TqY>N[XĭPK $`ʬ̒i뾭KrIkFmN0ةImn|~Pѡ ⩶*Jdj00 Xͥ4`*NN<`-=@ > Ұ aAȐ> ب r`oN@av(R.1&@&p"sR*r/_!`ii t~Qt-u1.--.y r/R. `` 1 0 @  3 2 3C HSP35555-h`P51 '6ss51ZS82fR9ӵJ9 Rs: ]l6]L6S8; g @P㐍6n *y${Lܨ A |'!N =a@e=QB71S@4t*DMD(+cє̎ɬR-_F+S-M.r12G2// P1^ HTI1#I06S 3JO3봮47S56i5eyP8@ u7 t5gP:E9S9]9I@ P L4384<:!I03)r?H't?o$#$#%<p%!'AyrSuU?HrSA s(PYUXͻRT2F[ÍGH|/E/U]2IsI-%ss22K72#S4+JS`5JLQ38t6Ual VQ45GL;Y5e4A-9=K]L:P Q!5?Rxx-M1` `V}ksהM~ ~  {RaBvo-o͔,T,gTbT[IX-T1.qrF4L8.aX/UrOXIUsst3tKu3NSw{7Lecyiw MmW m`}Sa5+:SxST6: QO6ez]VRx6]pfߨXVUxHэS*9KlZWi7]RHy[r4^UsYtJFǒr5t2s8J+/9J1ttdURM4ava]7bkWbew[5s9cy5SKIO9i:_,ʘ:5RYV6wy;Y<_v([z7TؑYXkZ rI؅t[Ts\Yr2q͞yHr7J-u29xWyP4bxwyN%v7kW 7CQQ+T _QyUQ!5yW!)AZ#y 9/D+] Ԓ݌4,YJuQHmQ_ؒ_ғ^Er_MkCTRm;c81ucNwwswaY_E3?OEy:XeIke{ =RoJ([y[c'yq+sT059y.zqr,=ׇZtKLUuډb%%-`ug`ĒL%ڴR;PSuuiSdz;A{黾MIM۴ﲆ˲9uWqջ4quy{22QYMkPV`3MEZE|#+uo3~?5]S[: `T9Լ˸PURN!ս!4s,<ɕɚکĔ i$I%ivꨐWʭ||||ټKH<ϗJ=} =}} h%})W&-}9=}0]bIMԙBм]a=)bb|e=u}wfl?4ׅ}؉]Cpؕ}ٙ E"~}+٩ڭ}ɏ]Zڹ۽]ɽ=|Ճٽh}}p >K)=l-~91^5E~mI>U~MQ~a>].eqi_>}}Y荞хm虾<遽~޾^ڗ~A'>1^Mپ0:"ڿ΋,B/|~ Ԟ ?U!? %1ߑ?9-i=?E@MSQ#i_Kqo?yU_+Yw_i~ϿWt #b *… :| +Z1ƍ;z2ȑ$K<2ʕ,[| 3̙4kڼ3Ƀ<{B DD=4ҥL:} 5ԩ{Z-4WCz 6رd˚=6mիlUܹtڽ7޽ٶ} 8@ >8f0丌+[9z@yCK>:%`Z4`ʬk۾;y`;Blһ?<0/7 K{ܻ{| ?|~}kۻj%Y`\ #8} RBoNHa~7ܰ Za]Hb&XA~!5"2Hc|T}CdBٔ=2#N> e*xJ.PRne]X^ ]%W1zfjy,& b ifv:\s9xJh݁dbI> iFO &Pn)gmwߢK6izG# kETq^k3}'/k+j)fflN;Ҥ,Vʓ JmNފ+ߞ+l ox\׶ /F.;nroid>? 2mGq3Yގ^"^V:&ϖ@r.Wh pΆr:ۦgB)szAtӊI_V_c v؈QytZjտ:N-\lM7Sn{,0_w^rwÄhI?A mBy: y梏~rmʍ:Rܵ,N{&}VAYP%:ʋ8[nٟ)yWۏ-2O⇾h:?E η8Fe |'5pW{##Qp;@1n,Ů&l6f.$73F06a8&f>,"LA Flb6 q8Lt3=Wb4^,c,ρtU4_5]0e|w펍KŹ9,iE35{m4#ò r}|%!Yh!%?IOi)dmQvl%Œ(N{31˓%̌6JY,52lRrљnˡ49t}\xnG1ԥ)|fπ!:9ԏ=ЄJ<PEt}Z̢3L$eAJtOt1.U;9ӜҴ Nji#Q#Tp#8 4|_BUZժZBUխzU^BY V5WUk[V~]+\*׵us^׿ծ{+_ŊXi-+-^kخuUgYR=,a1&vMiZֲ~ 13v- ~ (qs Jwԭujww ^ -yϋWW w}za|\ x.*m~ KxpI_/? x]0lcx,nwa:f7Xox<}#D.d 1&js{a|0/x\L$R$y)^2|/qg&$! H0!` `.DkWsH#b7yӜ1*'K}.5~! n_ k7@#sl"PZժ_ i2%>K >ЃzE $ Żnm!ZA~`;(%n5ǻƖ\ATAܨ>wN?wD`t݅xQY FB!0!\?A yWe9OT2B+zEZ.-]N<.tFXP`@=xӐ ` "XxáouC)<&Kft E0A={n; D nP'H?ϐ z'\t{JP%~m8!@`x]W" nv릁k]@Qe/@p6pgq?hw pgP sg0e mЕvy#Yg'Z0p{5 y mmqqw\#J9d1Rg?wNfzB&T~i Npc FЉgsBP'@PP7`p'p% ^psg@ FP@q  N@U FpP# H7 0Ƒ_D]C`nFB7p ycZ0 u0"]q b{" 'v'X'mFvR0 >])\#@ #x0wqp:8uPz9 ZXvu "xIbP8F~{7ґ#r07 `,Y4 8 {$k a%Pxas_P H`(7 8p#0 ձ0 HuU_{@B'Ibx )vx PȚJ 'Ȍm%  ]=pqֵ? hGe70(`%qy\`9v7C6(&@}wZ<~nW$p%0 a ksx0I Ps8~0wJ Ўf>7$` p ` .5f'"Grx!rmfp @n.y xFF0 X@&`8s8 H0k Cp 7P_ BIPBwRUf[Ga *]Fu==@}& b{`Zrp \Yš"ٚت \'{ `9 NˮEsp\rzl7`q'Dt 8i{T7ɜG[ 0e8fP e/)B msXT @ h 7WVcK/  _P5[5> S9]tY8`{ P5`w]*` !`#]9UaT({)Gq|d9q~*\: F<tB1{%˔`,8@ te;]S 'Z$!@˳\ `ͻ ¶B!}\x=FU%Na-(2n1N+:>_<@&%E rHK^!*#SNUnWY[]_a.cNengikmoq.sNunwy{}.Nn臎艮.Nn闎陮.Nn꧎ꩮ.Nn뷎빮.Nnǎɮ.Nn׎ٮ.Nn!,       % ( $"*3<= ".*4;?2$%%&*++++#&)*.0"'5+04.4924516;49>;;;&7DHLR%A%!["#m#DHKRY6D,sӡ縞wyԘ8ש\:ϣ3g.>~٫[8qͧyI7 _`w1x7wq烀7߄{ǃU]ءz ]Y8xb`}!F!W^{8*HdVc|X:%B_sݕ $&nHy46^ #izƵ(M%x<韑ziewzJf;ʹ enٜ-uh^yFkz 8"ghl^j+ YzJwir]: Åx"~-z 緆i^vK{^^zܷ 㷢kh᲋J~ܦ6_k0^ \K1<ƛ1&죴n::|bCp [ e! B8_ȩ5"+,-ky=+eqGE QͲ^M0|m6= $v*W' oȊ5s\ʫ-3^~eO_䅢oD\t+QktȔnH<4@H2>lJnFA΀+ Q.lmI gH.|T gh?vX 0F< Y08w)bӟưv0H,ըPDcE]g=l2y7`h<4Fqb A2qLbD2[|$'I4RdYKxLpL$!BHe~ґ_'gCY򖸜! Y2ܥπIb6Җ1?L6\&~Qb@DnQz+iP8Ǡ0 2f)f[.YNA*g@y~Ѡ'3i(4Kь%Vυ~uCIA hC?QoIt bIgτ(STPD+v VXMEgHh@$Pu+uPZդŏ@1ծ ER0HJVaC5HC'BA|iKS)]bןY.ly,t5 kQcۛu%hHv'M_GNwc,oK{܏^Vpo~'z }\fPS*tf.7{ GN8.a׎ l? =+4i!(7>JA>ENNRxPԧN[_HCJРni#jVbFwp#PtZi=zJ+uK@< Aч@@GOka ~Aڈ".4ΈqKP? 'BZ@ :x4 ;Ͽ>Mv-k_5|~co}^W~7'|h}7~We}&pP |p\ gy0' @\4 [m0 Jl}]' [ P-X bPQX WO ` GGJP {` P >a= G@"y^dGsn! bxT4 qHG pǀ}{HzbW7Xׁh~Xi (~x~5]{G~ȊWixxĸz4 b`' U 7v\@ϧv7~>`J\DKa4 us ݘxw~yiH і Ȁhw'' }s- i U 㘃WxA 㨍WOP[ 8ĵ'l{40rPѧu5PQ 5ppB ``u IV ^`PB Ж08WPz ?p0ȑ$b(+iYonȚ/Ǩ1 vx b A4 I84`8WGm{·;%? }} ן{Hg8wX韨$0 |ĸ '  נ#ן[]sJ@[0p\uy}%l UwG]` 4VPIu%pE[{uJz\z`Z$ 8b ڒY @7{u>{J >suH{ %0w9^J qX W5 y.J}h{(ڡ-~ 9-:i$XZ4ZJJ:ͨy8Z ~(j~]ܺ{Sٝ'Z;0U?mWqi% } էZJ$YZ }%ݸ Y* jǰZZ{~ ʡȱ6|zxhRJ4@P`mS8݇ktnwGJ'y yw\i@gy;}Os> 84@@j m\b Ty\ ASfgv=ű4W+\txĶp$}5 BPJp { #Z}+걨jy8y: jǺ{-۵-{k ; !:.):ݺNֈv2kiЎ$Z'wA@:#@0Z z`  Ɗ8 * ̢z˿ | !Lp yfOp {D[O*.'lT $`ϚtGw9gbj ݧf*gwm;ݧ@44 mZ KsZIlåsMשG$( )ɟ|'ܫȀ\ɖLg$ &uǿXR'v \$>; wqo&l|+̫%J˝̬Z}"}8zKμ͘ɫZ8ʪ$̊ -az @ZEJOJrIPMJI[` 8WsnVK ylY $LVv $= m@I ymasf\F[~0 a$s& $w%t֖IZIG ɺL[W| 0` ի' lXF^ں{ ۭݡKެFp=֏,ڌ<|cЍ}'Zaݼ}HdݢMooV >c.b8`GXM-!ؕ uЊ瓾K^xW,jt~L=UlNDʩm[}N͢ͺŽm^ xiP}٦H~|4]׷kp-순ܟ>zm ;믾Mw.͜~|^i۶~YY?G~ܾN>ڨG^{NEf S,{"PD}y>B>v.R]nM'&pMN(R/~Y_^",g>O>PmjG/_`ߦt]Hmisr@խJN A䀏Ym`O?ꛏR/D/k a?FpՖkw RDS_!Pٕ$NO=?Q>?dbAD 4(B "\!Å ZLQ@$:LȐx J7|s˜2,XP+ tD͌0eҤĖ3.5TÜ"/hV&npHG"-{5+BlݾiVm*.ɜCyTȶQj)XoFXS+ʕ)s+Ν=Zc=8"BUlڵmƝ[nڦy\8Tq7\rș?]tcGbHTTm9+sx㫟G^z_|~~?$@-@dA ? GsP=+0 y0C0DG$ O7Ko:d1FgFmFQ? E|H D2I%u%z2J@̱*2K-<&O2ڌ\K1D63{[#L36qrB MA=O $P5:% DtTL8uAiI*/CO<-R39V{5JPmSUoվXs]u^U hvC(A J!8?XaUT~4hA VQ!QfeQ ۶K}^JJTI.“ΏF"$h`*shВfz.NΏidz*%'V<RBi{=Z8Ok!HpR|X]Dj||$5WO='G`HV "4FAw>LA焆Luݺ0B @(H?D$RђMŏX; S4Ѐ 0 @>T8 I(L XE~P, 7S-64 *PA<oz@*FSh*J~8DH P7q &0jS"AՑav4o6C yM (!:qƖ1JAZh md*օT`*)Bq3?,bN+A@b $(NVK. !I[`#jPO#GQB [ :@ $BŠ 'Y] 4D \E&` Hz*5qhT!T$?S8 9} mBPT¤P؉HBnP5rJNM!@Tcvb(ڶ Va?O $ f:R dQ#!R!uD" Ct7?'Q4 2P z2Q >*Lz Sq_E-A!DȕD*=bRԦA|PddML)t%[\?95i*ĈXCA cq4=)8siPoy  !:Q`I!*[P- X?A4$aAXbH \e> x ӟ]K>Ƥ)\OP." ]$ PAaZ.. ~` a(P%A#:1P2\ 1]% 5s{OAȜP!$hCWuS/GDщɩBC +x:&OPU~L1lXmHRG"Ґu¢T.Ѷe"遃z cU,1ܿ:yP8L/T|զJϞqra>}ߣ'뿣ȿ0T@+@->\ ܍L@ AU?4DKAS|AaA`؃AQ"4Ba>P%d&t'()*+,-. .01$243D4T5d6t78c-XBC=D C@ B4@DTD>dD0G\DIT FC?TNL@DJD@hTlUTETE P[P]^^Āa$ F@dd|ctFgĀfi| kFFnffDHs tp pwtG b 0F nF}F HƂ HTHntHB9ȉȊȋȌȍ˜:D;FR,EH0A5E5C#ɤ<C =I@thFtмPشYC hMQDDd eQJDQWDۤZѦ|`b{딁eKNtOodR 0%̂-DHA &C%L=P@H=e P|O(WCO 50@=ZZ%%0@ܪڬڮU¯,W}%5WqDeD F PTxTT чXNlPT DVMڔŎ=U MUJEu[p\%ƻ#Rd,YK<ֹ^kѯ4m/MOklYOkV4=ȂAHv qS2@/_0COĤ:%'ԃ- ^%&\L 6=CȂ0\%.`C'``6`(5#Fb9܍8T؁eXC]H]дPJ=ņD>>T֬DIDWʠ$S]U JT<Ⱦ'e>a]p'#VAHB(gJgTp pT  7Vho 'q p7 'q'qr!q7r$'&t'V(*wANK@bUduVd,-7-r,+*x9@(s-?-r321r)>t+7nlf(6W\~6L$e `($,UfaiFWNAQW^R]aj\t_ o~Bݹ.hvBqPvnwq n&ToEg0wps7q /{w Or#?r%'q%|w 0OaM_ /xr>7O˷w4:>?ys3's7s,x>'7,H[Z&%LFnC`[p[[jzDHBS=X>XVv[yC(oQu[DzE;]g[B `gvC%v ?'wqqx'u_wzvzw||{7wq~qx /xgr-o}aEs'4UV._}2}'? OK(9y?y@s*rs?r8 tc"*nɉܝ#Mm]wsMof4r$\WryX\t~;;xDkdtNH9pƚ'!4a0 a-Fz03 '7g|ӯe3??{C긿=|4bD%lf1" 2xȞFL Bu_#Z( ~aiB(wR\?ex!>,#x @6ԡ {bC6 n BuFpD>a[.Xnp_ (11[c ſ/|0D"68bkk!"HG .hX0ԕlG8nsL!IHXXXMr!F 0 ,uO|BQJ%,s]V\-OeIᲗ<&2_DT&4)ibͼf,,jr&ydS,':өu1(8-Sΰ'> qS2H=)Ё4g}(D9y~r(F3jƳhFC*ґ(HIҕC!B36)N%˘S5)P*IPԥj(DO#RRVLQ9իr^ NMmf=PqnUZiYʷҵ,(j׽xM^** ,ao멢)b+4}&e3]V,hC[ﳢ=-j7j٘6} W¶=;Մ*涾H8ƽpF2JR7SfZW.w+^C]wh/zKK}/|ݎ7_ⷿ~K^! j/#l_;jpu)\cp,e-I0_c<];2qi,LǷ16S!2Y#J~b<,'C}Lyl-=2üW/Kb>W[|U,s3{R;6}h8~>4PC>A v,gDSZ 2-'+-j> OR҆5-?՝&4_]15 cPkF6~6c}]k@0l6i;/v-m׺7 +_ ;N7ٹm`[qnvoXڻ-pn;$`50 3\ɦwMb7,8uns7;.\>Syݽq|n9 B =S>c? ϓ.yg9߹Q1-wyauJ^;]>tZzktWQ1˯zt{0G?|0~{WЧ]v>nt'ڥ-]?K3m="o>=uze||{@ane!e !\Ia uZ%}a6b_]a !睡a*F aE] \"!Z#` ,FVZ1"''R)Mb#b ""u"+nc`,,΢!"izբ"1`&")."A.".Jc/^f65.>.Fc8_3ޡ5#6c:j87 8c1&Z=.== %d9V#B _ ̙_=dDN#aFF!_H#bII$A;~#D$ua00$y`!Fd/ZG$PnPbQfQ8n$I%K:%=FƜ0#Uzclh%NreWNlA pY[*\%ՙY\aA"b*Va (A(/m&gvg~&hh&ii~hj&kLlkjʦmfmnfoJmpp kJpr"gmj"'qqoFgoʦujNv&lBkuFonmrflVgtrʧz2gq's6'{|}gsz'o'm'kgxg~Vg}g6'(~tR~fzmj:CjA(1&ii(ƨ(h(I(hh)|"i*)2i>)FFiR)Bivzi)h2.隖i©iA~H h*桊fЩ>*jiN*V^*fn*v()i*"jHƪ*֪*]:U @ b% Gn梶h&0:+F) k@ªv~+nh깒)~SVޞ6k+z+"v[lJA&콪rA>,FzRijjbcL,A˾\ *+g"Al--N^:,̬^-bmclB,զ+Ҫkڮ-۶m򪵽+clΚ@T-x,ʬ.L,ɦl ⪜N^Ƃ*4b ll n斩@#(#@̦'|Ҁ(p Ҩ i(Ɋ. @ `iH&TA8Aȁ$h/bn>FL" @ ..F| Ԁ>cg@,(8ZAJ )) i:+"//PkΡ )LTi%od*>&` _*B-B! @o+ !.6; bT6 \!o#,b'v 6A)3C+5lF5JZBA@p("pK ;A&`XMA$hA Ѐρw(A%hlX@"$D"WWA IhOOQI78x#[#\Kmm lOw("*1 4BA'DiK!1@xb"bt0&),t;<*t%2!6 B(Bdto)9 (*)$0Ok )v,xwJB@ lJ&no&qw0|"t/VZAA;%کKx䰂(:2&lB-j[ZԖǎhh,n+)juhvt ъ7ۆhpt&l&@ϰL[%`BzhTI 7`rPB{0&htoUˁh( 䴵NָLuFq A8j°8 n[1޿p@[@@"|`A&U#.>xq!$"ND!M4D wBN0%P7E43Ϝ QE8PXe<03p M C N`70>P ÒFJQK,'|jA&2b1} yC-K A?4!t# -ERI'$'8IE=JhRT% *#aT( D#T*CLẏKP)2FP1E,ζ@+$UDaK1TӒpnpL 6ILUUy8}VY߫G 8:`8` hk8@\͙jA'#H{IoV&T2.l9/'( Lb= z;HpzC]kX^7_S^{m}_/:QE}DT@.׻6Mp'fielAB"g ret B}$ qC,~þpmDԉ?%$Xb2r3)&ODp̔($$35Ŗ&A)U5X&)+6wRirc 8Ŝؤ1kJp!CN)'ڄ4 BoZIP.`CPNhEыnhB3BMzҏJQRΔ5MqS=OT>2h3,pSF luZW:UcXn`fU׫NR ]+T@@5aX.c!f`Ȁ0YZ6'plc5k d6,i1 Z du-F [ $@lc -$kZʺֵ`+h3;iJ]nw^ըGEFԸbm]ձu(X=pWN@\^RSeY!5׿ X Yv', Zִ67azũ%.qa[6Ďx+6}6Ѝm/ӢxíP:^)OU򕱜e׼>LjBoW: N߮UsJ`5oX!WN0b1 l*d)kJڴl?[ӂӘ}k@َf[&1q֖J.c-Pd&ONLS rEaAv].wY$z<=~Wmk^ WW֯=aXHf-diҍjW\cjӭvǿ!Ȍ5f  ɖ `\X 8R."Xyw7l<}@tӦ6Hlk[oWɭUw%ʙ(T˚u 8MꁻN0T#|;!+ֶّwiI3رi\7xowY0c;jbt&AzHBl+*`E$@إhγ $ :'|+G]Rs۽޾ڳngfiNulv =o-Lw Җ7u5yҪj}bfrN b djŜ(j` A r.aja*Ho\p6@m -`M !`N0L@€@<0pO N2pC0FТ0pp VHЫV۲ ꬎ.L˫M*8ꬎp ln0D ̱TҔlB/Nv̷x +^ eok*(KɆK  ` @* : JAQo1%J G ɀ ^P  !`S 00 q1ؼQ +j 0$Er$K(X'C\@ ĎªQ&ʪ0QL4>Q * .0`VQ@< |LFZ^+s/V L 6`,O-Yd㎌*L2-NA:đV` j52 @l 7jaLans Wn2ΐ !S2=ТI99sPV:W ;PT T;;S<<̽ԳZ`T:s=A>u5<ՓVau @UA (,0 0C9tCuG5Kn^@CCc[]\5\OqD_`Ge+HuZHIR4DDUQ4I-K/K' P4ƱM 14[ton #-uv3qz qϡҥʤd׮Pb% Sh=5T:q;Si:>>3W3'i@%@X t?ŖA޶t Z/Z6*[7toc[DoUEUt\_4]E2]-Fr1(w^HH-TIn6Dn/+EotG{@`|"@:3/6O`S`N4 L t x!r6v J36 lPu3fgw4{]JhjHss@4 R+* \N ~7$F>6vj_jU=Ku6WհuA@B+Nn#K[U[3agFaKq!U3W4L7#wq9u;5|Ç YIMQĽ~?ೆ>%^}1>/١\5A+>%!>M|Q]U#>}с~>9ם_Cg>ꃮ5bmeRBGўʘӾ>־~ާ>ަ>_> 7j ?8~:~!~'#1^9A^IQ^Yua]^iCq1^y]3?E]yHſ?iџص?_׿?ѽ(?? HՅ :|!A+Z1ƍ;z2ȑ$K<2ʕ,[| 3̙4kڼ3Γ{L1Јw=4ҥL:} 5ԩTf,(`VE UQd˚=6ڵlۺ}{>}p6/сp 8 (b@v}pɔ+[9,.bn>:լ[SJbѤKv;ݼ{iPl@k;̛;_{P@qVƏ~>˛t4Dݻ@ۿo/Br` .hzQ^{ ^anȓA-vQXwb*2 ]DPHc6޸[tXA͈cBI`-&>BdN> eO'=-|Rne^tu)&X>jfa>mމgRRٟf)zJh)Yq*> )y✋.hnʩnэXJ^ije:*ꈗ2*hJ'e Z|袥lgNKKG*w[m~[@Vmˮ@@ùgiouQZVB{\@ْ+$ LqŨt1 2TVr*o٢s+LƢ\s>xmsF7lBIN?\=;1V_-}fX vj $yravnMvJuvBC$vxNe-]/x#Xzx3%u;}yOl0yOu&鮿.SlЭÎ{(!*{¯| +}z/}з&[򍶽iS{I~3Iao[> n@R- ']prFwa Atk`pR{4&`<.]“poݒ.-o\u^.bh@T("0\1 "mt־N),b A( |@86؜nn`gX,nߟ[&(W6o LH u5 r XP7.H@.>S]?~!$z))<"6Lz $)mCH _Tnse~}m̕[fFؼphMmCG0.|pURz7|{>A|AP{uRG!!~GP 6g|\mE7P57gUPp` p!f\y yNw+7s50yrFLxԅ w7Ut\" / y% <\4 g opw'x4P sPe V͕U@vt,rg2hh%c  p[` ~ÉՓ 0TGW{ '`4 ( @$đi` $00 ip `ht['8dΕm|,z& }gU T <` XP\,WqlGw\y; pwu`}]snp~% ykxy`}%'!` %)oxuXgtV||7V_n >@ 4 wP>5 H$p%  SI~P 5{5 H>A@i  B Hz$R` O@Z J i i@>@s{nM$+kI#0uY]tGU\t@v\oѥ-r3sGx !@p\,@< /M-W028s! Igqo:[nP Q)^|05 KP>1 vz${]`t)@{ aBi0 ~C@Ixi` ~@С$Й=z} `YRva6$嚘&kɚdoI M x p`!pg 9 bw0Ѓr` _h]yf 6r8 qxN  }׏(qoWp:@FRph(> @@O $pJR v (ZЫ |yi@] 4pH@!`X7>1eudDelk`p6" m] `\< F` _g0Wx'xɵwכW޷ ; *]pɥlx`b \ؗ}'qưbʟk7i;DU$PG@}m0H~c`ijOT(JpJ V<:3O` Jz HQ-j{Qxeԯel9y0XٴJUЏEq\p\W:k؁5{JeX F4Px4 A\h@y8Gy0fw53sv 賝<`jȻ\E`Vp|GVk[ y{ P:^ Z WO7~$ x;<P )  }{DXЉ+³ո%o+oLkkڋdUpq@X΅!rHhj5P y xR f rK5ѻ\SX\50' ʅ;`MĜR MƽNր:Iyrò9Q]} x$ 7e򵼖PM{Ȋ$  "Q pX(cKcȟJP ʈcBuxp7N7:b\Wg\u~͢jUL`ZMh*] c9M)1Umen- K^p;e *i{vIq=׃M؄t}؉օ ّ]GvQ2= ` QصL&ؒmuV.}ſ^]w=x[Q6\=^-^l­iΥkMR-wat Uݣ}_M\-fGFF׃gs޽y# F ]` K]ӥlKZMEXp5Z{ZK5 EWp .lUETW!SY (. NT+Jb;3.G=Hw:Ł; u8nZ-CM/Y!*579PE4hQ>6[VT.v v 1_C[eNнPyMAzh[Mz1z䰅eٜ]UESv?bnG[6Nc5ZT*Zwϣ[鉖^h'1l̍_`N\_a.'.Nn׎ٮ.Nn.Nn/Oo /Oo!/#O%o')+-/1/3O5o79;=?A/COEoGIKMOQ/SOUoWY[]_a/cOeO!,         " ,0$#*4== #-*0 647=? .6"&7%%%+++%)+ 8 (-1+04.4934516;49><<<BHLS\a$H(!d!"i"%r%'x'CMT]6D##U,uйG_={G]q xt]rMGt .w^taWivɕGYu޹ t虇xETtbqu(W4Ƨx`H"hߏ8dtw#{JޗW:b5"FGD!hZxw͙&};#(yyƥޕvbqI%%8_~.bqbsD9! 8HI&^@9&aWr zG)gv+y)%% _؝pbUJcف(iy>!-Gbʷf!"Q*r^Zi)—qKF-2˰;qˠ03L##JqsKjhky*pYf,qW |M ݭCw4M]󊛵{uq0s -t/ 5f+ppG#G {WqkOv֍0/,-'0u<2N8cn>o+Ej|q>HrK$rU<冇K{;{מ-;n&w{Og^3)g5M <}=z_LF u^#`5m.r$]FGY5OrAP/hkhy3v >Z5%Nk),eiR CC0y?⍄=\F]FA 7>pAL¨1Z!ѤD#%0,l vF4"|AB= $6⫐[CF&A̚# BHZң aO=rxܤ(!y~_MЄZ6 JP.uYU%Qd 2DbЙ|f&IuI}Ħ 'Mqh+L3hRސ8! kXCҀz hH5Qn󑋴#HLF,@υ A/JdFzȁ3e7Ѐjԏ"I:irp35L^\GyІU)P9ԝRtM@xE(Mt XV '< ,ԚGI5R4BZͺP:)iB/ZWN|5QMҕgi_KRԱ5,b;Wʕ5+^ 62ū?9Y *dZ]kOAZVVgg[t%lo[{RV> +ܨbd(mK9밠F*U&$A߻v#^t6Iuk_N{>a-6F7^T_PiG]$ A4)>HyPuqx]S+k= kȁ%ȁ^)*8k(8/Hx<5h@0"H'肝;2QDF8V P j( d GM7m4u϶Ho` @ hSs` Q=bz2H0 d le M o ] HM ЈO=(kS mЈ@w | =   @]N0S:UXPXW>8莴vE(+z-Ƅ86.(W]iXPrxf {bh s؏_\ >e w]Ghz_UrJp452\PEu #ȓhih(E"xꈅXxs;T*ȓhR8Tx5(+Дa ]騕Jit Uo@ :E kHL||w錌P=@PuIU]U70x^s tm{ő ЖM` [  =@U v I`p&H2A;8XnLB ƞ֔EnHI)69X^<=H)0Z>9#(RHkGX^s/P 0Dٞ zx)(:/7ZJڍP,TJHS:Nx/ZIRcYAH9hI iɤKJkTH^ڥ9^t: ɆlHwm+`vꙗf = + lP~po0 |XIy6 WpL}G|M |=pu l׬0M9ؤxɐ JjKڥmzQTiVZ*9$ؤUjz)(oUl:KȤ^*Ti* =:2U5qZZ]4*,+C: vڮJrWi5*6[<>XXz8HzRȣ>KW(kIɥORۮ਑`, OEUHPOW = @P:|С@련Z x{ȇ9*H_Dpt zֺTLk8> Ql%f w_@, g `h D+sGOxpNɴA;{ Z;ʵY˰ һ<۾\j aGj g!\# \6犢HPOH*T:1 ~"UTj_KJZ:0#˴ 7˯<,37j/< ijĹHPxZHn(uH`+0t@gGIHz>e+0~ ++0 ~77Pu,@X:U fKBLHAò>Xja ˫|?ZK>BZ 98d0̥(+쭹$VށE>Lr澰DlϼϏk8 ʴܫ' T[;Lõ;',={]g= =0 |h= Yuc t)D@+YjF" pͣ Dtwզ\nHXT$AnX Yy]ʆ PI`<mϽ z!=< Ѝt mݦˌG̦Z2ڧ JkĬLlP8 mZc5 H|ܽ PqLҖiM)\m =`l¨m]"=ܓ &-ݿ,mќӽҝލ} Miݚ֔Œ]x8P]+)) O; P_ +7] oM ~Ј`=@ ] uIZ @hȷ} ٝLl + 6@ dp p ![N=ˍmL-=I+lތN)`X}ND|,@iWq!q&WF & >L^sP.=ح郾L!MM뗎޽߄^Ӆo --6ݧ ɽL꭬݋d ~|b~Okپ ދ^ށ>~N 3/-|]Љg1T9(=c$40lٽ.-^> {φqOouOwOso܋ZI _h=ms?Ѓ0 o~-߇?O/r?JOW?_]ɿ0_ɟ֏匟혯?g BPB *4hP *(VĞ;+RY )P/\zݿ_|?{~0@$@D@21VJa X0C 7C?1DG$DO$BBEEgc1GwDpE1H!$H#DG$G'2J)J+>'̲K/ RI0$L A1dӽ5Ք6p QΚO?]s@iPBMF>WD49R-=SIӔEUMG%uIQ[SSKeS 3qUVgROYLa[ipWR! y%ؑ|dA\qYa)ZgZlvn]Vmy2c%5uCLUx 7;wy7?e3䗾;SE.VXd`dN/8VbU1r׈4td3y39nh>&R1  dИd@4217@A2u1KTOilYnIN HLpď8z5|/ W+Ȕ7@A^qTĿFLŌ&( 2 D$DYK ]OtLjڛ$?ArSX*&-`mZcL>A*_ S6_p@Q!1vIwX2 sCTR&įWJe7PY%\@_D&ް9@EcS$ VQ TnA߁bZ O|#T zSC]Fl `U"I Ub=0 ,UX1Ip*-QN!D(> ZpfJ##K}4"Vc!AhdPEu8D0U`1OVq4QJIXz $g=H)Ap>#HJalXALD" ,ٜI xC` jc Qho;(Sm| _%ԈUr $*MVHg% rr +0+=M> ش*5W1BwdȄHE*ȁBB`[>9Cs"IJSIqcCX2tWMG((AoD!@3C"F)(|S,1**lǭ> 5F4  `]#V0JSspDEA aK"`ZB: MiqA&Q%$! dd[% V5$pWL!,b B$vp}: ur7.# ]\AͮĪfT"oq+[f&9n ҈ET-"98S=Y X +pU,cIqƗ$bГ oHMBRǧF xܞLį iO`*bT Y8#geVXmk ŗQ VI( 6 SnfC96 e' r_y^2حF|)`k<>>@CX@g -pY*Mz s-3=jeGV0 Ef4'GyVcG$OM:)VFLHi׳H;2Z+V ɹwj WB`0ҽ ‹OTwP%*H!@ B &$iS*\q C%8+NW?= &L'`|Y\P -ۂ .9fbHdE?#t *6֒zذ"wb__ J(L~PjI$c~c5}RU2$ Z0\`>>&ٗOB_Ra>U_8O>cHSW|o׿$@ DTdD@ 9@ @q=(DTA>p!$"4#D$T%d&t'()*++.B!01$243D4T5d%$ Ki6A0|A:CCC< D,D<;TEdFtGGB KMO R$E@ETUMlV\ E[EEOD`F`LbbDFT Pfpf|ij k oF 0G(s udG!w؁xsG|z4rLGs䁀Hǂ4ytG!Pu\Hq qǃGѼL ԊI4P\6h<1BXExA1(lF8CCCP\{S?8eRH\5XC8E%8amhA?PoZU$RtlenAx%y^%eҁ%؂B'(ĥN*PQ7SN0-1 ]Ō-ӲTSMXLTD*hSiSmkS GtU=T}K ME5ȿTɤL5lQPLm%@DTzUMל|mee-g}W6h!1-muA1MA?(hEU 6l,\P@=]EQ]W\]CPUۜX$5uA LX+Ũ8ثRE\DYJdSx7ʒU;uƖ˘ xO|G,z4EmЅDw̙=ߊTOmQDPSuUUQEGLհ}M?U6+!uABgM[4\Blm5PV[ !C0wmCP[?W}hkU]a~]]U%fbDXQX۽@^3,-E$YXXݨ]D1>EƳ^dlO;mY= Ȁ Gq܀?5LAϿ A|A} Ȉ,P OPZ}TTEGK΁[X+`]U-\uA}AwhHx"~AB(ce\HVEoBX([Z^e`]VBŝAhf+(&q.'(b*\KΉRX21^ƭ\|^Rt `Ypk8(Nl6l(vk`&mNlpkmVlpm.0&pؖmצA!TF⾀Vfnp~nfnUn^nn8noTT 8o @oo0>ovnVHOhC(F% BA$hZ\1 7] +hH(ng~@nm1!F%f=,5Rv'OCsNJFf,ls&^>F>ʞnl9@Vms>vmܖ6mB7pm0IwAt6ntOtu~nOETWofooouQoYLuNp|B?PE(hִ]FfmA\l?h]@X@d%@ef7VbXPsX"Cbc\Hxc75z*,Ghn2xFmm:7NsN6sӎ5mE>wlAtAڦmvE_tl7D P7OWY/zOV~WoZ?oNu.?{OjDeq j${_Ą-KFhf&m1G|236퐷Ɏs_lplsswtvyݎE' zDdL/6NzYuM?uTԢwozY{Z/u_Oe\ J=BZp3i|?/|1>m62y4ml̞7l (X@`@'Jh@cD\2c*i&: ɳ'МnBF>eCOHZtTDEB}[D &eG(ޚL{1y;5yӞ[YUXi5hO^Mh }ծP#]|0 +0ã J [[n 1D^qzk$$PF!\Ww@8y(.D)K1ĮwT~P鷟jտ!ki0m6q' ͚l @#$ S!ɝ -Q)08K>tz7欎WߍQqH#K! p~(ш" vK%"!p~4) /N탉8{=?㘹y_/$48w!%VK`,A#A*X@CE4eP p?@pR.|a^gD }S:5/; jb!pq!4u `pEW!ňH$ 1f<#g$@q.ʠC0@CN "He_2HGiR^C)Q\i(11p/40 -?nX<L/L.eq|&4)i"n4*WJh= v*H$ax / -#8A|Ƥ$TA[Nt ˆE&D#*щR2DU6S1*ﮠP0lDHG:]ϓd="=Fx[F(XOk %@xh1Ί2N}3/Jjc Tխr*j#^=+ZӪ֦U4bU յҵv Z÷x+`+Xꕯ`2m5l_R eYͨ,hC+z$-lc+bƴUlZbJ%Tmmo9*! Q0bpq5Q1ylhz-m,ibTEdzt0"-1)]bN_G N~{de 2ذT\,1Y1]$X5Oe~]йtMez=/~(PASY9E0юLg PRCiz4 ^BZqYTֶ@r]:9Pfac0@82j;צ2-BISIttqW6!h %<#P5̟ڥTĮu}]'-iJkMd;/.^gsrѦ괧mm݃&pk7)Sm\ݘ^wxԢu!߮Zt/D,meJ~ϴSƙJ [͵Hxi6pH͆ xxT `2uzPFjd~춗<#Ӡ /3 l`w~?P0 `@p1>c`??/?1H~ Āֿ~<@__ PPPZPIx~^Tv   @ @Ҡ T xaŭ=6)\Na@ ,mޞe!  _A5ހQ_i_ ހ _@ 2ٟ͙[ɀQ%]@H*N Va  ` R Ơ. ڰ! 5H --:a=#nfa퍡7.L!!@#y-b!_#;~a&nb R (cy@^*z` v`Ƣ b "I." v0 fEdU"" @ d՟s@=ꟹQ̀n&RLdD&RTQ\d)`b`avg6aJ@ d-/ g.6U& ^f1z0KHN#z+NcP6aAf`l&mޖm!nb"Iր;Ngr_qX#t%?(#F!y\_qBCJdx`H@l@F)&` "g Z`}Ң}" JgL"iLZE2jE2$$hha\jH8URfniY,tz (5d1 '<^g%٣X.ge)Xe@_%6]HDeR_)zVD)+iƧ `-f) `bR`i`*n&E!4&!1+bj!`XH8Q&j6jCơ @jnߍ$%g!"Ÿ6bţҜe'(]*'mL@bLB`vd{F+bd}*--FJ" !fB`\EM3dz2,^~+ϲHи+nrghNvek+şs6'A&_dAa PbU@]j"&@'>$*in$R`{jN:b)}b/:f"V&KJ+3&Pafvc6.h,-aae"b-AbrjbQnNdâ)z |䭺"| :i|b ^U16flg$B+b!X1[Q9~fΗyoEzRhoFFr/n(|Io/|7S ?0W(Epo0su }*i0 O k 0 0ܴL 0Wg 0 WU171XX;1WWAwF1q1^1p1qa߱qd111g2 rk1!'2j"? 3rl2$WK9%oc2Q2'zr(r2"2+g*r+2SF@L,˲/i! .o&2ϲiB!  r2_F! 4C35W/c8iD97p23;i@6{:=i@s4s:p=3@CY9W0=r@/i ps433CW3kpJ4 47oD{J˴N@3:oINq9t46%4Q/5{ 6tR4SOvC4PPS5W_MMYNw5YVi6Ag:wtYuha9BX55^Ǚi5VtPu^b9SsI#T b'WuTbG`}57׵XݵdgS>[6Uaf{0a#6djFgghlGi3TR϶n?QCkgSlpMoRprKqX/te_k5`Ov_^smtox{K iqx7{GNH @zv{7s37v u}w^viz?g7'8F sccWts7 Do۔ s3u8FyK8n'87 ﷇ49JywsXyG974|۸gP9_9k9+9~x91i<8!xy9,948˹Hm5]﹠s^;]/:`9w*:O:89CJ:/zq;SeCy:6Yz*zh:7:iZ/{s5G{c?yG;m:c3S{S_5/;[?׶[w':{8_;ùo\7s{#ws{y;O{KSS6cTk|C|geg<'xo/yqoO|˻|wgp9üΣ9F{ <wSC=?O}ا513 <ܣ|3W н w0_u!3p p+n2S>{[c~k>'~x/ܰgE5 ƫ.>lH ^iUK/?7??G[T_x`?Ac?g?w?@xCƋ1v9aCF0b1f"ƎPBCl2ʆ^NFTXrF4U.HRa͖Kڔ8MCEpEF:jURkW_;lYgѦUm[oƕ;nw͛.߾{ 'Vqà |Brῄ#ʼnˏrhӧQVuYƖ;֮)⦻wo߿o[8XŽGծ]G>zuz)_׾{wa?vXѧW}{8E}׿ o*ss >|+(B 'lL(>A QI,$PE+6d9d9Ì y } RH6ԋ AjQI88'R)Kq )/P %@B!LS5ٔ"ڒK(K%(S *?+ >/L<(6!TҴB8:E@ˆ@I-SPZT+M\LG[1ĕJ9;UTL%hHQTγmTUW-ˬ`}"-dm"j۲zdR֘d&\7@1WC6y1S`WR/]t'%كM# #JH!">4hE6}'xD %%b$|Vf \Xb]JbǦUW- AT9z-.( 3i%b[80ʼ9M 4yX)CZ컾x,>FOCjZ -"Oi\ Ejz#wcSy-X VB" V ŽP$"@"$z`Y}Oʘ(~~.:+Mϛ50N>©:Xg'අ]O GYAV@b(ޥ$HbVP+`L T4AsMC)G8+X)V*uM+QSb)V1 -(X# 6(P?HI k!;$>! # /TXR@18: c=rN!/_C4Q 7qx KT_4 ; %R dd,Or6><.Af *#hjⲊ>A.4.>"3|Ĩ"oHI Ϫ, *$X|5/d*we[z)M$zcBڠ] (8+Dl-ލ+bMHHk`~:/H #P@:gm c~'z=f@CH@NJJ`qqy" P $ XA W|Q&/d9$o~'Ls)J<ʂ$8x1<QVPqߴ1bclӄpJN:ajM!Q61 A2$He>L1?.2%Y;tqUB Ԣ>R$mrj=%y'Y% #or(( }2)J#'B*W*(_pRr*)+`KB +{Dd-*AԲ-4-%QC,RArA- 20cRE "1s1#B 11!s2@2)"2)&0s3#!4! 433#HS"N4"4SS#Zs5%3&\3M1037uTb#9,8S$/U@-EqcD-.-ئ3#_M*-7E)<<3=s=ٳ==3>s>>>3?s???4@t@ @ @4AtA-ij@=`=`BB/?1<-C7tCD%C4A+B/EK=E4==_4DӳEEiT>otEutGyG}G4H@A"+&II) JtJ-JJ+K4J4\L_KĔM)IMtNNN#`OO'4&@ P5`uQ 5R%:(R;< 8S=S55S/SI5:&UT9HU?Ua5V/`VmV'uRvWgU$UT1V=U9UsV3@Y)5S3TYT;Z%tH5[u[[[HIb hNTJJ4OMŴMLKTMԵ]ە$@NM`OVP'@aaa $6@5ZYUUS#R3S-R/5S/`dUTYvTS5VelViW%Y7huvg3@dsU;h: eioTRK5eSZ<sΓJ[Փll6m3\5*uI_T`vLT^MT^n_הoMJpto5KO UPVP!QUQ/sM6j1c5UeWd'ujadKfIufcVwfVjsuYWWsus_5isVkfVvVUSSZͳA V? H>@ wEGlz!F@mɷ||m6\i_]ʹqJLo_nNpN4J qI5P+P1s1v.RuTR-"Ut?6UAxKuER?SAhY^xm5guRkWks;zWieijs%xK5y;<` `?@zͳBiT`T=s<ø=WA`@ }ٸlշmvIpL6o`uL)ؕNvp8Lց'I u&Pbw6X6S?`UYVj=ikeeU_xVeQxYfov6wA9i?X{7jՖvV9yv;jWa   `<Š k <H`B!sks`vkYk[`Ԁz9a{9=߸M4m5nJה~5Kôo) K]L4p 7~ pO:L$ a'yaa+Q3b@R[XAt=T18tIW7dsu9U<5V=io5vMy5c9h/ WUfڅo5RyXM8M5yڳy-T<< @aBA k ! $ӱU` yA%{A< f [=3=U@{m+וVեa:]!YLN Mݴ~tJyZJ_zYTvPI)1(0WvRA6IUzUMAXZKTQuMT:b5~gYgCxV}V}cu0Vv ;QȕXg` w@9y{y.`s[t ˀ9s@!aZ )=St  41͔7?ϔ_!WY}a:!^mC_37vLTԛ4؃cZ' ڥ]`EP sX8F1 t`wA6]u@$^w`w {1%=:=%uQ>ɽT1 VX V;F<7XF B<|z!Y!@@8@<e<r!>St;-77KO=[]'@S=]U} حߛN ?)@էA ۿzgY5 e? u5@K~]:_55o9umw +%^Ú7?]MIW_ESUT χǗC{{>S{9shDhC%G`A#$<$84d6bdڼ3Ν<{ 4СD=4R+:} 5XTZ5V+R4Bb+ ¬XlۺZ@{}"nݾ8{7qb x8rw)u6GVԫ5huk栁n7ν۷n9?nܶo><7Y_~Av;w ><#H`{|!?  @ )tЈB,QH!$!I%H"*42@aP W    PEH%8##tE9$ T(!F R^~ fbIff)TjUn \gcnegvMןa5XdYVc/bI~mYf>[FPj^`e@qժ[t7ki=w^sjq]w] ^x'ޯꮗ_rP#d\!,9$ "ta,G#%T8@j(҈ pmĎ=ѽ`#Xh1~@#λ2H @BN;C4$1ItFtJfP uԱ<%PZx^mqW\^_J^ia/P@AcA`gud_+f9 ZgZPm AaprŹއ\)klsޮbW9\õ܅mx%-y.xn 0N0I ![?}O_֯ u[Q simq'݂B^Sقۑ5cuc*{wߠ8f)^ 6Iq w.tg:g|x]Gv-ׁ+=jl#.]#~|'!x79ĄJQ/(;$!*qLlr=ioWZWo|]S˥ %MA}cFF%ƌ_d2s4YƂҕ.l 7!- KbrD'pі ֩yͫ*ʰ~\>|Ndi"/ EiT,Ԝ", |[\, 8el]x22oK#na;:&pi W@$Pr ѝds,a1aFm͍M#NmFm2Bc@N> `~WdraY2Z]BPh)wUB0UcҼu|PRj{H <CۓDK$kYd']fЊv-+~# 1]$KNhk[_AfG4o wEmTT= 5@A"E #XBjwь4F,pE H8cA?,BI{)_=C  +x#((Ȼ? )/2Ab^ je+2D? HxY#2FB@@,@C]ba2NTky\}9⩔MMqF<7mpNn\5&^X ƄhT@ٝ5d ͓Dˌn i({cvSWV$7^n54J#uD^(!z|+נIR` {Ć)]X fÎH$`$96H-#!cg$V$8ِeDn o;pZvyMx@* V@81 +! XĈ]!JTĢ\ q%Bv(uO|V.}XhK`%6"A^N Kr$^~p2"F\ 8M~q+׿0+vy==ن}loIr+Jyq{Uț}/OEGK~}g<Ղk~Dx AOԃ~<[~g]Oz~͎~/녾MKÿwSk/mw/HoI/* ?(Cc*'|Ȁ %Q%v~~h(^xH%}!Z#h+ȂÇ-(3z/R1H9gƄ;Aw=XL?(G`G+xIOrxwLtWxl2F_\Sahg]cZVmi(]؄oHuhZj\sh{ȇW}(hKx(5HzrȆ8=[XH4hbd‰PhHf;ƊH&zhXoȋ芾l(r͸ʘhЈH؆8w+hyh(zhxcT(A(G:R'fh;X׏ɐ%Ux )y)')E#&y=,!39~(I9)MP1Y:)5)d@NsCWPPI&?O'%'ɔ[I'VBV"(e閦' SYk9AyzR1VvɏxyqٗٓewMIz|)`%)]ZY阙yzRA_9 ɃQ )ɚyg) |mٚu陲i}iryǛiƗVrÙ~y)l RɜЉ)Ą QrPxᩞ!cnɝ)))bɝeI Z\g}" JV"ZWҩulj|~ z/z~M^}i"ʅ ;J\-Y) ɣE[-9@YHF 壔ݙ$*Qy%ɢ7[*γ6>cHc^:q:R|gZi*y*W z 87jMiLX E*Lgy}=ЩxzZꩠ'Iʦ = Xf *cҥY:4:()ΊI*Z$*AQъ(:*ɪ*>Ѧ: 7鯳m ٨dɝzjʯ{ڱ8j:p Kr1+ '8ˮ!kJf EiNC˴?;ֹU{\JB۰D+ 蹵i{d۲fh Jʳ"{Fkm{o۷jz_k[2˧KJkG+ZQa sYʷ JLZZ붭뺆+l+K+;સ;K۴ۙNjڥ ֊ eZPAz ˽reۼg;,˨+s9kR!Zi{*,@c`=&,7;FJ۳"̣8k#L=cEG=]P\B|>LcPMYF\ZLed,eAl`q|DS,vLiѽͫ 3*ȗՆP؉hMPD@ō`'p }`F ,ܳ\ Y`^٠ {E`׭+` m0 ]Nq l=o )Խaף݋^VJ@H`nMaYp˙߸<\=@ }2a*nEmD ݉9 hpfݎPMq 40#нIÓ 0 Mp{\ʌ}E m>PNt`4-@A^^ ̻}nG:-˨JGG I-R؎[`40N\_PAPAkr &^暜 z~Q1NДo`6xڏ-PI$z` nNt ` Y)@|@OG` np G}wp9] G= nP'y p,`ʖ-}RIPVP>=b d J0T p ގ Iyqn Ў PM +0+POQ`O  N p Iሠ N 0q`j MCk̽RE@4#)pa@ Fu(9.^)`((\ Y̟m˕)P}wP4~u0| }(>v{LFt}P DM'#'.dC%NXň'qJ(IRJ$DhhK1eΤYM9uL TH0#D/J DB-$* Az &+ 1J&x|͊*4iմ*5=`5]Q͊$̬J cVW߸zCzgСW]XUf>QRH$P!Q,0'^q(T"H' MNP٣pa:4 Ew)N"0ݹX:2D%0{x:r>ܘ.#P8!P䋂$HO,Ϻ:C%(J,)?XXbaHMFh4Fq\!68*dU(8:*V@#$XaPYlN1 X|B@17PHR$H*씎 $JTFiGӱlPFO6H#T6h+ۚ)VP!8BuTRC Î.>A*:N@0$B:,̂OBkDR8B Kw^z"'F"TS&E3:Z "XibQڄUVX_*jUR+cXB(7ƨKUV鬦7@PV0Iǯ y4GY!?:*; BP`j)mH$h( $VAX^ dXmCK5dp ;#;3">67P[*"J'9zdڣߩ}p9zHfH La *y%CtK#fX(0`h\ a /}&m}Uq]Lqm|c ƹ+%eYC&ocXKfr=#(.r|e,[HeG=2le4Ykvxf8Y1;;;&7CHAKT\!e!#j#DKT]6D,>|rxA_|Ig h ]qF!ҹwa{ ygtpb) AQx_ }xq`}77򘣌 wX/ha ZnD\nYXakXx]I܈Ǖu$#>*rl"< '>hb+b砠 ƙ |5{e=p-yv,XmI~l7#t[fiCeޕzݮ%j^z.F"(޳*]`J!<Í˪~ %.򺡙V!߸jo̱!^{B;qzQz( yaū;n520219f⛞70'LsרS{=S\ ^rV5♝5w@[o)"M" &m* (! 2LzWKPp}1^5yf\T<:+Y0ty^۵>}j+뾽0&q{Oro|êGҕCd rp'ӕNN˓|xO{DBĝJl[YRiJqJ昀9_2 T-߲(]a 'km"Cq,ޥqAHEM1?To8,x2ٰH"D2n:;#ՈU l1W18YD F 1LIEJv pt$"3Djrt4`8I!JLe hDpP#B%vI!T e)bS]ļO2Ld6|2m0M*V3ȼ&6UmzC#H V`0P7Q"KsKl`Cن1Ӟbg>Miӑl}#P.X?9ͅR3BtMRSgu& xT E9or&BC:1tTCYQ̥DWp(щAz# G/ҋHjU+-h7ZҊjnU'KzSfTA*Wvk53? WvY)^*ش,m`֬k[ѻfӰ]V{WdhG٪Y:65+W,`M؆&t*SWaNx\A#4>BQP\ HJ^xuZT׼}(z~]=.z^>Tp+K8/~`7qU `=}RZarN0aQam\Q&86x],a𞶼-y a K761[lbR%vecy\NXEޱ{+HqOU'NPoy[Re'B7?|4gmP}"PyΈN4SeEѐ~,%GzM4?Ȑft?K4S}b2+0QSl?QTxm!=i?7>_6x]4-SO{Ԥ޵oOXV6=}m 'ՋEIYfvO] Lw"PE|)]iP]TOĻx[ϸ5K)¯k`IԾ65#" +R5p6q;9t O1aL⧐hzoo=i~W( % >žh?rp!aS'7u)ia[ Ƹ=Tܼ6pv; . CTGޯ:$~E$&GUo;r XG& g}ǽ.9XO>|Lp]>6=#_ |_|#?>|×~/|ۋާ uϟ} W~|GM(~z PuU_ `kGre wD  tkS \{ky5@ 1 5Px C`}G~g3 `0 5@XgPgHxQV @ w| X~F{}'|XH||w~瀘 8(H|8h}u~}x}|u|^ F}e |w'XW[L 8zW }guZzMEpt`K zTS0@5sx[(5؍Էu~gh{xxxi؀h~Gg}ux\bsF؎zȉ Ȋ&tM fPps6p e`sQrl5w5@p9k6 `5P]EP9){p> b@tBT.M0@TET f D`djYgY5@`5@9ɇ 8~}շ|x}7̷h8ŧl\IȘy\xkǗ'9~sF( [@ XHz`YU4]$@0.'X?h)y~x|IYI zɝY׀򙝓y9uY) \8)~ٞW w<) ``PwZs,$Gek&PPM =@w lPryP~65`rbXr@khZV Du|@4tQ` UZ~w|~Ǣr G fP|ϧ~旐ڞڙzʟ9Xg jzpʧɝ)꩟}jʊu` Y8 eP9Yכf Gw{8]Bؠ{ iYgڠ8*WϷ :u|J|ZZWH@s04œ0'gvW &] J0` `4QrS @Ws`yTP+[A M0 5pRxewXWs% Jk@xr`Pz{߉ڟiY{*jx:ij막J~c`Кǔb˩W %z[G G$#* r0ɸj~O{ʻk*ƩkIYۼZڋ9g xsx \ NK `yʢH Au1ji}p ߥ HW .ykiyΩ\F sM?n}l>oz}m]H >k~Az<` }@\H vțNm7Mh@}O>Ɲ=Gn餾Z> #/ ,؛>CnOגM lҪH^X{GzdWg쭪+-c>]u^L-Q~>\UM.gܾ^P^65 N>\ ?n,jOn )_5o91>.H ? J3oIZY ?69<g?%"$ p!! p pQ/M/0(4,DROI +7?7Odm㏯_ilUQy^H>xMO_9~?N9_j@.)oU?_,OŸ g6۞(a,~g<mnQB@ DPB >QD-qQF="QH%MJ%("j)E^™SN=}dH@-ETRM>UTUĚU]~VXe͞UZXhݾW\E)ڥ ^}`…5Xb-?q+7ƜfΝMBh/c>=7uեNHخ^nϼme 7#q͝3y>];5n4^Z x~h|gk.}i[+@dA9".zB"L.P=$'*p [Q $b(byܑHkƭ&aȳ4CL!*.uTRH8"J8&01$9I LbC% FBD-P,벧$yWa W\yLB",1+%" @e)WZaCj(9iR"0YPHHTV 0lӨ@"!FbC#P׬EۃU!I1!܂L1#@ج!݂j4$P)"Hh VFUM O Z !?Iel"ރuXEZBi6炋JBiQp:Z)-STa*.STIS6>1WL9%@eDݹPTŦoj$zU(H2DSh OT9lQ$T`U"RPaTnF)R( RR9P)v Az9qUV92mb$Lfc @^VD8=LfxkPih ,C4#V;SԦ.TYB@Y@]IjpEfjHEOmJ:U( UTF@ R9JH(E&D@Vlp H@`<"v E pB >"!' iaXJU &~8a WXf-cV!f(APx+Ⱥ(+R)C) 1,W"p+LjC ^1 3@džP M>Q?a ɊH ZWb((AHĨPL+D&?dvW hBA)026@p8Sv9QDFT.ir ]>_PI̘E"4R h&+R8DH>qVA#SPE4AXL1D +ITB6ٯ.I$V I @BAE)9N0[ G8"&B) Ut30 &̴ &V C<!h%E4-Oli Puկ͙'N*#4v ^fh09 ;EWQLZ}8[#pvl)ׁ̆8$&1&U PDVQ@CՈX %0Dz`:s ERD w"+!w=@@toqL RqZ! w*1poYEV X}4dQ5h @"` `J:XQ?ږ%Elw5l@\9U"6(lPPH4':35 =-^`^ 03Qzp$\kf6g&i$[XWLSt{FO7K"$W!Dp8P'zVЫ Pw3EFQv(Ŝ?OlY^((,ij^N%x<6O t"6D*\qB0*L@EqX| &h_;UB+sᵤ˛Dɽ-C1EKɐ!͑( +57_pa +*K?|AC|JHZ3s+yl)!|ȅ>t]B")nt7AAwzԥ>uUzֵ\\;>vx9{^G"P9WrvH :w|?xx` |5yw}E?zҗG}g R0{ڧ`)}a*~_{?= x(-H/}@~_^,p0)?ԯ~,  X4 Ā@@?Ct @ t@˿A t 0Ԁ @ \[@T@ S#D$T%d&t'Bc=+==#>B>3[=>h>CK>k> ?+?>7{[xBAK@Ԁ4HL@Al@$ HLDLDk>;#?# pB$DB EE DKORA}tE"tKLXH$E|HȿT\ķI 020/AXDLI;@`ɴ$4CF3RFfFlJsá8=1¥콤B5ʧJ{>4C?tC[? h?z?{$ $ JT$ĸLH{?TdAX A|EE\Ɋɽc>8& cԻBx<8/@ PLYMtF=: JJqg,3M >|7F[ >TǮ cGBGlPLO(4@DDF˂KC;TC6Q7Jd>LGwçJ"]X{L㿷?N@}E4H,uRlO S1̀LdS7@TkLPEB  U8P#3`X8F@IB(PxY,Y/H03xcHؐZ;V ZF ڿD'LUT}L$YZxBH=pmSQ>؄QsmN[װyɍ[tȁS$]XKcFO!+@ H]A%B8/xFxUL2HZ.HZKٽ;١eG=-*ޔLK%B(T&PGT[ -PB`_.6xB07cb.EfF;NenUCٔ~fFNt[|JCPn VaWfxJvuaG*}D Ȁ,EdWᄼew5]}@/XEXTuvPG`ayxx0 f66G&ni`hi飾ii6j&阆ꖞ꫞iNjj.<ųFEQV/YoT j p;#;q.r6qeEWqGrl=qqqGw&I}>MwqjwywN#'q~H̀w"ogG/w#3bՎ}0 ! ( $L!!d(bH Ȓ!q}' :r5Ƈ#Dr02DONDD}V!g"Pv0@vO 񅪉jF^I({,ɺhsި#'#V g`_qePii)Hwdy nF{*//.1˜.:$DF"lQb~H!_dd0!1D}lkH!K@c$l93=;pM#lX0&Zt#1/LgD}AamKѻIM y>6m]BL}FL$HQkGB"; n%  %5JO Hª@"i~+۩:gMt}w+4$ȖO޵F.něqb]ќKf{Έ ^}FC :';}Ъg??8q|?(-۟th| e@ : 3 mCO.&$A!.|! 'Bd:Y sFa I*~=<"čDnbO"!JN)7Qm^"58C(Ć@vV_(9}c$d|JO`/<$"vG2J1 D4.dT-3!MrʟXF!O_ ͓|%,WH( P !b9̩%2L6O"p5Ghrzѷer4s!QD7өuЀc 4@r~HNsn'@!}ҜP+P6:B"Ht(F3`m(G &iI;QNd{< IRn4+Kq*s2Il#5. *RѦTd4 nO Q*g^mQCU8h QJ[WZeue H_כzKIb)qS&!X*lբb%HeIyL@K$ƪM(Ez>l"$k*[ B1Q$0ѵ-Fқl!ibӾOC—M,v4͈g){TmCIQ:hWL{+K mkg_ նEDy[ MWJfnG/Җj# >n[>W4*IwJ^ Fe%j#$u /Z,qi{T }FuVjFy@<Nv 02 e,]2 f@Nfd-l2 'y[ns[ =yF3=@(w GsF3A .MӞ B-P`285SMU9Ab-kX^k\wjt a;8}Ԝ4@ diiS;کvi:ԩ 7_/d)o22+ f{o\s<d ,fY]BO^=H\@1iM_ZӞ`RzԦvyk\Žs[Mo:ȎQ H[ۘ6Ω]js樾wTmOd7ݭv{MF3f{Wx ]ft&'wr%[\hJ yE':/(I.̠3Ww_iüz[[ճ5щ~OҗdVͶzϺe! G#X"kw>y9P>||9xx{9rx[\z< @& ɍUY%^ZMZe])] [˅`j,e_i) AE|0]afA1٢Uy <É_ U =^!BYEa\ U^EYf^l^ս0ਡ֡Z T]Μu j|5\= n$Fؽ`@PЌ&`(&++.^&-QeYEQ^5Z`Q~̕Zޡ \NA !n"" :fUݶ}΁`0* Y~?M(Sb]" 1Ya!Y/.!ENA1ڡIJR3"jZZ5: EN ޭ=[,b99^##a1ŠTX@ɕL_?WF)ҍ$*BqC[ɢV"^..G"Hz\1dAf)"d4@M5 "Ͻ%P8:gj!B:`$N#T}k~%lDXXR! >a[D¥*"."E/mtz\YޢeaN@KBc) =fS!6R` 8vfv-d@ͧ$_%f%EXY_lIf& d/+[+*^\ ޕgUBvd^Ne ܡ`F)㤱f &*4dZMFbO67 y$'"g&ej\ES']bmW (FiHPwH\ITpi Qi) ")))֩) #*Hv.*- *Fǡ N*f*xT쩦~*:ӥlC* ;%֪VDުEpj}*k k."+(+Fk:A^+Rk*v+j q뢂kY+뛚kk++kЫWkP+l(lX,6l91NWD tA A,~݄ >ǂ,MA~lAD̒,6KAI7lώ@Ϊ-*M! @-6-MDmԒll.lB㞯m-ίmZMn/J¯on:o7- pWpJ[# w b z װҰ ,onu,!671";#&^o1  q±00ޱWz2De#.#W2n%w2z. &'ք"f#+s*h+-wD[,Wi-߲/T'1DJ r1s$1?53隭&1)_7 S73B1׮8s93H,o"/$;36333:=[4Op53s74A82+B{n3Dr$3/Ei_7So3{tDo26#C;0='.twLC3At 9n4Z4P1M:Q5:W/Q[@7?#qCsJSuA঴OVoumTw*N)q5ZqN@ \\f[-^ߵ]^r_nauaasb+6Gq``udd2 B \6^f3ug5DivMgDc a4mbp^׵fvoN<5duBwU#2Lul37_;# vudcw'kJ#6xWMvL,wyygMxA*l{6Vw|Mƚk/l7z׈ C뷁1S˷;t;xk/v8πip8p8Gͦ욁8Ǹ͊ϸ8縎8GxXA yww\x7w ?yO9[ 9kycysgQ9kyyo'_9y繝 z+y9[wyy9o9Szo9sg: yz7wA:׺:纮zX:{;' 7?;GO;W_;go;w{[7:;߸88{:;׻;绾;;{<8'|Ȼ?GSs L}>}̽O>׾z#뛾_A>=GK~ ,A="P4?.*a3W+|,Rx«;8@(1`A&T8P VDDqI#QrD>lL9dI'QTeK/EN5"~p2igK:YV3 G&UȂF:jUR vA"B4r'Um[oƕk!6jȈP"j]4-OrOypncǏ 2"I]\iDY'@6=ti!1Ǐ=8b$OIR(QNUǯfEص+acruױcC!A~]]ԗ#Ldqݿg_y$s"BPI, Pph>?b A6l-7Z)Dͤ%*jA8H$Q0a >CK⛱mS@EFlj2(S)j6NQ"J+0 9eMHVY+!Q(RRER1%jhT<҆GP3NCUB"EmD꾎|N8CSXCM.B>UdJjBkcKR !$"ڂN2BB $CdK6㇈#L֐L!=ضOMKlת b?|A_GO)J`Fk/TQC@/J L@Y^,P,ȋE b2D $0EK0%AߣG "b 9bquZ";6NxȂ` @#A>!Sma-pZM(aJx) >Zc8=$ xT9ך]wA ނ|?a 4Xo|}!N"ZgUJlP 0:W6C " 0BL YMy$0zLac&ZAjZ"ӣN|`Ԏs#b` y &o(vPD(>@C'V9dCiJH$7 ;Ёnt:r{=4D!/{M@^X2Sp+xQ8 b*U4ac%p 6XwY*VѲ'ԔO)u"HPE@!ٰ bA.:A7\b "~7NIbÅÂ%NH5٩xzU(\q rx8 x3ʅN}(  ܖ!MZ>WWRt!N"UK 6ֵDEMJ4k ;7 nf-cך%At_d(< l{ږo=6 j[ݦnk%rj@ w@lZGh"6q }$φTY uA}4!V8R0Q_5A~W!79 ~p}-wi>q\H=ü2wH>MwIyU>!KOEn7ˎw}4dDAli[WA%O5 ݨ|'x-|\^ R BpAz/. /t \z[a]p/Xt{ۯ {忞7~PϾg.LZ|^ʧ}/kp_}>go`ObOgO/P\O PhoTkOe/?`/ZoNp(pJBG} .n8->00.ocS"=B uI!z(S o b~uLN _, ~H 8F Ѿ ›2‰Ȑd !d$-0ڎ"Q&qvn9 8qV" #FQ'Q1+`YYq^qO& *]q?Vo1OQk1# 1sYqC" q3`23`4`14@. ..ɑ1q 7 c ,@!r"1@R!)2q# 7 4$7!I$- ۑQ9&m&q2'ur'y'}'2(r(((2)r)))2*12W`?Qe) a+ՒxQ'#&. r$@][/e1 qsq1#2Q r$1 q2Q3!1+S c#$$5ϑ"!_3=I e4-Mrg317Q0 %s839s9993:s)*m jR+R/s,,R+r߲;+0R,Wq0E0# 117r5@=23y3"9iS2!k -,rC3ɱ"-#-<@3S&er,r3C54%=s8-@3`:q4GutGyG}Gt*:@;K++W,=Qϲ/'@-W2-s;%>>S??@`Cͱ7g2)t W3iqOB!2O/ 1"qSC%2 RQ25%s2m4M$-$ɱ&P4mN)& '! aUa5VeuVi5VHbTI%L-+@˴TMq?Mt 7A CA7B;7uQS9P7E3tCW35_SC_7) S6_$Q2F7S8rA &OȠTrUU( A,6:;U <:VUveYeVq+rW=q;.S.W15YyViqZ+C1Kt,Yq[0[NatRA1 2T3AٕN18s4/!Q! 5[n1rA`=8K2Ca53Q  2& ac`N@:G9 HeEwtItfc*t5IkfWhYqXuJiQ=X2,S =ՓZ4tZv.MTk4"S2tR53O]2 W3S{q5+T 3BU`K2F1`]#aT6iPM$_t4S޵$OR|s sw a& F a6<&;08' Ju"dXUaN`RC *8dqRw' t8XtQ7uWww]vђwwɲv,iK2hO(@.XL3jgv+3ZM61@ct%'uQ t7@]]1 P!ٶ Omao3#W,12mmWa[tFlp ')17Igp !p tX02aY' w. +r @ xskrYv9'{!s !y'I  w GbiRf |w+ø;}vvv=tg-3h֎4vc1@ [YU3m1Eב2A7 47!o9##U}nz+2u$Q4/ au3~1OQB Y- 2QbrUA֘9y oR Nq "- o2:c?Y:U{cvu --iqxu;շ1}w{zi1)};cz _  # #3&z11q1&C` r7 :@ ? x9I S ' `=A:mْh_ {絛[{ҿ5=w~ӯAI}[ `"_eݾ=B@ c`„ B $Vp!ƍcG ,\(iVD3IkVX3M6{B\ 12ҥK =Id0 'Q }PY P,}1t)d1à"J У3g `g8*u)Dر";~uGL2([~ ;ٴk۾;ݼ{ <8ċ?8̛;=zsK8@!; صk=EڱN7p=|SاP<׫OOwGz@ -HapJ aI>M6XC1A @/RbGTN$R,Sd>ڴd6apS}x֚!fmIy\IfffjIfrn)]rIRezyf^xGA{_ї}I_ fJH< @(jfx2iAx%"i T-.bC*("ڨ7ю:LC>=4bh-1EF\\C¦~.Sn of͉o  Xx)!xZW{ާ:uIZ0wz2 JafO<@HN+Pή2b)hNft JtLGZRAd6g#NjkM;o^6cevjvr w - GqNA [o 9 1%-,S23+LZ Ҏ tmJ*$$IF%fv/|?fMsB=l}gO~柏~kK_o[ki5WQeʏ pcbG=A5aDWҵ  J?p1`7? P@Qdp|(_e@V89yWUjߕ^{e^ |a9 Ct?n'{ԟk;]@YW{wA(uo,3/C~7ywyzWB3"rz # +1"wg{,2zH{74y4}`QkdG|+(/Ƈ@7tÇtMWv6}w}WGا}^W}}ewַu=htW}Gtu~qwxWxWyZV(W$ hzwz3yx7{x{^S8G\,HtW~4}N|v;K?ӗ~~Dtmvt~x~RXxVg{wy7y842Rz y):.*؁W{JÌLJ^&eLfOnVHgg3HW(uW~hv7(tsg}tJH~x}NJWxyX{7yygxeys膷*rxh˨x 0'uȒf"=C_V1|HHt^h}D'wG׃u}6؈L׃GHJ蓥xXw犲8syyG3@c3'#p":)ψ{{e֒{&hv|RW^guG}@ u(׈e瓇9H~)A}wwP؏YVjY]YxzYy8zf:ɖxr$PyPzɗ>A|E'Iד؉YǘMܧG)IW2)yXSzV)xd8yy9@#BSdžf)yixH@0\fIɜY=0IiZuX}Y}㷈H}yOIy[Y`)+xy(3 "+:s"pxXG1G)ȡj)D95h}u,j(:1 u4lGc}yGz iQYVم_Rc(WyH|S+y:i:Drq1aP%'WA r 5-1qBB&ݒeʪ:7/,6#7*z `,1*)Ǭ,ZRC磪*E& {e+㦰 SxkKLp1Ib !k'gn-2²/K5ۗ*%k;˳[=+CK/ ۱AKI_K Q;F cO+W;M;SV]kZYHc˳`^bKiU+˵j kqk ;Uw˷j@;]}Kf1y3 ظF˪hks a˹mBf@ U5`hnۺG۹05knn^`㶮˻+l@0]@9kKφ0;; ۰{j˼;{˾ i;@0۶K+{k05p@< Lȑٛ5࿁g澂\!"*+[߻3`$0eK;<_C\L 3%|Ȫ!h%k'/ /6̿0mӗ5}?<[L G>\lшH lBW=/mMmO_}_Qv\`.I!>qQ[rׯdmׁݜ9m]}ւhƜwmy؁ƅ &ٓt~F١ݪ9n|Nآ+օkի-ۮɜ}γJɎɒ\^۔ʏ|ܼm˚˼ܱܳmٛ۟ ٽL>^Z~n>$nTҞnR1Ύlk.mFJlZjܞЎ^`nW]]W.>VPnn~^.No ? O /! _)O01o!?'4^EEoGIKSp9!t>Q DJa $J`1(i(Qz;!K0R[HI@drMRBlh*2Q2_  yc \HUD(8SͯTfj3%~ DjKT&)Ϡ֨P% -A3C2AF+)O6>qʾJ17=$M-! )k3YG LώVUmJ@M!@ȇ<:鄎^Z`УM*9@衩<2لDNH"azpL# o^ UHO@?TJ!] UPQFUL %9Ji"-AQH6p:%QVDO*fCF)S,}/T!m?j5j1*C>PfppAMT5*kZ2!(x5 N,<a<2Jhs=Gv6 ^`@u J#RE0(NRV8.%l}"+JUUH EJUtMԌP!d:)>ab'i "Ԡ4 BJWD.<}Bi^Ԅ-GJ@ݝ`ccD DJk:2hA(^D1 *ad BhᐪDRE6r",Ath$$<]W^O2"PB 5x\W$e K.A=\RY`v&PZF|بٱ0KsEl8a~#۟ Q2]%'VQ8\fT*2%6W)P˕0' !.pFFBux(\66A xJB.#7*dȩFxzSgQB[; h3z\ Ijw9J>+o>x@!@P=j!B0 |b D&4C6dFD!,>,D.G4D=4,D0zzA@#UVY7EAAEZ ^wY*'C"=ދ`, #F0UCEp,\LF{a'o @mdFl ԃs]Ďƭk Fe,|G$ HG{,HlH|H ~HʨHZD @HHH II,I` to lock the file you want to write to, instead create a separate ``.lock`` file as shown above. .. image:: example.gif :alt: Example gif Similar libraries ----------------- Perhaps you are looking for something like: - the `pid `_ 3rd party library, - for Windows the `msvcrt `_ module in the standard library, - for UNIX the `fcntl `_ module in the standard library. Installation ------------ ``filelock`` is available via PyPI, so you can pip install it: .. code-block:: bash python -m pip install filelock Tutorial -------- A :class:`FileLock ` is used to indicate another process of your application that a resource or working directory is currently used. To do so, create a :class:`FileLock ` first: .. code-block:: python from filelock import Timeout, FileLock file_path = "high_ground.txt" lock_path = "high_ground.txt.lock" lock = FileLock(lock_path, timeout=1) The lock object supports multiple ways for acquiring the lock, including the ones used to acquire standard Python thread locks: .. code-block:: python with lock: with open(file_path, "a") as f: f.write("Hello there!") lock.acquire() try: with open(file_path, "a") as f: f.write("General Kenobi!") finally: lock.release() @lock def decorated(): print("You're a decorated Jedi!") decorated() The :meth:`acquire ` method accepts also a ``timeout`` parameter. If the lock cannot be acquired within ``timeout`` seconds, a :class:`Timeout ` exception is raised: .. code-block:: python try: with lock.acquire(timeout=10): with open(file_path, "a") as f: f.write("I have a bad feeling about this.") except Timeout: print("Another instance of this application currently holds the lock.") The lock objects are recursive locks, which means that once acquired, they will not block on successive lock requests: .. code-block:: python def cite1(): with lock: with open(file_path, "a") as f: f.write("I hate it when he does that.") def cite2(): with lock: with open(file_path, "a") as f: f.write("You don't want to sell me death sticks.") # The lock is acquired here. with lock: cite1() cite2() # And released here. Logging ------- All log messages by this library are made using the ``DEBUG_ level``, under the ``filelock`` name. On how to control displaying/hiding that please consult the `logging documentation of the standard library `_. E.g. to hide these messages you can use: .. code-block:: python logging.getLogger("filelock").setLevel(logging.INFO) FileLock vs SoftFileLock ------------------------ The :class:`FileLock ` is platform dependent while the :class:`SoftFileLock ` is not. Use the :class:`FileLock ` if all instances of your application are running on the same platform and a :class:`SoftFileLock ` otherwise. The :class:`SoftFileLock ` only watches the existence of the lock file. This makes it ultra portable, but also more prone to dead locks if the application crashes. You can simply delete the lock file in such cases. Asyncio support --------------- This library currently does not support asyncio. We'd recommend adding an asyncio variant though if someone can make a pull request for it, `see here `_. Contributions and issues ------------------------ Contributions are always welcome, please make sure they pass all tests before creating a pull request. This module is hosted on `GitHub `_. If you have any questions or suggestions, don't hesitate to open a new issue 😊. There's no bad question, just a missed opportunity to learn more. .. toctree:: :hidden: self api license changelog ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/docs/license.rst0000644000175100001710000000012100000000000015746 0ustar00runnerdockerLicense ======= *py-filelock* is public domain: .. literalinclude:: ../LICENSE ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/docs/logo.svg0000644000175100001710000000341100000000000015260 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/pyproject.toml0000644000175100001710000000051400000000000015564 0ustar00runnerdocker[build-system] requires = [ "setuptools >= 41.0.0", "wheel >= 0.30.0", "setuptools_scm >= 2", ] build-backend = 'setuptools.build_meta' [tool.black] line-length = 120 [tool.isort] line_length = 120 profile = "black" known_first_party = ["filelock", "tests"] [tool.setuptools_scm] write_to = "src/filelock/version.py" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.019809 filelock-3.6.0/setup.cfg0000644000175100001710000000336000000000000014473 0ustar00runnerdocker[metadata] name = filelock description = A platform independent file lock. long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/tox-dev/py-filelock author = Benedikt Schmitt author_email = benedikt@benediktschmitt.de license = Unlicense license_file = LICENSE classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: Public Domain Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Topic :: Internet Topic :: Software Development :: Libraries Topic :: System download_url = https://github.com/tox-dev/py-filelock/archive/main.zip project_urls = Source=https://github.com/tox-dev/py-filelock Tracker=https://github.com/tox-dev/py-filelock/issues [options] packages = find: python_requires = >=3.7 package_dir = =src zip_safe = True [options.packages.find] where = src [options.extras_require] docs = furo>=2021.8.17b43 sphinx>=4.1 sphinx-autodoc-typehints>=1.12 testing = covdefaults>=1.2.0 coverage>=4 pytest>=4 pytest-cov pytest-timeout>=1.4.2 [options.package_data] filelock = py.typed [coverage:run] plugins = covdefaults parallel = true [coverage:paths] src = src .tox/*/lib/python*/site-packages .tox/pypy*/site-packages .tox\*\Lib\site-packages\ */src *\src other = . */py-filelock *\py-filelock [coverage:report] fail_under = 88 [coverage:html] show_contexts = true skip_covered = false [coverage:covdefaults] subtract_omit = */.tox/* [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/setup.py0000644000175100001710000000011200000000000014354 0ustar00runnerdockerfrom __future__ import annotations from setuptools import setup setup() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.015809 filelock-3.6.0/src/0000755000175100001710000000000000000000000013437 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.015809 filelock-3.6.0/src/filelock/0000755000175100001710000000000000000000000015227 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/__init__.py0000644000175100001710000000233600000000000017344 0ustar00runnerdocker""" A platform independent file lock that supports the with-statement. .. autodata:: filelock.__version__ :no-value: """ from __future__ import annotations import sys import warnings from ._api import AcquireReturnProxy, BaseFileLock from ._error import Timeout from ._soft import SoftFileLock from ._unix import UnixFileLock, has_fcntl from ._windows import WindowsFileLock from .version import version #: version of the project as a string __version__: str = version if sys.platform == "win32": # pragma: win32 cover _FileLock: type[BaseFileLock] = WindowsFileLock else: # pragma: win32 no cover if has_fcntl: _FileLock: type[BaseFileLock] = UnixFileLock else: _FileLock = SoftFileLock if warnings is not None: warnings.warn("only soft file lock is available") #: Alias for the lock, which should be used for the current platform. On Windows, this is an alias for # :class:`WindowsFileLock`, on Unix for :class:`UnixFileLock` and otherwise for :class:`SoftFileLock`. FileLock: type[BaseFileLock] = _FileLock __all__ = [ "__version__", "FileLock", "SoftFileLock", "Timeout", "UnixFileLock", "WindowsFileLock", "BaseFileLock", "AcquireReturnProxy", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/_api.py0000644000175100001710000002032500000000000016513 0ustar00runnerdockerfrom __future__ import annotations import contextlib import logging import os import time import warnings from abc import ABC, abstractmethod from threading import Lock from types import TracebackType from typing import Any from ._error import Timeout _LOGGER = logging.getLogger("filelock") # This is a helper class which is returned by :meth:`BaseFileLock.acquire` and wraps the lock to make sure __enter__ # is not called twice when entering the with statement. If we would simply return *self*, the lock would be acquired # again in the *__enter__* method of the BaseFileLock, but not released again automatically. issue #37 (memory leak) class AcquireReturnProxy: """A context aware object that will release the lock file when exiting.""" def __init__(self, lock: BaseFileLock) -> None: self.lock = lock def __enter__(self) -> BaseFileLock: return self.lock def __exit__( self, exc_type: type[BaseException] | None, # noqa: U100 exc_value: BaseException | None, # noqa: U100 traceback: TracebackType | None, # noqa: U100 ) -> None: self.lock.release() class BaseFileLock(ABC, contextlib.ContextDecorator): """Abstract base class for a file lock object.""" def __init__(self, lock_file: str | os.PathLike[Any], timeout: float = -1) -> None: """ Create a new lock object. :param lock_file: path to the file :param timeout: default timeout when acquiring the lock. It will be used as fallback value in the acquire method, if no timeout value (``None``) is given. If you want to disable the timeout, set it to a negative value. A timeout of 0 means, that there is exactly one attempt to acquire the file lock. """ # The path to the lock file. self._lock_file: str = os.fspath(lock_file) # The file descriptor for the *_lock_file* as it is returned by the os.open() function. # This file lock is only NOT None, if the object currently holds the lock. self._lock_file_fd: int | None = None # The default timeout value. self.timeout: float = timeout # We use this lock primarily for the lock counter. self._thread_lock: Lock = Lock() # The lock counter is used for implementing the nested locking mechanism. Whenever the lock is acquired, the # counter is increased and the lock is only released, when this value is 0 again. self._lock_counter: int = 0 @property def lock_file(self) -> str: """:return: path to the lock file""" return self._lock_file @property def timeout(self) -> float: """ :return: the default timeout value .. versionadded:: 2.0.0 """ return self._timeout @timeout.setter def timeout(self, value: float | str) -> None: """ Change the default timeout value. :param value: the new value """ self._timeout = float(value) @abstractmethod def _acquire(self) -> None: """If the file lock could be acquired, self._lock_file_fd holds the file descriptor of the lock file.""" raise NotImplementedError @abstractmethod def _release(self) -> None: """Releases the lock and sets self._lock_file_fd to None.""" raise NotImplementedError @property def is_locked(self) -> bool: """ :return: A boolean indicating if the lock file is holding the lock currently. .. versionchanged:: 2.0.0 This was previously a method and is now a property. """ return self._lock_file_fd is not None def acquire( self, timeout: float | None = None, poll_interval: float = 0.05, *, poll_intervall: float | None = None, ) -> AcquireReturnProxy: """ Try to acquire the file lock. :param timeout: maximum wait time for acquiring the lock, ``None`` means use the default :attr:`~timeout` is and if ``timeout < 0``, there is no timeout and this method will block until the lock could be acquired :param poll_interval: interval of trying to acquire the lock file :param poll_intervall: deprecated, kept for backwards compatibility, use ``poll_interval`` instead :raises Timeout: if fails to acquire lock within the timeout period :return: a context object that will unlock the file when the context is exited .. code-block:: python # You can use this method in the context manager (recommended) with lock.acquire(): pass # Or use an equivalent try-finally construct: lock.acquire() try: pass finally: lock.release() .. versionchanged:: 2.0.0 This method returns now a *proxy* object instead of *self*, so that it can be used in a with statement without side effects. """ # Use the default timeout, if no timeout is provided. if timeout is None: timeout = self.timeout if poll_intervall is not None: msg = "use poll_interval instead of poll_intervall" warnings.warn(msg, DeprecationWarning, stacklevel=2) poll_interval = poll_intervall # Increment the number right at the beginning. We can still undo it, if something fails. with self._thread_lock: self._lock_counter += 1 lock_id = id(self) lock_filename = self._lock_file start_time = time.monotonic() try: while True: with self._thread_lock: if not self.is_locked: _LOGGER.debug("Attempting to acquire lock %s on %s", lock_id, lock_filename) self._acquire() if self.is_locked: _LOGGER.debug("Lock %s acquired on %s", lock_id, lock_filename) break elif 0 <= timeout < time.monotonic() - start_time: _LOGGER.debug("Timeout on acquiring lock %s on %s", lock_id, lock_filename) raise Timeout(self._lock_file) else: msg = "Lock %s not acquired on %s, waiting %s seconds ..." _LOGGER.debug(msg, lock_id, lock_filename, poll_interval) time.sleep(poll_interval) except BaseException: # Something did go wrong, so decrement the counter. with self._thread_lock: self._lock_counter = max(0, self._lock_counter - 1) raise return AcquireReturnProxy(lock=self) def release(self, force: bool = False) -> None: """ Releases the file lock. Please note, that the lock is only completely released, if the lock counter is 0. Also note, that the lock file itself is not automatically deleted. :param force: If true, the lock counter is ignored and the lock is released in every case/ """ with self._thread_lock: if self.is_locked: self._lock_counter -= 1 if self._lock_counter == 0 or force: lock_id, lock_filename = id(self), self._lock_file _LOGGER.debug("Attempting to release lock %s on %s", lock_id, lock_filename) self._release() self._lock_counter = 0 _LOGGER.debug("Lock %s released on %s", lock_id, lock_filename) def __enter__(self) -> BaseFileLock: """ Acquire the lock. :return: the lock object """ self.acquire() return self def __exit__( self, exc_type: type[BaseException] | None, # noqa: U100 exc_value: BaseException | None, # noqa: U100 traceback: TracebackType | None, # noqa: U100 ) -> None: """ Release the lock. :param exc_type: the exception type if raised :param exc_value: the exception value if raised :param traceback: the exception traceback if raised """ self.release() def __del__(self) -> None: """Called when the lock object is deleted.""" self.release(force=True) __all__ = [ "BaseFileLock", "AcquireReturnProxy", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/_error.py0000644000175100001710000000061700000000000017075 0ustar00runnerdockerfrom __future__ import annotations class Timeout(TimeoutError): """Raised when the lock could not be acquired in *timeout* seconds.""" def __init__(self, lock_file: str) -> None: #: The path of the file lock. self.lock_file = lock_file def __str__(self) -> str: return f"The file lock '{self.lock_file}' could not be acquired." __all__ = [ "Timeout", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/_soft.py0000644000175100001710000000316200000000000016715 0ustar00runnerdockerfrom __future__ import annotations import os import sys from errno import EACCES, EEXIST, ENOENT from ._api import BaseFileLock from ._util import raise_on_exist_ro_file class SoftFileLock(BaseFileLock): """Simply watches the existence of the lock file.""" def _acquire(self) -> None: raise_on_exist_ro_file(self._lock_file) # first check for exists and read-only mode as the open will mask this case as EEXIST mode = ( os.O_WRONLY # open for writing only | os.O_CREAT | os.O_EXCL # together with above raise EEXIST if the file specified by filename exists | os.O_TRUNC # truncate the file to zero byte ) try: fd = os.open(self._lock_file, mode) except OSError as exception: if exception.errno == EEXIST: # expected if cannot lock pass elif exception.errno == ENOENT: # No such file or directory - parent directory is missing raise elif exception.errno == EACCES and sys.platform != "win32": # pragma: win32 no cover # Permission denied - parent dir is R/O raise # note windows does not allow you to make a folder r/o only files else: self._lock_file_fd = fd def _release(self) -> None: os.close(self._lock_file_fd) # type: ignore # the lock file is definitely not None self._lock_file_fd = None try: os.remove(self._lock_file) except OSError: # the file is already deleted and that's what we want pass __all__ = [ "SoftFileLock", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/_unix.py0000644000175100001710000000305200000000000016723 0ustar00runnerdockerfrom __future__ import annotations import os import sys from typing import cast from ._api import BaseFileLock #: a flag to indicate if the fcntl API is available has_fcntl = False if sys.platform == "win32": # pragma: win32 cover class UnixFileLock(BaseFileLock): """Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems.""" def _acquire(self) -> None: raise NotImplementedError def _release(self) -> None: raise NotImplementedError else: # pragma: win32 no cover try: import fcntl except ImportError: pass else: has_fcntl = True class UnixFileLock(BaseFileLock): """Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems.""" def _acquire(self) -> None: open_mode = os.O_RDWR | os.O_CREAT | os.O_TRUNC fd = os.open(self._lock_file, open_mode) try: fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except OSError: os.close(fd) else: self._lock_file_fd = fd def _release(self) -> None: # Do not remove the lockfile: # https://github.com/tox-dev/py-filelock/issues/31 # https://stackoverflow.com/questions/17708885/flock-removing-locked-file-without-race-condition fd = cast(int, self._lock_file_fd) self._lock_file_fd = None fcntl.flock(fd, fcntl.LOCK_UN) os.close(fd) __all__ = [ "has_fcntl", "UnixFileLock", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/_util.py0000644000175100001710000000112200000000000016711 0ustar00runnerdockerfrom __future__ import annotations import os import stat def raise_on_exist_ro_file(filename: str) -> None: try: file_stat = os.stat(filename) # use stat to do exists + can write to check without race condition except OSError: return None # swallow does not exist or other errors if file_stat.st_mtime != 0: # if os.stat returns but modification is zero that's an invalid os.stat - ignore it if not (file_stat.st_mode & stat.S_IWUSR): raise PermissionError(f"Permission denied: {filename!r}") __all__ = [ "raise_on_exist_ro_file", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/_windows.py0000644000175100001710000000354200000000000017436 0ustar00runnerdockerfrom __future__ import annotations import os import sys from errno import ENOENT from typing import cast from ._api import BaseFileLock from ._util import raise_on_exist_ro_file if sys.platform == "win32": # pragma: win32 cover import msvcrt class WindowsFileLock(BaseFileLock): """Uses the :func:`msvcrt.locking` function to hard lock the lock file on windows systems.""" def _acquire(self) -> None: raise_on_exist_ro_file(self._lock_file) mode = ( os.O_RDWR # open for read and write | os.O_CREAT # create file if not exists | os.O_TRUNC # truncate file if not empty ) try: fd = os.open(self._lock_file, mode) except OSError as exception: if exception.errno == ENOENT: # No such file or directory raise else: try: msvcrt.locking(fd, msvcrt.LK_NBLCK, 1) except OSError: os.close(fd) else: self._lock_file_fd = fd def _release(self) -> None: fd = cast(int, self._lock_file_fd) self._lock_file_fd = None msvcrt.locking(fd, msvcrt.LK_UNLCK, 1) os.close(fd) try: os.remove(self._lock_file) # Probably another instance of the application hat acquired the file lock. except OSError: pass else: # pragma: win32 no cover class WindowsFileLock(BaseFileLock): """Uses the :func:`msvcrt.locking` function to hard lock the lock file on windows systems.""" def _acquire(self) -> None: raise NotImplementedError def _release(self) -> None: raise NotImplementedError __all__ = [ "WindowsFileLock", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/src/filelock/py.typed0000644000175100001710000000000000000000000016714 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122370.0 filelock-3.6.0/src/filelock/version.py0000644000175100001710000000021600000000000017265 0ustar00runnerdocker# coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control version = '3.6.0' version_tuple = (3, 6, 0) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.015809 filelock-3.6.0/src/filelock.egg-info/0000755000175100001710000000000000000000000016721 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122370.0 filelock-3.6.0/src/filelock.egg-info/PKG-INFO0000644000175100001710000000410700000000000020020 0ustar00runnerdockerMetadata-Version: 2.1 Name: filelock Version: 3.6.0 Summary: A platform independent file lock. Home-page: https://github.com/tox-dev/py-filelock Download-URL: https://github.com/tox-dev/py-filelock/archive/main.zip Author: Benedikt Schmitt Author-email: benedikt@benediktschmitt.de License: Unlicense Project-URL: Source, https://github.com/tox-dev/py-filelock Project-URL: Tracker, https://github.com/tox-dev/py-filelock/issues Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: Public Domain Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Topic :: Internet Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System Requires-Python: >=3.7 Description-Content-Type: text/markdown Provides-Extra: docs Provides-Extra: testing License-File: LICENSE # py-filelock [![PyPI](https://img.shields.io/pypi/v/filelock?style=flat-square)](https://pypi.org/project/filelock/) [![Supported Python versions](https://img.shields.io/pypi/pyversions/filelock.svg)](https://pypi.org/project/filelock/) [![Documentation status](https://readthedocs.org/projects/py-filelock/badge/?version=latest&style=flat-square)](https://py-filelock.readthedocs.io/en/latest/?badge=latest) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Downloads](https://pepy.tech/badge/filelock/month)](https://pepy.tech/project/filelock/month) [![check](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml/badge.svg)](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml) For more information checkout the [official documentation](https://py-filelock.readthedocs.io/en/latest/api.html). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122371.0 filelock-3.6.0/src/filelock.egg-info/SOURCES.txt0000644000175100001710000000131100000000000020601 0ustar00runnerdocker.gitignore .pre-commit-config.yaml .readthedocs.yaml LICENSE README.md codecov.yml pyproject.toml setup.cfg setup.py tox.ini whitelist.txt .github/workflows/check.yml docs/api.rst docs/changelog.rst docs/conf.py docs/example.gif docs/index.rst docs/license.rst docs/logo.svg src/filelock/__init__.py src/filelock/_api.py src/filelock/_error.py src/filelock/_soft.py src/filelock/_unix.py src/filelock/_util.py src/filelock/_windows.py src/filelock/py.typed src/filelock/version.py src/filelock.egg-info/PKG-INFO src/filelock.egg-info/SOURCES.txt src/filelock.egg-info/dependency_links.txt src/filelock.egg-info/requires.txt src/filelock.egg-info/top_level.txt src/filelock.egg-info/zip-safe tests/test_filelock.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122370.0 filelock-3.6.0/src/filelock.egg-info/dependency_links.txt0000644000175100001710000000000100000000000022767 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122370.0 filelock-3.6.0/src/filelock.egg-info/requires.txt0000644000175100001710000000023300000000000021317 0ustar00runnerdocker [docs] furo>=2021.8.17b43 sphinx>=4.1 sphinx-autodoc-typehints>=1.12 [testing] covdefaults>=1.2.0 coverage>=4 pytest>=4 pytest-cov pytest-timeout>=1.4.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122370.0 filelock-3.6.0/src/filelock.egg-info/top_level.txt0000644000175100001710000000001100000000000021443 0ustar00runnerdockerfilelock ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122370.0 filelock-3.6.0/src/filelock.egg-info/zip-safe0000644000175100001710000000000100000000000020351 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1645122371.019809 filelock-3.6.0/tests/0000755000175100001710000000000000000000000014012 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/tests/test_filelock.py0000644000175100001710000003160400000000000017217 0ustar00runnerdockerfrom __future__ import annotations import inspect import logging import sys import threading from contextlib import contextmanager from inspect import getframeinfo, stack from pathlib import Path, PurePath from stat import S_IWGRP, S_IWOTH, S_IWUSR from types import TracebackType from typing import Callable, Iterator, Tuple, Type, Union import pytest from _pytest.logging import LogCaptureFixture from filelock import BaseFileLock, FileLock, SoftFileLock, Timeout, UnixFileLock, WindowsFileLock @pytest.mark.parametrize( ("lock_type", "path_type"), [ (FileLock, str), (FileLock, PurePath), (FileLock, Path), (SoftFileLock, str), (SoftFileLock, PurePath), (SoftFileLock, Path), ], ) def test_simple( lock_type: type[BaseFileLock], path_type: type[str] | type[Path], tmp_path: Path, caplog: LogCaptureFixture ) -> None: caplog.set_level(logging.DEBUG) # test lock creation by passing a `str` lock_path = tmp_path / "a" lock = lock_type(path_type(lock_path)) with lock as locked: assert lock.is_locked assert lock is locked assert not lock.is_locked assert caplog.messages == [ f"Attempting to acquire lock {id(lock)} on {lock_path}", f"Lock {id(lock)} acquired on {lock_path}", f"Attempting to release lock {id(lock)} on {lock_path}", f"Lock {id(lock)} released on {lock_path}", ] assert [r.levelno for r in caplog.records] == [logging.DEBUG, logging.DEBUG, logging.DEBUG, logging.DEBUG] assert [r.name for r in caplog.records] == ["filelock", "filelock", "filelock", "filelock"] assert logging.getLogger("filelock").level == logging.NOTSET @contextmanager def make_ro(path: Path) -> Iterator[None]: write = S_IWUSR | S_IWGRP | S_IWOTH path.chmod(path.stat().st_mode & ~write) yield path.chmod(path.stat().st_mode | write) @pytest.fixture() def tmp_path_ro(tmp_path: Path) -> Iterator[Path]: with make_ro(tmp_path): yield tmp_path @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) @pytest.mark.skipif(sys.platform == "win32", reason="Windows does not have read only folders") def test_ro_folder(lock_type: type[BaseFileLock], tmp_path_ro: Path) -> None: lock = lock_type(str(tmp_path_ro / "a")) with pytest.raises(PermissionError, match="Permission denied"): lock.acquire() @pytest.fixture() def tmp_file_ro(tmp_path: Path) -> Iterator[Path]: filename = tmp_path / "a" filename.write_text("") with make_ro(filename): yield filename @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_ro_file(lock_type: type[BaseFileLock], tmp_file_ro: Path) -> None: lock = lock_type(str(tmp_file_ro)) with pytest.raises(PermissionError, match="Permission denied"): lock.acquire() @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_missing_directory(lock_type: type[BaseFileLock], tmp_path_ro: Path) -> None: lock_path = tmp_path_ro / "a" / "b" lock = lock_type(str(lock_path)) with pytest.raises(OSError, match="No such file or directory:"): lock.acquire() @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_nested_context_manager(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # lock is not released before the most outer with statement that locked the lock, is left lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) with lock as lock_1: assert lock.is_locked assert lock is lock_1 with lock as lock_2: assert lock.is_locked assert lock is lock_2 with lock as lock_3: assert lock.is_locked assert lock is lock_3 assert lock.is_locked assert lock.is_locked assert not lock.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_nested_acquire(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # lock is not released before the most outer with statement that locked the lock, is left lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) with lock.acquire() as lock_1: assert lock.is_locked assert lock is lock_1 with lock.acquire() as lock_2: assert lock.is_locked assert lock is lock_2 with lock.acquire() as lock_3: assert lock.is_locked assert lock is lock_3 assert lock.is_locked assert lock.is_locked assert not lock.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_nested_forced_release(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # acquires the lock using a with-statement and releases the lock before leaving the with-statement lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) with lock: assert lock.is_locked lock.acquire() assert lock.is_locked lock.release(force=True) assert not lock.is_locked assert not lock.is_locked _ExcInfoType = Union[Tuple[Type[BaseException], BaseException, TracebackType], Tuple[None, None, None]] class ExThread(threading.Thread): def __init__(self, target: Callable[[], None], name: str) -> None: super().__init__(target=target, name=name) self.ex: _ExcInfoType | None = None def run(self) -> None: try: super().run() except Exception: # pragma: no cover self.ex = sys.exc_info() # pragma: no cover def join(self, timeout: float | None = None) -> None: super().join(timeout=timeout) if self.ex is not None: print(f"fail from thread {self.name}") # pragma: no cover raise RuntimeError from self.ex[1] # pragma: no cover @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_threaded_shared_lock_obj(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # Runs 100 threads, which need the filelock. The lock must be acquired if at least one thread required it and # released, as soon as all threads stopped. lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) def thread_work() -> None: for _ in range(100): with lock: assert lock.is_locked threads = [ExThread(target=thread_work, name=f"t{i}") for i in range(100)] for thread in threads: thread.start() for thread in threads: thread.join() assert not lock.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) @pytest.mark.skipif(hasattr(sys, "pypy_version_info") and sys.platform == "win32", reason="deadlocks randomly") def test_threaded_lock_different_lock_obj(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # Runs multiple threads, which acquire the same lock file with a different FileLock object. When thread group 1 # acquired the lock, thread group 2 must not hold their lock. def t_1() -> None: for _ in range(1000): with lock_1: assert lock_1.is_locked assert not lock_2.is_locked def t_2() -> None: for _ in range(1000): with lock_2: assert not lock_1.is_locked assert lock_2.is_locked lock_path = tmp_path / "a" lock_1, lock_2 = lock_type(str(lock_path)), lock_type(str(lock_path)) threads = [(ExThread(t_1, f"t1_{i}"), ExThread(t_2, f"t2_{i}")) for i in range(10)] for thread_1, thread_2 in threads: thread_1.start() thread_2.start() for thread_1, thread_2 in threads: thread_1.join() thread_2.join() assert not lock_1.is_locked assert not lock_2.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # raises Timeout error when the lock cannot be acquired lock_path = tmp_path / "a" lock_1, lock_2 = lock_type(str(lock_path)), lock_type(str(lock_path)) # acquire lock 1 lock_1.acquire() assert lock_1.is_locked assert not lock_2.is_locked # try to acquire lock 2 with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): lock_2.acquire(timeout=0.1) assert not lock_2.is_locked assert lock_1.is_locked # release lock 1 lock_1.release() assert not lock_1.is_locked assert not lock_2.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_default_timeout(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # test if the default timeout parameter works lock_path = tmp_path / "a" lock_1, lock_2 = lock_type(str(lock_path)), lock_type(str(lock_path), timeout=0.1) assert lock_2.timeout == 0.1 # acquire lock 1 lock_1.acquire() assert lock_1.is_locked assert not lock_2.is_locked # try to acquire lock 2 with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): lock_2.acquire() assert not lock_2.is_locked assert lock_1.is_locked lock_2.timeout = 0 assert lock_2.timeout == 0 with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): lock_2.acquire() assert not lock_2.is_locked assert lock_1.is_locked # release lock 1 lock_1.release() assert not lock_1.is_locked assert not lock_2.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_context_release_on_exc(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # lock is released when an exception is thrown in a with-statement lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) try: with lock as lock_1: assert lock is lock_1 assert lock.is_locked raise Exception except Exception: assert not lock.is_locked @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_acquire_release_on_exc(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # lock is released when an exception is thrown in a acquire statement lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) try: with lock.acquire() as lock_1: assert lock is lock_1 assert lock.is_locked raise Exception except Exception: assert not lock.is_locked @pytest.mark.skipif(hasattr(sys, "pypy_version_info"), reason="del() does not trigger GC in PyPy") @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_del(lock_type: type[BaseFileLock], tmp_path: Path) -> None: # lock is released when the object is deleted lock_path = tmp_path / "a" lock_1, lock_2 = lock_type(str(lock_path)), lock_type(str(lock_path)) # acquire lock 1 lock_1.acquire() assert lock_1.is_locked assert not lock_2.is_locked # try to acquire lock 2 with pytest.raises(Timeout, match="The file lock '.*' could not be acquired."): lock_2.acquire(timeout=0.1) # delete lock 1 and try to acquire lock 2 again del lock_1 lock_2.acquire() assert lock_2.is_locked lock_2.release() def test_cleanup_soft_lock(tmp_path: Path) -> None: # tests if the lock file is removed after use lock_path = tmp_path / "a" lock = SoftFileLock(str(lock_path)) with lock: assert lock_path.exists() assert not lock_path.exists() @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_poll_intervall_deprecated(lock_type: type[BaseFileLock], tmp_path: Path) -> None: lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) with pytest.deprecated_call(match="use poll_interval instead of poll_intervall") as checker: lock.acquire(poll_intervall=0.05) # the deprecation warning will be captured by the checker frameinfo = getframeinfo(stack()[0][0]) # get frameinfo of current file and lineno (+1 than the above lineno) for warning in checker: if warning.filename == frameinfo.filename and warning.lineno + 1 == frameinfo.lineno: # pragma: no cover break else: # pragma: no cover pytest.fail("No warnings of stacklevel=2 matching.") @pytest.mark.parametrize("lock_type", [FileLock, SoftFileLock]) def test_context_decorator(lock_type: type[BaseFileLock], tmp_path: Path) -> None: lock_path = tmp_path / "a" lock = lock_type(str(lock_path)) @lock def decorated_method() -> None: assert lock.is_locked assert not lock.is_locked decorated_method() assert not lock.is_locked def test_wrong_platform(tmp_path: Path) -> None: assert not inspect.isabstract(UnixFileLock) assert not inspect.isabstract(WindowsFileLock) assert inspect.isabstract(BaseFileLock) lock_type = UnixFileLock if sys.platform == "win32" else WindowsFileLock lock = lock_type(str(tmp_path / "lockfile")) with pytest.raises(NotImplementedError): lock.acquire() with pytest.raises(NotImplementedError): lock._release() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/tox.ini0000644000175100001710000000551600000000000014172 0ustar00runnerdocker[tox] envlist = fix_lint py310 py39 py38 py37 pypy3 type coverage docs readme isolated_build = true skip_missing_interpreters = true minversion = 3.21 [testenv] description = run tests with {basepython} passenv = PIP_* PYTEST_* setenv = COVERAGE_FILE = {toxworkdir}{/}.coverage.{envname} extras = testing commands = pytest {tty:--color=yes} {posargs: \ --junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}filelock --cov {toxinidir}{/}tests \ --cov-config=setup.cfg --no-cov-on-fail --cov-report term-missing:skip-covered --cov-context=test \ --cov-report html:{envtmpdir}{/}htmlcov --cov-report xml:{toxworkdir}{/}coverage.{envname}.xml \ tests} package = wheel wheel_build_env = .pkg [testenv:fix_lint] description = format the code base to adhere to our styles, and complain about what we cannot do automatically passenv = * basepython = python3.9 skip_install = true deps = pre-commit>=2 commands = pre-commit run --all-files --show-diff-on-failure python -c 'import pathlib; print("hint: run \{\} install to add checks as pre-commit hook".format(pathlib.Path(r"{envdir}") / "bin" / "pre-commit"))' [testenv:type] description = run type check on code base setenv = {tty:MYPY_FORCE_COLOR = 1} deps = mypy==0.930 commands = mypy --strict src/filelock mypy --strict tests [testenv:coverage] description = combine coverage files and generate diff (against DIFF_AGAINST defaulting to origin/main) passenv = DIFF_AGAINST setenv = COVERAGE_FILE = {toxworkdir}/.coverage skip_install = true deps = covdefaults>=2.1 coverage>=6.2 diff-cover>=6.4 extras = parallel_show_output = true commands = coverage combine coverage report --skip-covered --show-missing coverage xml -o {toxworkdir}/coverage.xml coverage html -d {toxworkdir}/htmlcov diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}/coverage.xml depends = py310 py39 py38 py37 pypy3 [testenv:docs] description = build documentation extras = docs commands = sphinx-build -d "{envtmpdir}{/}doctree" docs "{toxworkdir}{/}docs_out" --color -b html {posargs} python -c 'print(r"documentation available under file://{toxworkdir}{/}docs_out{/}index.html")' [testenv:readme] description = check that the long description is valid (need for PyPI) skip_install = true deps = build>=0.6 twine>=3 extras = commands = pyproject-build -o {envtmpdir} --wheel --sdist . twine check {envtmpdir}/* [testenv:dev] description = generate a DEV environment usedevelop = true extras = docs testing commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' [flake8] max-complexity = 22 max-line-length = 120 [pep8] max-line-length = 120 [pytest] timeout = 120 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1645122363.0 filelock-3.6.0/whitelist.txt0000644000175100001710000000053300000000000015426 0ustar00runnerdockerautoclass autodoc autosectionlabel caplog creat eacces eexist enoent exc extlinks favicon fcntl filelock fmt fspath intersphinx intervall isabstract iwgrp iwoth iwusr levelno lk lockfile msvcrt nblck nitpicky notset param pathlib pygments rdwr ro runtime skipif tmp trunc typehints unlck util win32 wronly stacklevel frameinfo getframeinfo lineno