Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Slowroll:Build:1
python-plaster
plaster-1.1.2.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File plaster-1.1.2.obscpio of Package python-plaster
07070100000000000081A4000000000000000000000001637AD733000000C0000000000000000000000000000000000000001A00000000plaster-1.1.2/.coveragerc[run] parallel = true source = plaster tests omit = tests/fake_packages/* [paths] source = src/plaster */site-packages/plaster [report] show_missing = true precision = 2 07070100000001000081A4000000000000000000000001637AD73300000198000000000000000000000000000000000000001600000000plaster-1.1.2/.flake8[flake8] max-line-length = 89 ignore = # E203: whitespace before ':' (black fails to be PEP8 compliant) E203 # E731: do not assign a lambda expression, use a def E731 # W503: line break before binary operator (flake8 is not PEP8 compliant) W503 # W504: line break after binary operator (flake8 is not PEP8 compliant) W504 show-source = True exclude = tests/fake_packages, 07070100000002000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000001600000000plaster-1.1.2/.github07070100000003000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002500000000plaster-1.1.2/.github/ISSUE_TEMPLATE07070100000004000081A4000000000000000000000001637AD733000002EC000000000000000000000000000000000000003300000000plaster-1.1.2/.github/ISSUE_TEMPLATE/bug_report.md--- name: Bug Report about: Create a report to help us improve --- ## Get Support To get help or technical support, see [Get Support](https://pylonsproject.org/community-support.html). ## Bug Report Please [search the issue tracker](https://github.com/Pylons/plaster/issues) for similar issues before submitting a new issue. **Describe the bug** A clear and concise description of the bug. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain the issue. **Additional context** Add any other context about the issue here. 07070100000005000081A4000000000000000000000001637AD733000002F1000000000000000000000000000000000000003600000000plaster-1.1.2/.github/ISSUE_TEMPLATE/documentation.md--- name: Documentation Suggestion about: Create an issue to improve our documentation --- ## Get Support To get help or technical support, see [Get Support](https://pylonsproject.org/community-support.html). ## Documentation Suggestion Please [search the issue tracker](https://github.com/Pylons/plaster/issues) for similar issues before submitting a new issue. **Describe the issue** A clear and concise description of the issue. **Include references** 1. Go to the URL '...' 2. Click on '....' 3. Scroll down to '....' **Describe the improvement** A clear and concise description of your suggestion. **Screenshots** If applicable, add screenshots to help explain the issue. **Additional context** Add any other context about the issue here. 07070100000006000081A4000000000000000000000001637AD73300000337000000000000000000000000000000000000003800000000plaster-1.1.2/.github/ISSUE_TEMPLATE/feature_request.md--- name: Feature Request about: Suggest an idea for this project --- ## Get Support To get help or technical support, see [Get Support](https://pylonsproject.org/community-support.html). ## Feature Request Please [search the issue tracker](https://github.com/Pylons/plaster/issues) for similar issues before submitting a new issue. **Is your feature request related to an issue? Please describe.** A clear and concise description of the issue. Example: "I'm always frustrated when [...]". **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. 07070100000007000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002000000000plaster-1.1.2/.github/workflows07070100000008000081A4000000000000000000000001637AD73300000A0D000000000000000000000000000000000000002D00000000plaster-1.1.2/.github/workflows/ci-tests.ymlname: Build and test on: # Only on pushes to master or one of the release branches we build on push push: branches: - master - "[0-9].[0-9]+-branch" tags: # Build pull requests pull_request: jobs: test: strategy: matrix: py: - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - "pypy-3.7" os: - "ubuntu-latest" # - "windows-latest" - "macos-latest" architecture: - x64 - x86 exclude: # Linux and macOS don't have x86 python - os: "ubuntu-latest" architecture: x86 - os: "macos-latest" architecture: x86 name: "Python: ${{ matrix.py }}-${{ matrix.architecture }} on ${{ matrix.os }}" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: python-version: ${{ matrix.py }} architecture: ${{ matrix.architecture }} - run: pip install tox - name: Running tox run: tox -e py coverage: runs-on: ubuntu-latest name: Validate coverage steps: - uses: actions/checkout@v2 - name: Setup python 3.10 uses: actions/setup-python@v2 with: python-version: "3.10" architecture: x64 - run: pip install tox - run: tox -e py310,coverage docs: runs-on: ubuntu-latest name: Build the documentation steps: - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: python-version: "3.10" architecture: x64 - run: pip install tox - run: tox -e docs lint: runs-on: ubuntu-latest name: Lint the package steps: - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: python-version: "3.10" architecture: x64 - run: pip install tox - run: tox -e lint 07070100000009000081A4000000000000000000000001637AD73300000340000000000000000000000000000000000000001900000000plaster-1.1.2/.gitignore# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ env27 .idea cover !tests/fake_packages/defaultapp/ !tests/fake_packages/defaultapp/DefaultApp.egg-info/ 0707010000000A000081A4000000000000000000000001637AD73300000F8E000000000000000000000000000000000000001A00000000plaster-1.1.2/CHANGES.rst1.1.2 (2022-11-20) ================== - Fix a bug in which plaster would crash harder than expected if a URI is specified to a distribution that does not have the specified entry points. Now a LoaderNotFound exception will be raised instead of a bad unpacking of tuples. 1.1.1 (2022-11-20) ================== - Add support for Python 3.11. - Fix an bug introduced in 1.1 on some systems where ``plaster.exceptions.MultipleLoadersFound`` would be raised due to ``lib`` and ``lib64`` being symlinked to each other and both added to the ``sys.path``. See https://github.com/Pylons/plaster/pull/27 1.1 (2022-10-06) ================ - Drop support for Python 2.7, 3.4, 3.5, 3.6. - Add support for Python 3.8, 3.9, 3.10. - Drop runtime dependency on setuptools / pkg_resources by switching to ``importlib.metadata``. 1.0 (2017-10-11) ================ - Improve the exception message for ``InvalidURI`` to show the ``config_uri``. See https://github.com/Pylons/plaster/pull/17 0.5 (2017-06-02) ================ - When a scheme is not supplied, ``plaster.parse_uri`` will now autogenerate a scheme from the file extension with the format ``file+<ext>`` instead of simply ``<ext>`` (for example, ``file+ini`` instead of ``ini``). See https://github.com/Pylons/plaster/pull/16 - Absolute lookups are now pulled from the start of the scheme instead of the end. This means that if you want to explicitly define the package that the loader is pulled from, use ``package+scheme`` instead of ``scheme+package``. See https://github.com/Pylons/plaster/pull/16 0.4 (2017-03-30) ================ - Removed the ``plaster.NoSectionError`` exception. It's expected that individual loaders should return an empty dictionary of settings in the case that a section cannot be found. See https://github.com/Pylons/plaster/pull/12 - Expect the ``wsgi`` protocol to raise ``LookupError`` exceptions when a named wsgi component cannot be found. See https://github.com/Pylons/plaster/pull/12 0.3 (2017-03-27) ================ - Lookup now works differently. First "foo+bar" looks for an installed project distribution named "bar" with a loader named "foo". If this fails then it looks for any loader named "foo+bar". - Rename the loader entry point to ``plaster.loader_factory``. - Add the concept of protocols to ``plaster.get_loader`` and ``plaster.find_loaders``. - ``plaster.find_loaders`` now works on just schemes and protocols instead of full ``PlasterURL`` objects and implements the lookup algorithm for finding loader factories. - Change the ``ILoaderInfo`` interface to avoid being coupled to a particular uri. ``ILoaderInfo.load`` now takes a ``config_uri`` parameter. - Add a ``options`` dictionary to ``PlasterURL`` containing any arguments decoded from the query string. Loaders may use these for whatever they wish but one good option is default values in a config file. - Define the ``IWSGIProtocol`` interface which addons can use to implement a loader that can return full wsgi apps, servers and filters. - The scheme is now case-insensitive. 0.2 (2016-06-15) ================ - Allow ``config_uri`` syntax ``scheme:path`` alongside ``scheme://path``. See https://github.com/Pylons/plaster/issues/3 - Improve errors to show the user-supplied values in the error message. See https://github.com/Pylons/plaster/pull/4 - Add ``plaster.find_loaders`` which can be used by people who need a way to recover when ambiguous loaders are discovered via ``plaster.get_loader``. See https://github.com/Pylons/plaster/pull/5 - Rename ``plaster.Loader`` to ``plaster.ILoader`` to signify its purpose as an interface with no actual implementation. See https://github.com/Pylons/plaster/pull/5 - Introduce ``plaster.ILoaderFactory`` to document what the entry point targets are expected to implement. See https://github.com/Pylons/plaster/pull/5 0.1 (2016-06-12) ================ - Initial release. 0707010000000B000081A4000000000000000000000001637AD73300000BF2000000000000000000000000000000000000001F00000000plaster-1.1.2/CONTRIBUTING.rst.. highlight:: shell ============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. You can contribute in many ways: Types of Contributions ---------------------- Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/Pylons/plaster/issues. If you are reporting a bug, please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * Detailed steps to reproduce the bug. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything tagged with "feature" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ plaster could always use more documentation, whether as part of the official plaster docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/Pylons/plaster/issues. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) Get Started! ------------ Ready to contribute? Here's how to set up `plaster` for local development. 1. Fork the `plaster` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/plaster.git 3. Install your local copy into a virtualenv:: $ python3 -m venv env $ env/bin/pip install -e .[docs,testing] $ env/bin/pip install tox 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: $ env/bin/tox 6. Add your name to the ``CONTRIBUTORS.txt`` file in the root of the repository. 7. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 8. Submit a pull request through the GitHub website. Pull Request Guidelines ----------------------- Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 2.7, 3.4, 3.5, 3.6, and for PyPy. Check https://travis-ci.org/Pylons/plaster/pull_requests and make sure that the tests pass for all supported Python versions. Tips ---- To run a subset of tests:: $ env/bin/py.test tests.test_plaster 0707010000000C000081A4000000000000000000000001637AD73300001363000000000000000000000000000000000000001F00000000plaster-1.1.2/CONTRIBUTORS.txtPylons Project Contributor Agreement ==================================== The submitter agrees by adding his or her name within the section below named "Contributors" and submitting the resulting modified document to the canonical shared repository location for this software project (whether directly, as a user with "direct commit access", or via a "pull request"), he or she is signing a contract electronically. The submitter becomes a Contributor after a) he or she signs this document by adding their name beneath the "Contributors" section below, and b) the resulting document is accepted into the canonical version control repository. Treatment of Account -------------------- Contributor will not allow anyone other than the Contributor to use his or her username or source repository login to submit code to a Pylons Project source repository. Should Contributor become aware of any such use, Contributor will immediately notify Agendaless Consulting. Notification must be performed by sending an email to webmaster@agendaless.com. Until such notice is received, Contributor will be presumed to have taken all actions made through Contributor's account. If the Contributor has direct commit access, Agendaless Consulting will have complete control and discretion over capabilities assigned to Contributor's account, and may disable Contributor's account for any reason at any time. Legal Effect of Contribution ---------------------------- Upon submitting a change or new work to a Pylons Project source Repository (a "Contribution"), you agree to assign, and hereby do assign, a one-half interest of all right, title and interest in and to copyright and other intellectual property rights with respect to your new and original portions of the Contribution to Agendaless Consulting. You and Agendaless Consulting each agree that the other shall be free to exercise any and all exclusive rights in and to the Contribution, without accounting to one another, including without limitation, the right to license the Contribution to others under the MIT License. This agreement shall run with title to the Contribution. Agendaless Consulting does not convey to you any right, title or interest in or to the Program or such portions of the Contribution that were taken from the Program. Your transmission of a submission to the Pylons Project source Repository and marks of identification concerning the Contribution itself constitute your intent to contribute and your assignment of the work in accordance with the provisions of this Agreement. License Terms ------------- Code committed to the Pylons Project source repository (Committed Code) must be governed by the MIT License or another license acceptable to Agendaless Consulting. Until Agendaless Consulting declares in writing an acceptable license other than the MIT License, only the MIT License shall be used. A list of exceptions is detailed within the "Licensing Exceptions" section of this document, if one exists. Representations, Warranty, and Indemnification ---------------------------------------------- Contributor represents and warrants that the Committed Code does not violate the rights of any person or entity, and that the Contributor has legal authority to enter into this Agreement and legal authority over Contributed Code. Further, Contributor indemnifies Agendaless Consulting against violations. Cryptography ------------ Contributor understands that cryptographic code may be subject to government regulations with which Agendaless Consulting and/or entities using Committed Code must comply. Any code which contains any of the items listed below must not be checked-in until Agendaless Consulting staff has been notified and has approved such contribution in writing. - Cryptographic capabilities or features - Calls to cryptographic features - User interface elements which provide context relating to cryptography - Code which may, under casual inspection, appear to be cryptographic. Notices ------- Contributor confirms that any notices required will be included in any Committed Code. Licensing Exceptions ==================== Code committed within the ``docs/`` subdirectory of the plaster source control repository and "docstrings" which appear in the documentation generated by running "make" within this directory are licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License (https://creativecommons.org/licenses/by-nc-sa/3.0/us/). List of Contributors ==================== The below-signed are contributors to a code repository that is part of the project named "plaster". Each below-signed contributor has read, understand and agrees to the terms above in the section within this document entitled "Pylons Project Contributor Agreement" as of the date beside his or her name. Contributors ------------ - Michael Merickel (2016-06-12) - Steve Piercy (2017-08-31) - Bert JW Regeer (2019-05-31) 0707010000000D000081A4000000000000000000000001637AD73300000424000000000000000000000000000000000000001A00000000plaster-1.1.2/LICENSE.txtCopyright (c) 2017 Michael Merickel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0707010000000E000081A4000000000000000000000001637AD73300000129000000000000000000000000000000000000001A00000000plaster-1.1.2/MANIFEST.ingraft src graft tests graft docs graft .github include README.rst include CHANGES.rst include LICENSE.txt include CONTRIBUTING.rst include CONTRIBUTORS.txt include .coveragerc .flake8 pyproject.toml pytest.ini include tox.ini rtd.txt recursive-exclude * __pycache__ *.py[cod] prune docs/_build 0707010000000F000081A4000000000000000000000001637AD73300000438000000000000000000000000000000000000001900000000plaster-1.1.2/README.rst======= plaster ======= .. image:: https://img.shields.io/pypi/v/plaster.svg :target: https://pypi.python.org/pypi/plaster .. image:: https://github.com/Pylons/plaster/workflows/Build%20and%20test/badge.svg?branch=master :target: https://github.com/Pylons/plaster/actions?query=workflow%3A%22Build+and+test%22 :alt: master CI Status .. image:: https://readthedocs.org/projects/plaster/badge/?version=latest :target: https://readthedocs.org/projects/plaster/?badge=latest :alt: Documentation Status ``plaster`` is a loader interface around multiple config file formats. It exists to define a common API for applications to use when they wish to load configuration. The library itself does not aim to handle anything except a basic API that applications may use to find and load configuration settings. Any specific constraints should be implemented in a pluggable loader which can be registered via an entrypoint. See https://docs.pylonsproject.org/projects/plaster/en/latest/ or ``docs/index.rst`` in this distribution for detailed documentation. 07070100000010000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000001300000000plaster-1.1.2/docs07070100000011000081A4000000000000000000000001637AD733000001F4000000000000000000000000000000000000001D00000000plaster-1.1.2/pyproject.toml[build-system] requires = ["setuptools >= 41"] build-backend = "setuptools.build_meta" [tool.black] target-version = ['py37', 'py38', 'py39', 'py310'] exclude = ''' /( \.git | .tox | docs )/ ''' [tool.isort] profile = "black" multi_line_output = 3 src_paths = ["src", "tests"] skip_glob = ["docs/*"] include_trailing_comma = true force_grid_wrap = false combine_as_imports = true line_length = 88 force_sort_within_sections = true default_section = "THIRDPARTY" known_first_party = "plaster" 07070100000012000081A4000000000000000000000001637AD73300000048000000000000000000000000000000000000001900000000plaster-1.1.2/pytest.ini[pytest] python_files = test_*.py testpaths = src/plaster tests 07070100000013000081A4000000000000000000000001637AD7330000000B000000000000000000000000000000000000001600000000plaster-1.1.2/rtd.txt-e .[docs] 07070100000014000081A4000000000000000000000001637AD733000005B7000000000000000000000000000000000000001800000000plaster-1.1.2/setup.cfg[wheel] universal = 1 [metadata] name = plaster version = 1.1.2 author = Michael Merickel author_email = pylons-discuss@googlegroups.com description = A loader interface around multiple config file formats. keywords = plaster, pastedeploy, ini, config url = https://docs.pylonsproject.org/projects/plaster/en/latest/ long_description = file: README.rst, CHANGES.rst long_description_content_type = text/x-rst classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: MIT License Natural Language :: English Programming Language :: Python :: 3 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy license_files = LICENSE.txt [options] package_dir = = src packages = find: zip_safe = False install_requires = importlib-metadata; python_version<"3.8" include_package_data = True python_requires = >=3.7 [options.packages.find] where = src [options.extras_require] docs = Sphinx pylons-sphinx-themes testing = pytest pytest-cov [check-manifest] ignore = .gitignore PKG-INFO *.egg-info *.egg-info/* ignore-default-rules = true ignore-bad-ideas = tests/** 07070100000015000081A4000000000000000000000001637AD73300000026000000000000000000000000000000000000001700000000plaster-1.1.2/setup.pyfrom setuptools import setup setup() 07070100000016000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000001200000000plaster-1.1.2/src07070100000017000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000001A00000000plaster-1.1.2/src/plaster07070100000018000081A4000000000000000000000001637AD73300000131000000000000000000000000000000000000002600000000plaster-1.1.2/src/plaster/__init__.py# public api # flake8: noqa from .exceptions import InvalidURI, LoaderNotFound, MultipleLoadersFound, PlasterError from .interfaces import ILoader, ILoaderFactory, ILoaderInfo from .loaders import find_loaders, get_loader, get_sections, get_settings, setup_logging from .uri import PlasterURL, parse_uri 07070100000019000081A4000000000000000000000001637AD733000009D0000000000000000000000000000000000000002800000000plaster-1.1.2/src/plaster/exceptions.pyclass PlasterError(Exception): """ A base exception for any error generated by plaster. """ class InvalidURI(PlasterError, ValueError): """ Raised by :func:`plaster.parse_uri` when failing to parse a ``config_uri``. :ivar uri: The user-supplied ``config_uri`` string. """ def __init__(self, uri, message=None): if message is None: message = f'Unable to parse config_uri "{uri}".' super().__init__(message) self.message = message self.uri = uri class LoaderNotFound(PlasterError, ValueError): """ Raised by :func:`plaster.get_loader` when no loaders match the requested ``scheme``. :ivar scheme: The scheme being matched. :ivar protocols: Zero or more :term:`loader protocol` identifiers that were requested when finding a loader. """ def __init__(self, scheme, protocols=None, message=None): if message is None: scheme_msg = f'scheme "{scheme}"' if protocols is not None: scheme_msg += ', protocol "{}"'.format(", ".join(protocols)) message = f"Could not find a matching loader for the {scheme_msg}." super().__init__(message) self.message = message self.scheme = scheme self.protocols = protocols class MultipleLoadersFound(PlasterError, ValueError): """ Raised by :func:`plaster.get_loader` when more than one loader matches the requested ``scheme``. :ivar scheme: The scheme being matched. :ivar protocols: Zero or more :term:`loader protocol` identifiers that were requested when finding a loader. :ivar loaders: A list of :class:`plaster.ILoaderInfo` objects. """ def __init__(self, scheme, loaders, protocols=None, message=None): if message is None: scheme_msg = f'scheme "{scheme}"' if protocols is not None: scheme_msg += ', protocol "{}"'.format(", ".join(protocols)) loader_list = ", ".join( loader.scheme for loader in sorted(loaders, key=lambda v: v.scheme) ) message = ( "Multiple plaster loaders were found for {}. " "Please specify a more specific config_uri. " "Matched loaders: {}" ).format(scheme_msg, loader_list) super().__init__(message) self.message = message self.scheme = scheme self.protocols = protocols self.loaders = loaders 0707010000001A000081A4000000000000000000000001637AD73300000CE9000000000000000000000000000000000000002800000000plaster-1.1.2/src/plaster/interfaces.pyimport abc class ILoader(metaclass=abc.ABCMeta): """ An abstraction over an source of configuration settings. It is required to implement ``get_sections``, ``get_settings`` and ``setup_logging``. Optionally, it may also implement other :term:`loader protocol` interfaces to provide extra functionality. For example, :class:`plaster.protocols.IWSGIProtocol` which requires ``get_wsgi_app``, and ``get_wsgi_server`` for loading WSGI configurations. Services that depend on such functionality should document the required functionality behind a particular :term:`loader protocol` which custom loaders can implement. :ivar uri: The :class:`plaster.PlasterURL` object used to find the :class:`plaster.ILoaderFactory`. """ @abc.abstractmethod def get_sections(self): """ Load the list of section names available. """ @abc.abstractmethod def get_settings(self, section=None, defaults=None): """ Load the settings for the named ``section``. :param section: The name of the section in the config file. If this is ``None`` then it is up to the loader to determine a sensible default usually derived from the fragment in the ``path#name`` syntax of the ``config_uri``. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. :returns: A ``dict`` of settings. This should return a dictionary object even if the section is missing. :raises ValueError: If a section name is missing and cannot be determined from the ``config_uri``. """ @abc.abstractmethod def setup_logging(self, defaults=None): """ Execute the logging configuration defined in the config file. This function should, at least, configure the Python standard logging module. However, it may also be used to configure any other logging subsystems that serve a similar purpose. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. """ class ILoaderFactory(metaclass=abc.ABCMeta): @abc.abstractmethod def __call__(self, uri): """ A factory which accepts a :class:`plaster.PlasterURL` and returns a :class:`plaster.ILoader` object. """ class ILoaderInfo(metaclass=abc.ABCMeta): """ An info object describing a specific :class:`plaster.ILoader`. :ivar scheme: The full scheme of the loader. :ivar protocols: Zero or more supported :term:`loader protocol` identifiers. :ivar factory: The :class:`plaster.ILoaderFactory`. """ @abc.abstractmethod def load(self, config_uri): """ Create and return an :class:`plaster.ILoader` instance. :param config_uri: Anything that can be parsed by :func:`plaster.parse_uri`. """ 0707010000001B000081A4000000000000000000000001637AD73300001BB1000000000000000000000000000000000000002500000000plaster-1.1.2/src/plaster/loaders.pytry: from importlib import metadata except ImportError: # pragma: no cover < py38 import importlib_metadata as metadata from .exceptions import LoaderNotFound, MultipleLoadersFound from .interfaces import ILoaderInfo from .uri import parse_uri def get_sections(config_uri): """ Load the list of named sections. .. code-block:: python sections = plaster.get_sections('development.ini') full_config = { section: plaster.get_settings('development.ini', section) for section in sections } :param config_uri: Anything that can be parsed by :func:`plaster.parse_uri`. :returns: A list of section names in the config file. """ loader = get_loader(config_uri) return loader.get_sections() def get_settings(config_uri, section=None, defaults=None): """ Load the settings from a named section. .. code-block:: python settings = plaster.get_settings(...) print(settings['foo']) :param config_uri: Anything that can be parsed by :func:`plaster.parse_uri`. :param section: The name of the section in the config file. If this is ``None`` then it is up to the loader to determine a sensible default usually derived from the fragment in the ``path#name`` syntax of the ``config_uri``. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. :returns: A ``dict`` of settings. This should return a dictionary object even if no data is available. """ loader = get_loader(config_uri) return loader.get_settings(section, defaults) def setup_logging(config_uri, defaults=None): """ Execute the logging configuration defined in the config file. This function should, at least, configure the Python standard logging module. However, it may also be used to configure any other logging subsystems that serve a similar purpose. :param config_uri: Anything that can be parsed by :func:`plaster.parse_uri`. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. """ loader = get_loader(config_uri) return loader.setup_logging(defaults) def get_loader(config_uri, protocols=None): """ Find a :class:`plaster.ILoader` object capable of handling ``config_uri``. :param config_uri: Anything that can be parsed by :func:`plaster.parse_uri`. :param protocols: Zero or more :term:`loader protocol` identifiers that the loader must implement to match the desired ``config_uri``. :returns: A :class:`plaster.ILoader` object. :raises plaster.LoaderNotFound: If no loader could be found. :raises plaster.MultipleLoadersFound: If multiple loaders match the requested criteria. If this happens, you can disambiguate the lookup by appending the package name to the scheme for the loader you wish to use. For example if ``ini`` is ambiguous then specify ``ini+myapp`` to use the ini loader from the ``myapp`` package. """ config_uri = parse_uri(config_uri) requested_scheme = config_uri.scheme matched_loaders = find_loaders(requested_scheme, protocols=protocols) if len(matched_loaders) < 1: raise LoaderNotFound(requested_scheme, protocols=protocols) if len(matched_loaders) > 1: raise MultipleLoadersFound( requested_scheme, matched_loaders, protocols=protocols ) loader_info = matched_loaders[0] loader = loader_info.load(config_uri) return loader def find_loaders(scheme, protocols=None): """ Find all loaders that match the requested scheme and protocols. :param scheme: Any valid scheme. Examples would be something like ``ini`` or ``pastedeploy+ini``. :param protocols: Zero or more :term:`loader protocol` identifiers that the loader must implement. If ``None`` then only generic loaders will be returned. :returns: A list containing zero or more :class:`plaster.ILoaderInfo` objects. """ # build a list of all required entry points matching_groups = ["plaster.loader_factory"] if protocols: matching_groups += [f"plaster.{proto}_loader_factory" for proto in protocols] scheme = scheme.lower() # if a distribution is specified then it overrides the default search parts = scheme.split("+", 1) if len(parts) == 2: try: dist = metadata.distribution(parts[0]) except metadata.PackageNotFoundError: pass else: ep = _find_ep_in_dist(dist, parts[1], matching_groups) # if we got one or more loaders from a specific distribution # then they override everything else so we'll just return them if ep: return [EntryPointLoaderInfo(dist, ep, protocols)] return [ EntryPointLoaderInfo(dist, ep, protocols=protocols) for (dist, ep) in _iter_ep_in_dists(scheme, matching_groups) ] def _iter_ep_in_dists(scheme, groups): # XXX this is a hack to deduplicate distributions because importlib.metadata # does not do this for us by default, at least up to Python 3.11. # Specifically, if ``lib`` is symlinked to ``lib64`` then a Distribution # object will be returned for each path, causing duplicate entry points # to be found. # # See https://github.com/Pylons/plaster/issues/25 dups = set() for dist in metadata.distributions(): name = dist.metadata["Name"] if name in dups: # pragma: no cover continue dups.add(name) ep = _find_ep_in_dist(dist, scheme, groups) if ep: yield (dist, ep) def _find_ep_in_dist(dist, scheme, groups): entry_points = [ entry_point for entry_point in dist.entry_points if entry_point.group in groups and (scheme is None or scheme == entry_point.name.lower()) ] # verify that the entry point from each group points to the same factory if len({ep.value for ep in entry_points}) == 1: return entry_points[0] class EntryPointLoaderInfo(ILoaderInfo): def __init__(self, dist, ep, protocols=None): self.entry_point = ep self.scheme = "{}+{}".format( dist.metadata["name"] if "name" in dist.metadata else "Unknown", ep.name ) self.protocols = protocols self._factory = None @property def factory(self): if self._factory is None: self._factory = self.entry_point.load() return self._factory def load(self, config_uri): config_uri = parse_uri(config_uri) return self.factory(config_uri) 0707010000001C000081A4000000000000000000000001637AD73300000F07000000000000000000000000000000000000002700000000plaster-1.1.2/src/plaster/protocols.pyimport abc class IWSGIProtocol(metaclass=abc.ABCMeta): @abc.abstractmethod def get_wsgi_app(self, name=None, defaults=None): """ Create a WSGI application object. An example application object may be: .. code-block:: python def app(environ, start_response): start_response(b'200 OK', [(b'Content-Type', b'text/plain')]) yield [b'hello world\\n'] :param name: The name of the application referenced in the config. If ``None`` then it should default to the :attr:`plaster.PlasterURL.fragment`, if available. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. :raises LookupError: If a WSGI application cannot be found by the specified name. """ @abc.abstractmethod def get_wsgi_app_settings(self, name=None, defaults=None): """ Return the settings for a WSGI application. This is similar to :meth:`plaster.ILoader.get_settings` for a WSGI application. :param name: The name of the application referenced in the config. If ``None`` then it should default to the :attr:`plaster.PlasterURL.fragment`, if available. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. :raises LookupError: If a WSGI application cannot be found by the specified name. """ @abc.abstractmethod def get_wsgi_filter(self, name=None, defaults=None): """ Create a composable WSGI middleware object. An example middleware filter may be: .. code-block:: python class Filter(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): return self.app(environ, start_response) :param name: The name of the application referenced in the config. If ``None`` then it should default to the :attr:`plaster.PlasterURL.fragment`, if available. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. :raises LookupError: If a WSGI filter cannot be found by the specified name. """ @abc.abstractmethod def get_wsgi_server(self, name=None, defaults=None): """ Create a WSGI server runner. An example server runner may be: .. code-block:: python def runner(app): from wsgiref.simple_server import make_server server = make_server('0.0.0.0', 8080, app) server.serve_forever() :param name: The name of the application referenced in the config. If ``None`` then it should default to the :attr:`plaster.PlasterURL.fragment`, if available. :param defaults: A ``dict`` of default values used to populate the settings and support variable interpolation. Any values in ``defaults`` may be overridden by the loader prior to returning the final configuration dictionary. :raises LookupError: If a WSGI server cannot be found by the specified name. """ 0707010000001D000081A4000000000000000000000001637AD73300000E98000000000000000000000000000000000000002100000000plaster-1.1.2/src/plaster/uri.pyfrom collections import OrderedDict import os.path from urllib import parse from .exceptions import InvalidURI class PlasterURL: """ Represents the components of a URL used to locate a :class:`plaster.ILoader`. :ivar scheme: The name of the loader backend. :ivar path: The loader-specific path string. This is the entirety of the ``config_uri`` passed to :func:`plaster.parse_uri` without the scheme, fragment and options. If this value is falsey it is replaced with an empty string. :ivar options: A dictionary of options parsed from the query string as url-encoded key=value pairs. :ivar fragment: A loader-specific default section name. This parameter may be used by loaders in scenarios where they provide APIs that support a default name. For example, a loader that provides ``get_wsgi_app`` may use the fragment to determine the name of the section containing the WSGI app if none was explicitly defined. If this value is falsey it is replaced with an empty string. """ def __init__(self, scheme, path="", options=None, fragment=""): self.scheme = scheme if not path: path = "" self.path = path if options is None: options = {} self.options = options if not fragment: fragment = "" self.fragment = fragment def __str__(self): result = "{0.scheme}://{0.path}".format(self) if self.options: result += "?" + parse.urlencode(self.options) if self.fragment: result += "#" + self.fragment return result def __repr__(self): return f"PlasterURL('{self}')" def parse_uri(config_uri): """ Parse the ``config_uri`` into a :class:`plaster.PlasterURL` object. ``config_uri`` can be a relative or absolute file path such as ``development.ini`` or ``/path/to/development.ini``. The file must have an extension that can be handled by a :class:`plaster.ILoader` registered with the system. Alternatively, ``config_uri`` may be a :rfc:`1738`-style string. """ if isinstance(config_uri, PlasterURL): return config_uri # force absolute paths to look like a uri for more accurate parsing # we throw away the dummy scheme later and parse it from the resolved # path extension isabs = os.path.isabs(config_uri) if isabs: config_uri = "dummy://" + config_uri # check if the uri is actually a url parts = parse.urlparse(config_uri) # reconstruct the path without the scheme and fragment path = parse.ParseResult( scheme="", netloc=parts.netloc, path=parts.path, params="", query="", fragment="", ).geturl() # strip off leading // if path.startswith("//"): path = path[2:] if parts.scheme and not isabs: scheme = parts.scheme else: scheme = os.path.splitext(path)[1] if scheme.startswith("."): scheme = scheme[1:] # tag uris coming from file extension as file+scheme if scheme: scheme = "file+" + scheme query = parts.query if parts.query else None options = OrderedDict() if query: options.update(parse.parse_qsl(query)) fragment = parts.fragment if parts.fragment else None if not scheme: raise InvalidURI( config_uri, ( "Could not determine the loader scheme for the supplied " 'config_uri "{}"'.format(config_uri) ), ) return PlasterURL(scheme=scheme, path=path, options=options, fragment=fragment) 0707010000001E000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000001400000000plaster-1.1.2/tests0707010000001F000081A4000000000000000000000001637AD73300000000000000000000000000000000000000000000002000000000plaster-1.1.2/tests/__init__.py07070100000020000081A4000000000000000000000001637AD733000001E7000000000000000000000000000000000000002000000000plaster-1.1.2/tests/conftest.pyimport os.path import sys import pytest @pytest.fixture(scope="session") def fake_packages(): # we'd like to keep this scope more focused but it's proven really # difficult to fully monkeypatch importlib.metadata and so for now we just # install the packages for the duration of the test suite test_dir = os.path.dirname(__file__) for name in ("app1", "app2"): info_dir = os.path.join(test_dir, "fake_packages", name) sys.path.insert(0, info_dir) 07070100000021000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002400000000plaster-1.1.2/tests/example_configs07070100000022000081A4000000000000000000000001637AD73300000052000000000000000000000000000000000000003500000000plaster-1.1.2/tests/example_configs/test_default.ini[DEFAULT] a = 1 b = 2 [main] foo = bar baz = %(a)s [other] foo = baz bar = %(c)s07070100000023000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002200000000plaster-1.1.2/tests/fake_packages07070100000024000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002700000000plaster-1.1.2/tests/fake_packages/app107070100000025000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002C00000000plaster-1.1.2/tests/fake_packages/app1/app107070100000026000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000003500000000plaster-1.1.2/tests/fake_packages/app1/app1.egg-info07070100000027000081A4000000000000000000000001637AD733000000B0000000000000000000000000000000000000003E00000000plaster-1.1.2/tests/fake_packages/app1/app1.egg-info/PKG-INFOMetadata-Version: 1.0 Name: app1 Version: 1.0 Summary: UNKNOWN Home-page: UNKNOWN Author: UNKNOWN Author-email: UNKNOWN License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN 07070100000028000081A4000000000000000000000001637AD733000000B8000000000000000000000000000000000000004100000000plaster-1.1.2/tests/fake_packages/app1/app1.egg-info/SOURCES.txtsetup.py app1/__init__.py app1/loaders.py app1.egg-info/PKG-INFO app1.egg-info/SOURCES.txt app1.egg-info/dependency_links.txt app1.egg-info/entry_points.txt app1.egg-info/top_level.txt07070100000029000081A4000000000000000000000001637AD73300000001000000000000000000000000000000000000004A00000000plaster-1.1.2/tests/fake_packages/app1/app1.egg-info/dependency_links.txt 0707010000002A000081A4000000000000000000000001637AD733000002AC000000000000000000000000000000000000004600000000plaster-1.1.2/tests/fake_packages/app1/app1.egg-info/entry_points.txt[other.loader] ini = app1.loaders:WontBeLoaded [plaster.dummy1_loader_factory] file+ini = app1.loaders:INIWSGILoader ini = app1.loaders:INIWSGILoader [plaster.dummy2_loader_factory] file+ini = app1.loaders:INILoader ini = app1.loaders:INILoader [plaster.loader_factory] bad = app1.loaders:BadLoader broken = app1.loaders.BadLoader conf = app1.loaders:ConfLoader dup = app1.loaders:DuplicateLoader file+conf = app1.loaders:ConfLoader file+ini = app1.loaders:INIWSGILoader file+yaml = app1.loaders:YAMLLoader ini = app1.loaders:INIWSGILoader yaml+foo = app1.loaders:YAMLFooLoader [plaster.wsgi_loader_factory] file+ini = app1.loaders:INIWSGILoader ini = app1.loaders:INIWSGILoader 0707010000002B000081A4000000000000000000000001637AD73300000005000000000000000000000000000000000000004300000000plaster-1.1.2/tests/fake_packages/app1/app1.egg-info/top_level.txtapp1 0707010000002C000081A4000000000000000000000001637AD73300000000000000000000000000000000000000000000003800000000plaster-1.1.2/tests/fake_packages/app1/app1/__init__.py0707010000002D000081A4000000000000000000000001637AD73300000678000000000000000000000000000000000000003700000000plaster-1.1.2/tests/fake_packages/app1/app1/loaders.pyimport plaster from plaster.protocols import IWSGIProtocol _SECTIONS = {"a": {"foo": "bar"}, "b": {"baz": "xyz"}} class LoaderBase(plaster.ILoader): entry_point_key = None def __init__(self, uri): self.uri = uri def get_sections(self): return list(_SECTIONS.keys()) def get_settings(self, section=None, defaults=None): if section is None: section = self.uri.fragment if defaults is not None: result = defaults.copy() else: result = {} try: result.update(_SECTIONS[section]) except KeyError: pass return result def setup_logging(self, defaults=None): self.logging_setup = True self.logging_defaults = defaults class ConfLoader(LoaderBase): entry_point_key = "conf" class INILoader(LoaderBase): entry_point_key = "ini" class INIWSGILoader(IWSGIProtocol, LoaderBase): entry_point_key = "ini+wsgi" def get_wsgi_app(self, name=None, defaults=None): return "wsgi app" def get_wsgi_app_settings(self, name=None, defaults=None): return {"a": "b"} def get_wsgi_filter(self, name=None, defaults=None): return "wsgi filter" def get_wsgi_server(self, name=None, defaults=None): return "wsgi server" class YAMLLoader(LoaderBase): entry_point_key = "yaml" class YAMLFooLoader(LoaderBase): entry_point_key = "yaml+foo" class DuplicateLoader(LoaderBase): entry_point_key = "app1+dup" class BadLoader: def __init__(self, uri): self.uri = uri class WontBeLoaded(LoaderBase): entry_point_key = "ini" 0707010000002E000081A4000000000000000000000001637AD73300000460000000000000000000000000000000000000003000000000plaster-1.1.2/tests/fake_packages/app1/setup.pyfrom setuptools import find_packages, setup setup( name="app1", version="1.0", packages=find_packages(), entry_points={ "plaster.loader_factory": [ "conf=app1.loaders:ConfLoader", "file+conf=app1.loaders:ConfLoader", "ini=app1.loaders:INIWSGILoader", "file+ini=app1.loaders:INIWSGILoader", "file+yaml=app1.loaders:YAMLLoader", "yaml+foo=app1.loaders:YAMLFooLoader", "dup=app1.loaders:DuplicateLoader", "bad=app1.loaders:BadLoader", "broken=app1.loaders.BadLoader", ], "plaster.wsgi_loader_factory": [ "ini=app1.loaders:INIWSGILoader", "file+ini=app1.loaders:INIWSGILoader", ], "plaster.dummy1_loader_factory": [ "ini=app1.loaders:INIWSGILoader", "file+ini=app1.loaders:INIWSGILoader", ], "plaster.dummy2_loader_factory": [ "ini=app1.loaders:INILoader", "file+ini=app1.loaders:INILoader", ], "other.loader": ["ini=app1.loaders:WontBeLoaded"], }, ) 0707010000002F000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002700000000plaster-1.1.2/tests/fake_packages/app207070100000030000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000002C00000000plaster-1.1.2/tests/fake_packages/app2/app207070100000031000041ED000000000000000000000002637AD73300000000000000000000000000000000000000000000003500000000plaster-1.1.2/tests/fake_packages/app2/app2.egg-info07070100000032000081A4000000000000000000000001637AD733000000B0000000000000000000000000000000000000003E00000000plaster-1.1.2/tests/fake_packages/app2/app2.egg-info/PKG-INFOMetadata-Version: 1.0 Name: app2 Version: 1.0 Summary: UNKNOWN Home-page: UNKNOWN Author: UNKNOWN Author-email: UNKNOWN License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN 07070100000033000081A4000000000000000000000001637AD733000000B8000000000000000000000000000000000000004100000000plaster-1.1.2/tests/fake_packages/app2/app2.egg-info/SOURCES.txtsetup.py app2/__init__.py app2/loaders.py app2.egg-info/PKG-INFO app2.egg-info/SOURCES.txt app2.egg-info/dependency_links.txt app2.egg-info/entry_points.txt app2.egg-info/top_level.txt07070100000034000081A4000000000000000000000001637AD73300000001000000000000000000000000000000000000004A00000000plaster-1.1.2/tests/fake_packages/app2/app2.egg-info/dependency_links.txt 07070100000035000081A4000000000000000000000001637AD73300000063000000000000000000000000000000000000004600000000plaster-1.1.2/tests/fake_packages/app2/app2.egg-info/entry_points.txt[plaster.loader_factory] dup = app2.loaders:DuplicateLoader yaml+bar = app2.loaders:YAMLBarLoader 07070100000036000081A4000000000000000000000001637AD73300000005000000000000000000000000000000000000004300000000plaster-1.1.2/tests/fake_packages/app2/app2.egg-info/top_level.txtapp2 07070100000037000081A4000000000000000000000001637AD73300000000000000000000000000000000000000000000003800000000plaster-1.1.2/tests/fake_packages/app2/app2/__init__.py07070100000038000081A4000000000000000000000001637AD73300000366000000000000000000000000000000000000003700000000plaster-1.1.2/tests/fake_packages/app2/app2/loaders.pyimport plaster _SECTIONS = {"a": {"foo": "bar"}, "b": {"baz": "xyz"}} class LoaderBase(plaster.ILoader): entry_point_key = None def __init__(self, uri): self.uri = uri def get_sections(self): return list(_SECTIONS.keys()) def get_settings(self, section=None, defaults=None): if section is None: section = self.uri.fragment if defaults is not None: result = defaults.copy() else: result = {} try: result.update(_SECTIONS[section]) except KeyError: pass return result def setup_logging(self, defaults=None): self.logging_setup = True self.logging_defaults = defaults class YAMLBarLoader(LoaderBase): entry_point_key = "yaml+bar" class DuplicateLoader(LoaderBase): entry_point_key = "app2+dup" 07070100000039000081A4000000000000000000000001637AD73300000123000000000000000000000000000000000000003000000000plaster-1.1.2/tests/fake_packages/app2/setup.pyfrom setuptools import find_packages, setup setup( name="app2", version="1.0", packages=find_packages(), entry_points={ "plaster.loader_factory": [ "dup=app2.loaders:DuplicateLoader", "yaml+bar=app2.loaders:YAMLBarLoader", ] }, ) 0707010000003A000081A4000000000000000000000001637AD733000010D5000000000000000000000000000000000000002700000000plaster-1.1.2/tests/test_exceptions.pyclass TestInvalidURI: def _makeOne(self, *args, **kwargs): from plaster.exceptions import InvalidURI return InvalidURI(*args, **kwargs) def test_it(self): exc = self._makeOne("foo") assert isinstance(exc, ValueError) assert exc.message == 'Unable to parse config_uri "foo".' assert exc.uri == "foo" def test_it_overrides_message(self): exc = self._makeOne("foo", message="bar") assert isinstance(exc, ValueError) assert exc.message == "bar" assert exc.uri == "foo" class TestLoaderNotFound: def _makeOne(self, *args, **kwargs): from plaster.exceptions import LoaderNotFound return LoaderNotFound(*args, **kwargs) def test_it(self): exc = self._makeOne("foo") assert isinstance(exc, ValueError) assert exc.scheme == "foo" assert exc.protocols is None assert exc.message == ('Could not find a matching loader for the scheme "foo".') def test_it_with_protocol(self): exc = self._makeOne("foo", ["wsgi"]) assert isinstance(exc, ValueError) assert exc.scheme == "foo" assert exc.protocols == ["wsgi"] assert exc.message == ( 'Could not find a matching loader for the scheme "foo", ' 'protocol "wsgi".' ) def test_it_with_multiple_protocols(self): exc = self._makeOne("foo", ["wsgi", "qt"]) assert isinstance(exc, ValueError) assert exc.scheme == "foo" assert exc.protocols == ["wsgi", "qt"] assert exc.message == ( 'Could not find a matching loader for the scheme "foo", ' 'protocol "wsgi, qt".' ) def test_it_overrides_message(self): exc = self._makeOne("foo", message="bar") assert isinstance(exc, ValueError) assert exc.scheme == "foo" assert exc.protocols is None assert exc.message == "bar" class TestMultipleLoadersFound: def _makeOne(self, *args, **kwargs): from plaster.exceptions import MultipleLoadersFound return MultipleLoadersFound(*args, **kwargs) def test_it(self): dummy1 = DummyLoaderInfo("dummy1") dummy2 = DummyLoaderInfo("dummy2") exc = self._makeOne("https", [dummy1, dummy2]) assert isinstance(exc, ValueError) assert exc.message == ( 'Multiple plaster loaders were found for scheme "https". ' "Please specify a more specific config_uri. Matched loaders: " "dummy1, dummy2" ) assert exc.scheme == "https" assert exc.protocols is None assert exc.loaders == [dummy1, dummy2] def test_it_with_protocol(self): dummy1 = DummyLoaderInfo("dummy1") dummy2 = DummyLoaderInfo("dummy2") exc = self._makeOne("https", [dummy1, dummy2], protocols=["wsgi"]) assert isinstance(exc, ValueError) assert exc.message == ( 'Multiple plaster loaders were found for scheme "https", ' 'protocol "wsgi". Please specify a more specific config_uri. ' "Matched loaders: dummy1, dummy2" ) assert exc.scheme == "https" assert exc.protocols == ["wsgi"] assert exc.loaders == [dummy1, dummy2] def test_it_with_multiple_protocols(self): dummy1 = DummyLoaderInfo("dummy1") dummy2 = DummyLoaderInfo("dummy2") exc = self._makeOne("https", [dummy1, dummy2], protocols=["wsgi", "qt"]) assert isinstance(exc, ValueError) assert exc.message == ( 'Multiple plaster loaders were found for scheme "https", ' 'protocol "wsgi, qt". Please specify a more specific ' "config_uri. Matched loaders: dummy1, dummy2" ) assert exc.scheme == "https" assert exc.protocols == ["wsgi", "qt"] assert exc.loaders == [dummy1, dummy2] def test_it_overrides_message(self): dummy = object() exc = self._makeOne("https", [dummy], message="foo") assert isinstance(exc, ValueError) assert exc.message == "foo" assert exc.scheme == "https" assert exc.protocols is None assert exc.loaders == [dummy] class DummyLoaderInfo: def __init__(self, scheme): self.scheme = scheme 0707010000003B000081A4000000000000000000000001637AD733000018FE000000000000000000000000000000000000002400000000plaster-1.1.2/tests/test_loaders.pyimport pytest @pytest.mark.usefixtures("fake_packages") class Test_get_loader: def _callFUT(self, *args, **kwargs): from plaster.loaders import get_loader return get_loader(*args, **kwargs) def test_simple_uri(self): loader = self._callFUT("development.conf") assert loader.entry_point_key == "conf" def test_scheme_uri(self): loader = self._callFUT("conf://development.conf") assert loader.entry_point_key == "conf" def test_scheme_uri_for_pkg(self): loader = self._callFUT("app1+conf://") assert loader.entry_point_key == "conf" def test_path_with_extension(self): loader = self._callFUT("development.ini") assert loader.entry_point_key == "ini+wsgi" def test_path_with_extension_and_protocol(self): loader = self._callFUT("development.ini", protocols=["wsgi"]) assert loader.entry_point_key == "ini+wsgi" def test_dup(self): from plaster.exceptions import MultipleLoadersFound with pytest.raises(MultipleLoadersFound): self._callFUT("dup://development.ini") def test_dedup_app1(self): loader = self._callFUT("app1+dup://development.ini") assert loader.entry_point_key == "app1+dup" def test_dedup_app2(self): loader = self._callFUT("app2+dup://development.ini") assert loader.entry_point_key == "app2+dup" def test_other_groups(self): from plaster.exceptions import LoaderNotFound with pytest.raises(LoaderNotFound): self._callFUT("other-scheme://development.ini") def test_bad(self): from app1.loaders import BadLoader loader = self._callFUT("bad:development") assert isinstance(loader, BadLoader) def test_it_broken(self): with pytest.raises(Exception): self._callFUT("development.broken") def test_it_notfound(self): from plaster.exceptions import LoaderNotFound with pytest.raises(LoaderNotFound): self._callFUT("development.notfound") def test_fallback_non_pkg_scheme(self): loader = self._callFUT("yaml+bar://development.yml") assert loader.entry_point_key == "yaml+bar" @pytest.mark.usefixtures("fake_packages") class Test_find_loaders: def _callFUT(self, *args, **kwargs): from plaster.loaders import find_loaders return find_loaders(*args, **kwargs) def test_simple_uri(self): loaders = self._callFUT("conf") assert len(loaders) == 1 assert loaders[0].scheme == "app1+conf" loader = loaders[0].load("development.conf") assert loader.entry_point_key == "conf" def test_case_insensitive_scheme(self): loaders = self._callFUT("CONF") assert len(loaders) == 1 assert loaders[0].scheme == "app1+conf" loader = loaders[0].load("development.conf") assert loader.entry_point_key == "conf" def test_scheme_specific_uri(self): loaders = self._callFUT("ini") assert len(loaders) == 1 assert loaders[0].scheme == "app1+ini" loader = loaders[0].load("development.ini") assert loader.entry_point_key == "ini+wsgi" def test_multiple_yaml_loaders(self): loaders = self._callFUT("dup") assert len(loaders) == 2 schemes = {loader.scheme for loader in loaders} assert "app1+dup" in schemes assert "app2+dup" in schemes def test_one_protocol(self): loaders = self._callFUT("ini", protocols=["wsgi"]) assert len(loaders) == 1 loader = loaders[0].load("development.ini") assert loader.entry_point_key == "ini+wsgi" def test_multiple_protocols(self): loaders = self._callFUT("ini", protocols=["wsgi", "dummy1"]) assert len(loaders) == 1 loader = loaders[0].load("development.ini") assert loader.entry_point_key == "ini+wsgi" def test_multiple_incompatible_protocols(self): loaders = self._callFUT("ini", protocols=["wsgi", "dummy2"]) assert len(loaders) == 0 def test_it_notfound(self): loaders = self._callFUT("notfound") assert len(loaders) == 0 @pytest.mark.usefixtures("fake_packages") class Test_get_sections: def _callFUT(self, config_uri): from plaster.loaders import get_sections return get_sections(config_uri) def test_it(self): result = self._callFUT("development.ini") assert set(result) == {"a", "b"} def test_it_bad(self): with pytest.raises(Exception): self._callFUT("development.bad") @pytest.mark.usefixtures("fake_packages") class Test_get_settings: def _callFUT(self, config_uri, section=None, defaults=None): from plaster.loaders import get_settings return get_settings(config_uri, section=section, defaults=defaults) def test_it_explicit_a(self): result = self._callFUT("development.ini", "a") assert result == {"foo": "bar"} def test_it_explicit_b(self): result = self._callFUT("development.ini", "b") assert result == {"baz": "xyz"} def test_it_fragment(self): result = self._callFUT("development.ini#a") assert result == {"foo": "bar"} def test_defaults(self): result = self._callFUT("development.ini", "a", {"baz": "foo"}) assert result == {"foo": "bar", "baz": "foo"} def test_invalid_section(self): result = self._callFUT("development.ini", "c") assert result == {} def test_it_bad(self): with pytest.raises(Exception): self._callFUT("development.bad") @pytest.mark.usefixtures("fake_packages") class Test_setup_logging: def _makeOne(self, config_uri): from plaster.loaders import get_loader return get_loader(config_uri) def _callFUT(self, config_uri, defaults=None): from plaster.loaders import setup_logging return setup_logging(config_uri, defaults=defaults) def test_it(self): loader = self._makeOne("development.ini#a") loader.setup_logging() assert loader.logging_setup assert loader.logging_defaults is None def test_it_top_level(self): self._callFUT("development.ini#a") def test_it_bad(self): with pytest.raises(Exception): self._callFUT("bad://development.ini") 0707010000003C000081A4000000000000000000000001637AD73300000427000000000000000000000000000000000000002600000000plaster-1.1.2/tests/test_protocols.pydef test_wsgi_protocol(): from plaster.protocols import IWSGIProtocol class WSGILoader(IWSGIProtocol): # pragma: no cover def get_wsgi_app(self, name=None, defaults=None): def app(environ, start_response): start_response(b"200 OK", [(b"Content-Type", b"text/plain")]) return [b"hello world"] return app def get_wsgi_app_settings(self, name=None, defaults=None): settings = defaults.copy() if defaults else {} return settings def get_wsgi_filter(self, name=None, defaults=None): def filter(app): def wrapper(environ, start_response): return app(environ, start_response) return wrapper return filter def get_wsgi_server(self, name=None, defaults=None): def server(app): from wsgiref.simple_server import make_server server = make_server("0.0.0.0", 8080, app) server.serve_forever() WSGILoader() 0707010000003D000081A4000000000000000000000001637AD73300000BE6000000000000000000000000000000000000002000000000plaster-1.1.2/tests/test_uri.pyimport os.path import pytest class TestURL: def _callFUT(self, uri): from plaster.uri import parse_uri return parse_uri(uri) def test_relative_path(self): uri = self._callFUT("development.ini") assert uri.scheme == "file+ini" assert uri.path == "development.ini" assert uri.options == {} assert uri.fragment == "" def test_absolute_path(self): path = os.path.abspath("/path/to/development.ini") uri = self._callFUT(path) assert uri.scheme == "file+ini" assert uri.path == path assert uri.options == {} assert uri.fragment == "" def test_absolute_path_with_fragment(self): path = os.path.abspath("/path/to/development.ini") uri = self._callFUT(path + "?a=b&c=d#main") assert uri.scheme == "file+ini" assert uri.path == path assert uri.options == {"a": "b", "c": "d"} assert uri.fragment == "main" def test_url(self): uri = self._callFUT("redis://username@password:localhost/foo?a=b#main") assert uri.scheme == "redis" assert uri.path == "username@password:localhost/foo" assert uri.options == {"a": "b"} assert uri.fragment == "main" def test_url_for_file(self): uri = self._callFUT("pastedeploy+ini://development.ini") assert uri.scheme == "pastedeploy+ini" assert uri.path == "development.ini" assert uri.options == {} assert uri.fragment == "" def test_missing_scheme(self): from plaster.exceptions import InvalidURI with pytest.raises(InvalidURI): self._callFUT("foo") def test___str__(self): uri = self._callFUT("development.ini") assert str(uri) == "file+ini://development.ini" def test___str___with_options(self): uri = self._callFUT("development.ini?a=b&c=d") assert str(uri) == "file+ini://development.ini?a=b&c=d" def test___str___with_fragment(self): uri = self._callFUT("development.ini#main") assert str(uri) == "file+ini://development.ini#main" def test___repr___(self): uri = self._callFUT("development.ini#main") assert repr(uri) == "PlasterURL('file+ini://development.ini#main')" def test_returns_same_instance(self): uri1 = self._callFUT("development.ini") uri2 = self._callFUT(uri1) assert uri1 is uri2 def test_colon_prefix_scheme(self): uri = self._callFUT("egg:myapp#main") assert uri.scheme == "egg" assert uri.path == "myapp" assert uri.fragment == "main" def test_only_scheme(self): uri = self._callFUT("egg:") assert uri.scheme == "egg" assert uri.path == "" assert uri.options == {} assert uri.fragment == "" def test_default_url_values(): from plaster.uri import PlasterURL url = PlasterURL("foo") assert url.scheme == "foo" assert url.path == "" assert url.options == {} assert url.fragment == "" 0707010000003E000081A4000000000000000000000001637AD7330000056F000000000000000000000000000000000000001600000000plaster-1.1.2/tox.ini[tox] envlist = lint, py37,py38,py39,py310,py311,pypy3, docs,coverage [testenv] commands = py.test --cov --cov-report= {posargs:} extras = testing setenv = COVERAGE_FILE=.coverage.{envname} [testenv:coverage] skip_install = true commands = coverage combine coverage xml coverage report --fail-under=100 deps = coverage setenv = COVERAGE_FILE=.coverage [testenv:docs] whitelist_externals = make commands = make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E" extras = docs [testenv:lint] skip_install = True commands = isort --check-only --df src/plaster tests black --check --diff . flake8 src/plaster tests check-manifest # build sdist/wheel python -m build . twine check dist/* deps = black build check-manifest flake8 flake8-bugbear isort readme_renderer twine [testenv:format] skip_install = true commands = isort src/plaster tests black . deps = black isort [testenv:build] skip_install = true commands = # clean up build/ and dist/ folders python -c 'import shutil; shutil.rmtree("build", ignore_errors=True)' # Make sure we aren't forgetting anything check-manifest # build sdist/wheel python -m build . # Verify all is well twine check dist/* deps = build check-manifest readme_renderer twine 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!148 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor