pax_global_header00006660000000000000000000000064146015306660014521gustar00rootroot0000000000000052 comment=bb04265ef73c47cff027b6c747d9c42f4da85b05 listparser-0.20/000077500000000000000000000000001460153066600136325ustar00rootroot00000000000000listparser-0.20/.editorconfig000066400000000000000000000002651460153066600163120ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [{*.yaml,*.yml}] indent_size = 2 listparser-0.20/.github/000077500000000000000000000000001460153066600151725ustar00rootroot00000000000000listparser-0.20/.github/FUNDING.yml000066400000000000000000000000471460153066600170100ustar00rootroot00000000000000github: "kurtmckee" ko_fi: "kurtmckee" listparser-0.20/.github/dependabot.yml000066400000000000000000000002731460153066600200240ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: github-actions: patterns: - "*" listparser-0.20/.github/workflows/000077500000000000000000000000001460153066600172275ustar00rootroot00000000000000listparser-0.20/.github/workflows/test.yaml000066400000000000000000000161401460153066600210740ustar00rootroot00000000000000name: "Test" on: pull_request: push: branches: - "main" - "releases" jobs: build: name: "Build wheel" runs-on: "ubuntu-latest" outputs: wheel-filename: "${{ steps.get-filename.outputs.wheel-filename }}" steps: - name: "Checkout branch" uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" # v4.1.1 - name: "Setup Python" id: "setup-python" uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" # v5.0.0 with: python-version: "3.12" - name: "Build the project" run: "pip wheel ." - name: "Identify the wheel filename" id: "get-filename" run: | echo "wheel-filename=$(find listparser-*.whl | head -n 1)" >> "$GITHUB_OUTPUT" - name: "Upload the build artifact" uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 with: name: "listparser-${{ github.sha }}.whl" path: "${{ steps.get-filename.outputs.wheel-filename }}" retention-days: 1 test: name: "Test on ${{ matrix.run.os.name }}" runs-on: "${{ matrix.run.os.id }}" needs: "build" strategy: matrix: run: - os: id: "ubuntu-latest" name: "Ubuntu" pythons: | pypy3.9 3.8 3.9 3.10 3.11 3.12 tox-environments: - "py312-http-lxml" - "py311-http-lxml" - "py310-http-lxml" - "py39-http-lxml" - "py38-http-lxml" - "pypy39" # Test lowest and highest versions on Windows. - os: id: "windows-latest" name: "Windows" pythons: | 3.8 3.11 tox-environments: - "py38" - "py311" # Test lowest and highest versions on Mac. - os: name: "MacOS" id: "macos-latest" pythons: | 3.8 3.11 tox-environments: - "py38" - "py311" steps: # The week number is used for cache-busting. - name: "Identify week number" shell: "bash" run: "date +'%V' > week-number.txt" - name: "Checkout branch" uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" # v4.1.1 - name: "Setup Pythons" id: "setup-python" uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" # v5.0.0 with: python-version: "${{ matrix.run.pythons }}" allow-prereleases: true - name: "Restore cache" id: "restore-cache" uses: "actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319" # v4.0.1 with: path: | .tox/ .venv/ key: "test-os=${{ matrix.run.os.id }}-hash=${{ hashFiles('.github/workflows/test.yaml', 'pyproject.toml', 'tox.ini', 'week-number.txt', 'requirements/*/*.txt') }}" - name: "Identify venv path" shell: "bash" run: | echo 'venv-path=${{ runner.os == 'Windows' && '.venv/Scripts' || '.venv/bin' }}' >> "$GITHUB_ENV" - name: "Create a virtual environment" if: "steps.restore-cache.outputs.cache-hit == false" run: | python -m venv .venv ${{ env.venv-path }}/python -m pip install --upgrade pip setuptools wheel ${{ env.venv-path }}/pip install tox - name: "Download the build artifact" uses: "actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85" # v4.1.3 with: name: "listparser-${{ github.sha }}.whl" - name: "Test" run: > ${{ env.venv-path }}/tox run --installpkg "${{ needs.build.outputs.wheel-filename }}" -e ${{ join(matrix.run.tox-environments, ',') }} - name: "Upload coverage data files" uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v4.3.1 with: name: "coverage-data-files-${{ matrix.run.os.id }}" path: ".coverage.*" retention-days: 1 coverage: name: "Calculate code coverage" needs: "test" runs-on: "ubuntu-latest" steps: - name: "Checkout branch" uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" # v4.1.1 - name: "Setup Pythons" id: "setup-python" uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" # v5.0.0 with: python-version: "3.12" - name: "Restore cache" id: "restore-cache" uses: "actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319" # v4.0.1 with: path: ".venv/" key: "coverage-hash=${{ hashFiles('.github/workflows/test.yaml', 'pyproject.toml', 'week-number.txt', 'requirements/*/*.txt') }}" - name: "Create a virtual environment" if: "steps.restore-cache.outputs.cache-hit == false" run: | python -m venv .venv .venv/bin/python -m pip install --upgrade pip setuptools wheel .venv/bin/python -m pip install coverage[toml] - name: "Download coverage data files" uses: "actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85" # v4.1.3 with: pattern: "coverage-data-files-*" merge-multiple: true - name: "Calculate coverage" run: | .venv/bin/coverage combine .venv/bin/coverage report quality: name: "Quality" runs-on: "ubuntu-latest" needs: "build" steps: # The week number is used for cache-busting. - name: "Identify week number" run: "date +'%V' > week-number.txt" - name: "Checkout branch" uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" # v4.1.1 - name: "Setup Pythons" id: "setup-python" uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" # v5.0.0 with: python-version: "3.12" - name: "Restore cache" id: "restore-cache" uses: "actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319" # v4.0.1 with: path: | .mypy_cache/ .tox/ .venv/ key: "lint-hash=${{ hashFiles('.github/workflows/test.yaml', 'pyproject.toml', 'tox.ini', 'week-number.txt', 'requirements/*/*.txt') }}" - name: "Create a virtual environment" if: "steps.restore-cache.outputs.cache-hit == false" run: | python -m venv .venv .venv/bin/python -m pip install --upgrade pip setuptools wheel .venv/bin/pip install tox - name: "Download the build artifact" uses: "actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85" # v4.1.3 with: name: "listparser-${{ github.sha }}.whl" - name: "Lint type annotations" run: > .venv/bin/tox run --installpkg "${{ needs.build.outputs.wheel-filename }}" -e mypy - name: "Lint documentation" run: ".venv/bin/tox run -e docs" listparser-0.20/.gitignore000066400000000000000000000001661460153066600156250ustar00rootroot00000000000000*.pyc MANIFEST dist build *$py.class .tox/ listparser.egg-info/ .cache htmlcov/ .coverage* .idea/ .venv/ /poetry.lock listparser-0.20/.pre-commit-config.yaml000066400000000000000000000032051460153066600201130ustar00rootroot00000000000000ci: autoupdate_schedule: "quarterly" default_language_version: python: "python3.12" repos: - repo: "meta" hooks: - id: "check-hooks-apply" - id: "check-useless-excludes" - repo: "https://github.com/pre-commit/pre-commit-hooks" rev: "v4.5.0" hooks: - id: "check-added-large-files" - id: "check-merge-conflict" - id: "check-yaml" - id: "end-of-file-fixer" - id: "mixed-line-ending" args: - "--fix=lf" - id: "trailing-whitespace" - repo: "https://github.com/asottile/pyupgrade" rev: "v3.15.2" hooks: - id: "pyupgrade" name: "Enforce Python 3.8+ idioms" args: - "--py38-plus" - repo: "https://github.com/psf/black-pre-commit-mirror" rev: "24.3.0" hooks: - id: "black" - repo: "https://github.com/pycqa/isort" rev: "5.13.2" hooks: - id: "isort" - repo: "https://github.com/pycqa/flake8" rev: "7.0.0" hooks: - id: "flake8" additional_dependencies: - "flake8-bugbear==24.2.6" - repo: "https://github.com/editorconfig-checker/editorconfig-checker.python" rev: "2.7.3" hooks: - id: "editorconfig-checker" - repo: "https://github.com/python-jsonschema/check-jsonschema" rev: "0.28.0" hooks: - id: "check-dependabot" - id: "check-github-workflows" - id: "check-readthedocs" - repo: "https://github.com/rhysd/actionlint" rev: "v1.6.27" hooks: - id: "actionlint" - repo: "https://github.com/kurtmckee/pre-commit-hooks" rev: "v0.1.1" hooks: - id: "verify-consistent-pyproject-toml-python-requirements" listparser-0.20/.readthedocs.yaml000066400000000000000000000003541460153066600170630ustar00rootroot00000000000000# Configure ReadTheDocs. version: 2 build: os: "ubuntu-22.04" tools: python: "3.12" python: install: - requirements: "requirements/docs/requirements.txt" sphinx: configuration: "docs/conf.py" fail_on_warning: true listparser-0.20/CHANGELOG.rst000066400000000000000000000205231460153066600156550ustar00rootroot00000000000000.. This is the listparser changelog. It is managed and updated by scriv during development. Please do not edit this file directly. Instead, run "scriv create" to create a new changelog fragment file. Changelog ********* Unreleased changes ================== Please see the fragment files in the `changelog.d directory`_. .. _changelog.d directory: https://github.com/kurtmckee/listparser/tree/main/changelog.d .. scriv-insert-here .. _changelog-0.20: 0.20 - 2024-03-29 ================= Python support -------------- * Support Python 3.11 and 3.12. * Drop support for Python 3.7. Added ----- * Add a ``py.typed`` file so mypy can lint listparser use in dependent applications. * Support lxml v5.0.0 while maintaining support for v4.6.2. Changed ------- * Support malformed XML documents by using lxml's HTML parsers. Fixed ----- * Fix the feed URL to the blog entries about listparser. Documentation ------------- * Add the OPML 1.0 and OPML 2.0 specifications to the listparser documentation for posterity. Development ----------- * Randomize the order of listparser's unit tests on each run to help ensure there are no testing interdependencies. * Add black, flake8, and isort as pre-commit hooks. * Add mypy as a tox test environment. * Add pyupgrade as a pre-commit hook and enforce Python 3.8+ syntax. * Python warnings encountered during testing are now escalated to errors. * Move tool dependencies into separate ``requirements.txt`` files. * Add a tox label, ``update``, so it's easy to update tool dependencies. .. _changelog-0.19: 0.19 - 2022-06-04 ================= *"Spring cleaning"* Added ----- * Add ``requests`` as an optional dependency. * Add ``lxml`` as an optional dependency. This is currently the only way to parse malformed XML files. * Add support for Python 3.7 through Python 3.10. Removed ------- * Drop support for CPython 3.6 and lower. * Drop support for IronPython. * Drop support for Jython. * Remove some ancient Jython compatibility code. * HTML entities are no longer automatically injected into the DTD. If ``lxml`` is installed and undeclared HTML entities are encountered, they will be ignored. If lxml is not installed and undeclared HTML entities are encountered, Python's ``xml.sax`` parser will fail to parse the document. Fixed ----- * Fix some coverage messages that were output during testing. * Fix the combined coverage HTML report's paths. Changed ------- * Migrate to a ``src/``-based directory structure. * Migrate to Read the Docs for documentation hosting. * ``parse()`` no longer accepts *etag*, *modified*, or *agent* arguments. * HTTP response information is no longer available in the return dictionary. * The unit tests no longer launch an HTTP server. * Coverage data is now erased before the unit tests begin. * ``bozo`` is now ``True`` or ``False``, not ``1`` or ``0``. * ``bozo_exception`` is guaranteed to exist in the return dict. * The RFC 822 parser now returns timezone-aware ``datetime`` objects instead of converting to UTC and losing timezone info. * Move and split ``lptest.py`` into the ``tests/`` subdirectory and into specific ``test_*`` files. * Increased test coverage reporting to include test runners. * Decreased the number of ``tox`` environments where listparser must be built and installed. * Internally rely on ``dict`` key-based lookups instead of ``SuperDict`` attribute-based lookups. * Cache XML parsing method lookup results to avoid excessive string formatting. * Change the name of the ``ListError`` to ``ListparserError``. * Split the codebase into multiple files. * Use ``tox`` to automate testing across multiple interpreters and versions. * Migrate to ``pytest`` for unit testing. * Remove dependence on the ``six`` package. * Add type annotations. * Remove compatibility code. * Migrate to Poetry and ``pyproject.toml`` for project configuration. * Change the license from LGPLv3 to MIT. * Use scriv to manage the CHANGELOG. 0.18 - 2015-04-21 ================= * Replace the regex RFC 822 date parser with procedural code. 0.17 - 2012-12-16 ================= *"Territory expansion"* * Python 3.3 is now tested and supported! * PyPy is now tested and supported! * Jython 2.5.2 and 2.5.3 are now tested and supported! * Python 2 and 3 are now supported without 2to3 conversion * Remove the hack to work around Jython bug 1375. (This means that Jython 2.5.1 is no longer supported.) * Support single-digit days in RFC822 dates 0.16 - 2011-12-17 ================= *"Refresh"* * Python 3.2 is now supported! * Made setup.py auto-convert listparser using 2to3 if necessary. * Switched to absolute URLs in the HTTP redirect tests. 0.15 - 2010-11-15 ================= *"A special day"* * IronPython 2.6.2 is now supported! 0.14 - 2010-10-22 ================= *"A good year"* * Added support for LiveJournal FOAF files. * Improved the documentation. * Improved the code quality. 0.13 - 2010-02-01 ================= *"Revelations"* * Fixed an infinite loop bug in Injector. * Fixed a threading-related bug in the unit tests. * Made Injector inject after the first '>', not '\n'. * Overhauled and modularized the unit test code. * Increased the code coverage of the unit tests. 0.12 - 2010-01-03 ================= *"Safety net"* * Fixed global USER_AGENT behavior. * Fixed several crasher bugs. * Fixed a 2to3 tool warning in lptest.py. * Made lptest.py return a status code to the shell. 0.11 - 2009-12-25 ================= *"Floodgates"* * Jython 2.5.1 is now supported! * Added support for opening relative and absolute filenames. 0.10 - 2009-12-12 ================= *"Internet-ready"* * Python 3 is now supported! * Correctly interpret undeclared HTML character entities. * Significantly sped up large RDF+FOAF document parsing. * Fixed RFC 822 date and time creation bug. * Fixed RFC 822 crasher bugs. * Fixed iGoogle-related crasher bug. * Refreshed and added to documentation. * Added many more tests. 0.9 - 2009-10-03 ================ *"Celery wolves"* * Support RDF+FOAF! * Capture opportunity URLs. * Added duplicate URL detection. * Added distutils support for easier distribution. 0.8 - 2009-09-03 ================ *"Three day weekend"* * Support the iGoogle exported settings format! * Support Liferea's version of subscription lists in OPML. * Removed ``feeds[i].claims``. * Removed almost all of listparser's bozo warnings. 0.7 - 2009-08-28 ================ *"The Codex"* * Added documentation! * Unified feed and subscription list code. * Extended category and tag support to subscription lists. * Result dictionary keys are now also attributes (i.e. ``result['meta']['title']`` -> ``result.meta.title``). * Feed and list titles are no longer filled with the associated URL if the title is not found. 0.6 - 2009-08-07 ================ *"Hatchet Hotel"* * Certain return result elements are now guaranteed. * ``bozo_detail`` has been renamed ``bozo_exception``. * Better support for Wordpress' wp-links-opml.php output. * Added 22 new tests (and modified several others). 0.5 - 2009-08-01 ================ *"Going green"* * Send a (configurable) User-Agent header. * Support HTTP ETag and Last-Modified headers. * Support HTTP redirects and errors. * Support parsing of strings and file-like objects (not just URLs). * The subscription list title is now stripped of whitespace. * Added 11 more tests. 0.4 - 2009-07-18 ================ *"07/18,29"* * Support categories and tags specified in ``@category``. * Support categorization using nested ```` tags. * Added 21 more tests. 0.3 - 2009-07-03 ================ *"...and Recursion for all."* * The feed key ``name`` is now ``title``. * Additional optional attributes supported. * Support subscription list inclusions. * Added 13 more tests. 0.2 - 2009-06-26 ================ *"Leveling up"* * RFC 822 date and time support added (+39 tests). * Added more thorough OPML version attribute detection (+5 tests). * ``dateModified`` and ``dateCreated`` OPML tags supported (+4 tests). * Added test cases for existing functionality (+2 tests). * ```` ``htmlUrl`` attribute support added (+1 test). 0.1 - 2009-06-19 ================ *"Achievement unlocked"* * Initial release. listparser-0.20/LICENSE.txt000066400000000000000000000021061460153066600154540ustar00rootroot00000000000000MIT License Copyright 2009-2024 Kurt McKee 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. listparser-0.20/README.rst000066400000000000000000000065161460153066600153310ustar00rootroot00000000000000listparser ========== *Parse OPML subscription lists in Python.* ------------------------------------------------------------------------------- If you're building a feed reader and you need to parse OPML subscription lists, you've come to the right place! listparser makes it easy to parse and use subscription lists in multiple formats. It supports OPML, RDF+FOAF, and the iGoogle exported settings format, and runs on Python 3.8+ and on PyPy 3.8. Usage ===== .. code-block:: pycon >>> import listparser >>> result = listparser.parse(open("feeds.opml").read()) A dictionary will be returned with several keys: * ``meta``: a dictionary of information about the subscription list * ``feeds``: a list of feeds * ``lists``: a list of subscription lists * ``version``: a format identifier like "opml2" * ``bozo``: True if there is a problem with the list, False otherwise * ``bozo_exception``: (if ``bozo`` is 1) a description of the problem For convenience, the result dictionary supports attribute access for its keys. Continuing the example: .. code-block:: pycon >>> result.meta.title 'listparser project feeds' >>> len(result.feeds) 2 >>> result.feeds[0].title, result.feeds[0].url ('listparser blog', 'https://kurtmckee.org/tag/listparser') More extensive documentation is available in the ``docs/`` directory `and online `_. Bugs ==== There are going to be bugs. The best way to handle them will be to isolate the simplest possible document that susses out the bug, add that document as a test case, and then find and fix the problem. ...you can also just report the bug and leave it to someone else to fix the problem, but that won't be as much fun for you! `Bugs can be reported on GitHub `_. Git workflow ============ listparser basically follows the git-flow methodology: * Features and changes are developed in branches off the ``main`` branch. They merge back into the ``main`` branch. * Feature releases branch off the ``main`` branch. The project metadata is updated (like the version and copyright years), and then the release branch merges into the ``releases`` branch. The ``releases`` branch is then tagged, and then it is merged back into ``main``. * Hotfixes branch off the ``releases`` branch. As with feature releases, the project metadata is updated, the hotfix branch merges back into the ``releases`` branch, which is then tagged and merged back into ``main``. Development =========== To set up a development environment, follow these steps at a command line: .. code-block:: shell # Set up a virtual environment. python -m venv .venv # Activate the virtual environment in Linux: . .venv/bin/activate # ...or in Windows Powershell: & .venv/Scripts/Activate.ps1 # Install dependencies. python -m pip install -U pip setuptools wheel python -m pip install poetry pre-commit tox scriv poetry install --all-extras # Enable pre-commit. pre-commit install # Run the unit tests. tox When submitting a PR, be sure to create and edit a changelog fragment. .. code-block:: shell scriv create The changelog fragment will be created in the ``changelog.d/`` directory. Edit the file to describe the changes you've made. listparser-0.20/changelog.d/000077500000000000000000000000001460153066600160035ustar00rootroot00000000000000listparser-0.20/changelog.d/fragment-template.rst.txt000066400000000000000000000016711460153066600227740ustar00rootroot00000000000000.. Please select the correct category for the change you are making. 1. Remove all lines before and after the chosen category including these instructions and the divider lines that use tildes ("~"). 2. Remove the four spaces in front of the remaining text. 3. Describe your change using active verbs and full sentences. For example, write "Support a new feature" instead of "Supports a new feature" or "A new feature is supported". 4. If your change is associated with an issue on GitHub, list the issue number(s) in parentheses, separated by commas. For example: Support a new feature. (#10, #20) {% for category in config.categories %} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {{ category }} {{ config.rst_header_chars[1] * (category|length) }} * Describe your "{{ category }}" change here. EDIT ME! {% endfor -%} listparser-0.20/docs/000077500000000000000000000000001460153066600145625ustar00rootroot00000000000000listparser-0.20/docs/changelog.rst000066400000000000000000000000361460153066600172420ustar00rootroot00000000000000.. include:: ../CHANGELOG.rst listparser-0.20/docs/conf.py000066400000000000000000000024071460153066600160640ustar00rootroot00000000000000import os import pathlib import sys if sys.version_info[:2] >= (3, 11): import tomllib else: import tomli as tomllib # Allow autodoc to import listparser. sys.path.append(os.path.abspath("../src")) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ["sphinx.ext.autodoc"] # The suffix of source filenames. source_suffix = ".rst" # The master toctree document. master_doc = "index" # General information about the project. project = "listparser" copyright = "2009-2024 Kurt McKee" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # pyproject = pathlib.Path(__file__).parent.parent / "pyproject.toml" info = tomllib.loads(pyproject.read_text()) version = release = info["tool"]["poetry"]["version"] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = () # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # HTML theme configuration # ------------------------ html_theme = "alabaster" listparser-0.20/docs/detection-algorithm.rst000066400000000000000000000034621460153066600212630ustar00rootroot00000000000000Feed and subscription list detection algorithm ============================================== .. note:: Neither RDF+FOAF nor the iGoogle exported settings formats support embedded subscription lists. This detection algorithm only applies to OPML subscription lists. Many services and softwares output OPML subscription lists with slight variations. listparser attempts to correctly determine whether an ``outline`` element is attempting to reference a feed or subscription list. As an example, although feeds should be indicated using ``type="rss"`` (no matter the format), some services refer to feeds using ``type="link"``, others indicate that the feed is Atom-formatted using ``type="pie"``, and still others fail to include a ``type`` attribute at all! Therefore it is necessary to make educated guesses. Feed detection -------------- If the ``outline`` element contains an ``xmlUrl`` attribute, the ``outline`` is assumed to represent a feed. listparser will also accept ``xmlurl``, or ``xmlURL``, or indeed any capitalization combination. The ``type`` is ignored entirely in making the decision, with one notable exception (see below). Subscription list detection --------------------------- If the ``outline`` element has a ``url`` attribute and ``type="link"`` or ``type="include"``, listparser assumes that it has found a subscription list. Additionally, if it has an ``xmlUrl`` attribute and ``type="source"``, it is also considered a subscription list. Although the "include" value was only introduced in OPML 2.0, listparser ignores the OPML version entirely. Additionally, despite the OPML 2.0 requirement that the ``url`` value of "link" ``outline`` elements must end in ".opml" to be considered an OPML file, listparser ignores the URL suffix when detecting whether the element is a subscription list. listparser-0.20/docs/getting-started.rst000066400000000000000000000052741460153066600204310ustar00rootroot00000000000000Getting started =============== Use pip to install listparser. To support retrieving and parsing of URL's, add ``[http]`` to the command so the requests package will be installed as well. To parse broken XML, add ``[lxml]`` to the command so the lxml package will be installed as well. .. code-block:: shell python -m pip install listparser[http,lxml] You don't have to install lxml or requests but it will reduce listparser's functionality. listparser has a single public function, :py:func:`~listparser.parse`. .. code-block:: pycon >>> import listparser >>> result = listparser.parse("https://raw.githubusercontent.com/kurtmckee/listparser/main/feeds.opml") :py:func:`~listparser.parse` can be given a URL, an open file handle, or even an in-memory string. The dictionary that :py:func:`~listparser.parse` returns will contain several important keys. The ``meta`` key ---------------- The ``meta`` key contains a dictionary of information about the subscription list, including its title, when it was created and last modified, and who maintains the subscription list. .. code-block:: pycon >>> result.meta.title 'listparser project feeds' >>> result.meta.author.keys() dict_keys(['name', 'email', 'url']) The ``feeds`` key ----------------- The ``feeds`` key is a list of dictionaries. The title and the URL are stored in keys of the same names. .. code-block:: pycon >>> for feed in result.feeds: ... print(f"{feed.title} <{feed.url}>") ... listparser blog listparser changelog The ``lists`` key ----------------- OPML subscription lists can point to other subscription lists as easily as they can point to feeds. These subscription lists are placed in the ``lists`` key, and have the same title and URL information that feeds do. The ``opportunities`` key ------------------------- Several subscription list formats can contain not just a feed URL but the feed's homepage URL as well. Some software butchers the OPML or RDF+FOAF file creation and outputs only the feed's homepage URL. When listparser encounters a homepage URL without a corresponding feed URL, it puts that information into the ``opportunities`` key. Opportunities contain the same title and URL information as feeds do, but remember that the URLs are expected to point to a homepage. It is therefore expected that feed readers using listparser will have to run feed and subscription list autodiscovery software against the list of opportunity URLs. .. seealso:: :doc:`reference/meta`, :doc:`reference/feeds`, :doc:`reference/lists`, :doc:`reference/opportunities` listparser-0.20/docs/http-features.rst000066400000000000000000000010751460153066600201120ustar00rootroot00000000000000HTTP Features ============= If the requests package is installed, listparser will be able to accept HTTP and HTTPS URL's for parsing. For example: .. code-block:: pycon >>> result = listparser.parse("https://domain.example/feeds.opml") A 30-second timeout is set on all requests. If requests is not installed, listparser will return a dictionary with the following data: .. code-block:: pycon >>> listparser.parse("https://domain.example/feeds.opml") { 'bozo': 1, 'bozo_exception': ListparserError('requests is not installed...') } listparser-0.20/docs/index.rst000066400000000000000000000017161460153066600164300ustar00rootroot00000000000000listparser - Parse subscription lists in Python =============================================== If you're building a feed reader and you need to import OPML subscription lists, you've come to the right place! listparser makes it easy to parse and use subscription lists in multiple formats. It supports OPML, RDF+FOAF, and the iGoogle exported settings format, and runs on Python 3.8+ and on PyPy 3.8. Contents -------- .. toctree:: :maxdepth: 1 changelog .. toctree:: :maxdepth: 2 getting-started http-features detection-algorithm reference/objects Reference --------- .. toctree:: :maxdepth: 1 reference/parse .. toctree:: :maxdepth: 2 reference/bozo reference/bozo_exception reference/feeds reference/lists reference/meta reference/opportunities reference/version Specifications -------------- .. toctree:: :maxdepth: 1 specifications/opml-1.0 specifications/opml-2.0 listparser-0.20/docs/reference/000077500000000000000000000000001460153066600165205ustar00rootroot00000000000000listparser-0.20/docs/reference/bozo.rst000066400000000000000000000004021460153066600202170ustar00rootroot00000000000000bozo ==== The ``bozo`` key can be either ``False`` or ``True``. If it is ``True``, it indicates that there was an error or exception during parsing. In such cases the exception can be found in the ``bozo_exception`` key. .. seealso:: :doc:`bozo_exception` listparser-0.20/docs/reference/bozo_exception.rst000066400000000000000000000004631460153066600223040ustar00rootroot00000000000000bozo_exception ============== listparser should never propagate an exception to the software that called it; instead it will store the exception in the ``bozo_exception`` key of the result it returns. Developers may wish to check for exceptions, but it should not be a necessity. .. seealso:: :doc:`bozo` listparser-0.20/docs/reference/feeds.rst000066400000000000000000000004431460153066600203410ustar00rootroot00000000000000feeds ===== The ``feeds`` attribute contains a list of all of the feeds in the subscription list. .. seealso:: * :doc:`feeds[i].categories ` * :doc:`feeds[i].tags ` * :doc:`feeds[i].title ` * :doc:`feeds[i].url ` listparser-0.20/docs/reference/lists.rst000066400000000000000000000004611460153066600204110ustar00rootroot00000000000000lists ===== The ``lists`` attribute is a list of all of the subscription lists in the parsed subscription list. .. seealso:: * :doc:`lists[i].categories ` * :doc:`lists[i].tags ` * :doc:`lists[i].title ` * :doc:`lists[i].url ` listparser-0.20/docs/reference/meta-author-email.rst000066400000000000000000000004571460153066600225730ustar00rootroot00000000000000meta.author.email ================= The email address of the author/maintainer of the subscription list. No attempt is made to verify that the value is a valid email address. However, leading and trailing whitespace characters are stripped off. .. rubric:: Comes from * ``/opml/head/ownerEmail`` listparser-0.20/docs/reference/meta-author-name.rst000066400000000000000000000003741460153066600224220ustar00rootroot00000000000000meta.author.name ================ The name of the author/maintainer of the subscription list. No normalization is performed, although leading and trailing whitespace characters are stripped off. .. rubric:: Comes from * ``/opml/head/ownerName`` listparser-0.20/docs/reference/meta-author-url.rst000066400000000000000000000004321460153066600222770ustar00rootroot00000000000000meta.author.url ================= The webpage of the author/maintainer of the subscription list. No attempt is made to verify that the value is a valid URL. However, leading and trailing whitespace characters are stripped off. .. rubric:: Comes from * ``/opml/head/ownerId`` listparser-0.20/docs/reference/meta-created.rst000066400000000000000000000005461460153066600216120ustar00rootroot00000000000000meta.created ============ A string representing the date and time at which the subscription list was originally created. The string should be in RFC 822 date/time format, with the exception that the year may be four digits long instead of just two. .. seealso:: * :doc:`meta-created_parsed` .. rubric:: Comes from * ``/opml/head/dateCreated`` listparser-0.20/docs/reference/meta-created_parsed.rst000066400000000000000000000005611460153066600231450ustar00rootroot00000000000000meta.created_parsed =================== A Python ``datetime`` object representing the date and time at which the subscription list was originally created. The ``datetime`` object is timezone-aware. ``meta.created_parsed`` is parsed from the ``meta.created`` string. .. seealso:: * :doc:`meta-created` .. rubric:: Comes from * ``/opml/head/dateCreated`` listparser-0.20/docs/reference/meta-modified.rst000066400000000000000000000005451460153066600217620ustar00rootroot00000000000000meta.modified ============= A string representing the date and time at which the subscription list was last modified. The string should be in RFC 822 date/time format, with the exception that the year may be four digits long instead of just two. .. seealso:: * :doc:`meta-modified_parsed` .. rubric:: Comes from * ``/opml/head/dateModified`` listparser-0.20/docs/reference/meta-modified_parsed.rst000066400000000000000000000005621460153066600233170ustar00rootroot00000000000000meta.modified_parsed ==================== A Python ``datetime`` object representing the date and time at which the subscription list was last modified. The ``datetime`` object is timezone-aware. ``meta.modified_parsed`` is parsed from the ``meta.modified`` string. .. seealso:: * :doc:`meta-modified` .. rubric:: Comes from * ``/opml/head/dateModified`` listparser-0.20/docs/reference/meta.rst000066400000000000000000000004001460153066600201720ustar00rootroot00000000000000meta ==== The ``meta`` attribute contains all of the information about the subscription list, including its title, when it was created or last updated, and information about its author or maintainer. .. toctree:: :glob: :maxdepth: 1 meta-* listparser-0.20/docs/reference/object-categories.rst000066400000000000000000000022341460153066600226440ustar00rootroot00000000000000objects[i].categories ===================== A list of all of the categories associated with the feed, subscription list, or opportunity object. Categories are hierarchical, and are represented as lists of strings. listparser uses the hierarchical groupings in the subscription list in order to assign categories. Here is an example in OPML: .. code-block:: xml In this example, the category hierarchy is ``["news", "sports"]``. In the OPML format, listparser will also use the ``category`` attribute of the ``opml:outline`` element as a source of categorization information. .. seealso:: :doc:`object-tags` .. rubric:: Comes from * ``/opml/body//outline/@category`` [#slashes]_ * ``/opml/body//outline/ancestor::outline/@text`` * ``/opml/body//outline/ancestor::outline/@title`` * ``/gtml:GadgetTabML//gtml:Tab/@title`` * ``/rdf:RDF/foaf:Group/foaf:name`` * ``/rdf:RDF//foaf:Agent/foaf:name`` .. rubric:: Footnotes .. [#slashes] The ``category`` attribute is a comma-separated string. listparser-0.20/docs/reference/object-tags.rst000066400000000000000000000015321460153066600214550ustar00rootroot00000000000000objects[i].tags =============== A list of all of the tags associated with the feed, subscription list, or opportunity object. They are copied from the ``categories`` key if the category is one level deep. As an example: .. code-block:: xml In this example, there is one tag: ``sports``. .. seealso:: :doc:`object-categories` .. rubric:: Comes from * ``/opml/body//outline/@category`` [#noslashes]_ * ``/opml/body/outline/outline/parent::outline/@text`` * ``/opml/body/outline/outline/parent::outline/@title`` * ``/gtml:GadgetTabML//gtml:Tab/@title`` * ``/rdf:RDF/foaf:Group/foaf:name`` * ``/rdf:RDF//foaf:Agent/foaf:name`` .. rubric:: Footnotes .. [#noslashes] The ``category`` attribute is a comma-separated string. listparser-0.20/docs/reference/object-title.rst000066400000000000000000000024301460153066600216360ustar00rootroot00000000000000objects[i].title ================ The title of the feed, subscription list, or opportunity object. It is not normalized except for the stripping of whitespace. .. note:: The ``opml:outline`` element's ``text`` and ``title`` attributes differ in function. Many feed readers keep track of the feed's original title while allowing users to change the title that they see. When their subscription list is exported, the ``opml:outline`` element's ``text`` attribute should contain the user's preferred title, and the ``title`` attribute should contain the original title. In practice, however, some software doesn't use the ``text`` attribute at all. Therefore, ``feeds[i].title`` is filled from the ``text`` attribute or, if that isn't available, the ``title`` attribute. For example: .. code-block:: xml And here is the title returned by listparser: .. code-block:: pycon >>> result.feeds[0].title '*text* attribute' .. rubric:: Comes from * ``/opml/body//outline/@text`` * ``/opml/body//outline/@title`` * ``/rdf:RDF//foaf:Agent/foaf:name`` * ``/rdf:RDF//foaf:Agent/foaf:member_name`` listparser-0.20/docs/reference/object-url.rst000066400000000000000000000015071460153066600213230ustar00rootroot00000000000000objects[i].url ============== The URL of the feed, subscription list, or opportunity object. It is not normalized beyond the stripping of whitespace. Due to the ubiquity of format-agnostic feed parsers, there is nothing to indicate what format the target feed is presented in. .. rubric:: Feed URLs come from * ``/opml/body//outline/@xmlUrl`` * ``/gtml:GadgetTabML//iGoogle:Module[@type="RSS"]/iGoogle:ModulePrefs/@xmlUrl`` * ``/rdf:RDF//foaf:Agent//rss:channel/@rdf:about`` * ``/rdf:RDF//ya:feed/@rdf:resource`` .. rubric:: Subscription list URLs come from * ``/opml/body//outline/@url`` * ``/opml/body//outline[type="source"]/@xmlUrl`` * ``/rdf:RDF//rdfs:seeAlso/@rdf:resource`` .. rubric:: Opportunity URLs come from * ``/opml/body//outline/@htmlUrl`` * ``/rdf:RDF//foaf:Agent//foaf:Document/@rdf:about`` listparser-0.20/docs/reference/objects.rst000066400000000000000000000006741460153066600207120ustar00rootroot00000000000000Shared object properties ======================== Feed, subscription list, and opportunity objects share several common properties. Although the content may come from different elements in the source document, the information is stored in consistent attributes. Wherever you see ``objects[i]`` in these documents you can substitute ``feeds[i]``, ``lists[i]``, or ``opportunities[i]``. .. toctree:: :glob: :maxdepth: 1 object-* listparser-0.20/docs/reference/opportunities.rst000066400000000000000000000011151460153066600221740ustar00rootroot00000000000000opportunities ============= The ``opportunities`` attribute contains a list of all of the homepage URLs in the parsed subscription list for which a corresponding feed or subscription list URL was not found. It is expected that feed readers using listparser can choose to run feed and subscription list autodiscovery software against the list of opportunity URLs. .. seealso:: * :doc:`opportunities[i].categories ` * :doc:`opportunities[i].tags ` * :doc:`opportunities[i].title ` * :doc:`opportunities[i].url ` listparser-0.20/docs/reference/parse.rst000066400000000000000000000003401460153066600203610ustar00rootroot00000000000000parse() ======= .. automodule:: listparser :members: parse --------------------------------------------------------------------------- SuperDict --------- .. automodule:: listparser.common :members: SuperDict listparser-0.20/docs/reference/version.rst000066400000000000000000000022101460153066600207320ustar00rootroot00000000000000version ======= ``version`` is a string that represents the format of the subscription list. It always exists, although it may be an empty string if the subscription list format is not recognized. These are the possible values: opml1 :doc:`OPML 1.0 and 1.1 ` [#opml11]_ opml2 :doc:`OPML version 2.0 ` opml An OPML file with an unknown or unspecified version rdf `RDF+FOAF `_ igoogle iGoogle exported settings format [#igoogle]_ .. rubric:: Footnotes .. [#opml11] `OPML 1.1 files are treated as OPML 1.0 files. `_ In 2001, Dave Winer announced that OPML 1.1 would include a ```` tag. In 2005, the original announcement was modified to state that the project was dropped, and included this message: [If] you see an OPML 1.1 file, you should treat it like an OPML 1.0 file. That's it. Enjoy! .. [#igoogle] `How to export your iGoogle page settings `_ listparser-0.20/docs/specifications/000077500000000000000000000000001460153066600175655ustar00rootroot00000000000000listparser-0.20/docs/specifications/no-text-attributes.png000066400000000000000000000253011460153066600240560ustar00rootroot00000000000000‰PNG  IHDRÖw×RqN„ò>‹‡ø>º÷¾O¼q✣œ-Eds¹R§§§"òŽó†ú;œœˆÈ»Ì% \ø y¹À›Á?|Àñññòðð¿/ÿâÏÿlùë—¿¹à¿þ—?]~÷W¿<æ:†ÊÜÝÙYîÿå_.÷×3xÌupÇwËdþ.e¾-‹Åñòøèh´óòyÁжšŒù7×ó¶¿ÁM¦•ÀMÀN0ý§ÿo–ÿì_þãå?ú‡ÿtù“Ÿ|ØÎ?üä³v=2¸Í”/mûëÝå—µåP^ÊeeìﻄòìÅ/—øÅÏ.ÊÌu²Žmì3tl ŽöÛ¿c{ÿhpûm@(û»;Ë­íK?xæùnø,·¶¶—»»ûEb±8jÿ&Î9ð¹|óâãåÿçßn—õ´¸‰øAñ¥L xîÖ{»»í/ Û¸ãÞD”™ãøçÏ›ønKÄmÜÁ‡ÊèCpèÇtk{«½ Ìlûâñ“j"8ÜÝnÅö/þÃwƒÛoÁÇgÆç¼wxÜþøo>+à\lòo›lêðîâbq|О‡s””ÍMY-¿úðýå?ùW¿Ü.ë9—ÀðÆ@@s÷ÌTÜÍìøà`yÐÀÊðº åXÊa~ÀM°î7_æáò¤aqxОƒmÙçºeäìÏt» ¾ƒ£ƒö8`žuÙ‡ó׸‹åïøçò¿·ß†¾Žö/>#þ®½æ»ˆXÇ÷V"H‘A÷乸ïÜHtÒ~\ü  £­­ Xf_ÄMŒÀ`?öç8ö_ì?_.Îiæÿׯ~Öž+?hîäWed$ì—´Ÿ2wš²v·Z˜gÛò÷pÌPYc‘;5'¹»³ú¬HçŽVËñÑa“Qí´ÛÙ¶½³w)°Ø¾»³}~ÜV+€•V)9G+Þóò€s"lÖórÜp¯y¬â»äoæ¸/ÿô¿5×°ÊrlßkDÉög[MVvxØ<Ží¬–Ÿo7’9\É¿“ ®ùNÚ¿«¹^޾ó|ÈlzŸóG%X]O¶¯®rþýì?¶Ûö.ŽÙ;8¿~%ðÖÜH_8røöÓ‡—èn÷ªt›/0ûè|Ÿãí.$}¯’ÁÍŸŽ"Û“Õ •5] ä¼|–Ýb;[Ryx¶½ßÁË5³-Û îÝ&@9Že5ºÒ%£â8¶ñœÌ9"þ5Xu®!Ÿ ÛùL»×FíórÍãÈùSç&ÐÙq¼ñ™°O·|>$ÁysÍlÿáßû;—®‡e¶QÞ±xkn$>t>à.¬»J|¡˜¨<àø”ñõ÷/ˆ²ü?Ÿü¸ý1å®Ê.òƒë±O÷?ha¾¿ÓPYc 䛀äïáz Š£æÑgow¯•fç½/Û`@R\#?~îÒË2eí6©?å0O9ýó¦,A9ÌÃóæŸG”{|.¶ÿüûíy²?ç&{Èö÷?þ_³ÿ~;ÏÝyH9ßJ‡×Í:2cøLXæüÝÏäWõ·mPg¹-£É²ÌgÃcOþ¾½#YÛ߇¸=­¸»^~7Àô(ÓãÏ~z±.ûðSÁ7TP&PFþtï–§û–g-?^žî~p±-×°²þp™œ3ÁÍäYøÏ-·Îy~øã峃Ë2à‡9TÖ˜ìnµç!Wë^§ñH ä1k‹0:wSö[»ZוÛWÜ=ïªî€m] àù SYàómeCž/³}gÿ°Ýÿò¹^K€Lâò¹_ (Çv×!”H€¿+×Ã5f]÷ÎÞ½^ŽÏùò7íµË¯%óÉ͸µüÿçÉçË¿ýÅ_Àr¤}¯“_*~¶÷AôgŸ,ÏúšƒFÍ6öa_ʼJÚ…þ`¹uôh¹süÓåî¢ ˜æYÇ6ö‰†Ê$Àµ¿–Àá% 0Ÿ»;°žiWAÞ<ðÙ± d{Òò‹óžï—c9Ç<Ýڻ؇cY÷¦ؾJ×Wç!_ß™Ûki‚˜ùa ¼ÞwHt´×<ŸÉXÇù‡$@†”ýYn%Ð|VÙ?ç“›q# ðaóóÅè§OW8Ûz¾âù³ |ÿøÁž¼ß*øBÔ¿Àa#€ãÏ–g‹Ïž4óÍôh%2ö¥Ìö9y <à l}²Üiïäóåþé“–½“'­ ØÆ>ìË1CeÇÑE&Àô Iö.ólßÙ?¸øa|¤Ñ\'ËÛVÚmo]lß=Xee0dÝrâ,·AÞ,®ÒëGPó½]dçËl_òe ]’™ÃêÛçírÝ9Û;é<©$Ðþ]ÍõpÍÙé$¨ûÈrþ&Þ’°.û_þüå:n$Úðñ!'hƒ¿ùAžíl¯¦Í2ëy4`?¾ |YCÐÆ€/ œFMž>]qÒ€œì­ê(ó* Ps p×GgO—‡gÏZ˜ßkdÀ6öaßíæú‡Ê£&#Újðü`ùóco¥ÐG~Ü×Ä<<ÛÞ»ô¬Ìvž²VÁ}ÔÓå€ç5-œåˆ!ûÛ)3bêý¸†H e^’@ÿx®ë\tìŸó±^YŸK {=Ì·ŸÉ¹T(å|çùœúØm$Àçý/þr­ø¯"?"¾(ÒýTÞíî\À2`;ûñ(0TV L¾TXì²<åîOðŸ5rÓg­N d?*Î†Ê Ý2wšãö›òþ£¦<8l`]$3TÖ˜Dœ‹M`ðyæu[=ëùöíAó7#‘þö­ç[m°r|îüìƒDØïó'ÏÚgïü}l'`Ø–²¨È5ä;ÞÞÙmƒ:ËÏ·V ŽRFމ|r–ùy­Èñù»Xǹàéó‹mɲ `^ù±8ád™ó±Oþ¦£ƒV HƒuýýåfÜHÀ‘/D'çènÛÛÞ¾6`Ǿ8øíwï/ÇMÐ7Ázú|y²xÚ®Ëv~PCetÁjùáÁÎQ“R7å5åÁþâY».ÛI¯9f¨¬RðcÍxh[æûŸßUÇŽY·/HmþueÉÐùŽWÈóÍë‘ËÜXÀl·I¹ùAì &à.‡xt:vp\·¬.lãuÖÐñCDו9…¦„z ¾£¤ãCûÔäè¼N"+CûHn%@àÒÚ‹JªÝ­­öÎOúxp0¸ÿuð# p¼ |–ßæBp_Uföé÷.ÃwÃßN%Ü‚ŽÊœ®g“i%À—pSr AÔ‡õCÇ\Ǻò`Neм‹ÜZ"ònÑJ€4YD6“V<Ÿ‰ÈfÁëÝ ˆÈæAÅ,2P" ¸¨‘̓¶2J@dƒQ"έ$°ätp›ˆÜOn%v¤Qˆ¼;ÜZ¼OT"ïo%E òîðÖP"ïw’+‘ûÍ[K`%€Åà~"rx+ (‘w‡[K@ˆ¼[ÜJŒÆ£DÞ-n%ÓS+EÞ5n%y÷@Ž' ²Á *ü•€È†Â "üß% ²¡ þ#•ÙPxà;Ž*þËÿ³6S$ðÞƒ—ãJ€`åŸLváŸ`ö×½ ¶S—jààçêß% 2.U%Àú»Š@ ˆŒKu ÜUJ@d\&‘À]D DÆe2 ¼mE¡—I$p—7J@d\ªKய •€È¸T•Àm”€È¸T“ÀX(‘qA£7V"÷:=yº£D6$ðë—#gŒEÈpE%:Ÿˆ¼=HF•€ˆÜY”€È†ro$ÀøÅÏñXddî…ÀïþúoZ¾xüDˆŒÈì%€™éö×»må ‘ÁÐ1"rsf-€yÿá'ŸµDdˆ@ˆ¼=³•@@Sc@y$èÊ ÿ üðG¿,KDÖ3K ¤€à§M3Î|7+`;’€ÁG¹=³“à‚ú/?Z~ÿøA+: üÉ Ø7Yó¬ÿæÅÇ—Ê‘ë™øg/~Ù^Tš #:9ðF õiBÌ:ì7T¦ˆ¬gv ˜ó&€ ã€`'ågéÞ dÀzš=2?T¦ˆ¬g–È$d¯+ÿVÏýHJÁ¶ '€ãã –)"ë™v†d".žùUÝ€= EnËì$µýkd¼ÎV…"r{f)°’Á›™óÀ*>^D®gÖ}3‘»s/$x!ÀÐ>"r;î•Dd|”€È†so$@+A*} —{!4BÌ#‡ÙK€ ݇iNœW‚ˆ@ˆÜYK€ÀGÌ“ô´ˆ ºëúÇŠÈ͘­’¤Mó}_ÉXçx"·g–èv"°Óu˜ e{Ú ø4Î#‚¹=³“OWa‚þÛO.Ï>û ]Ç?‡ÒRä8Ö;¨ˆÈ홥Hó»é-ØÍ"–™Gì7T¦ˆ¬gv ‘@D»>H°g]2Ö1ÎóݲDäzf)HÅ_dÔŸy2<tðêÕGo”'"W3;  ¨‡d°zXUv`{‘·c–ëdpá @änÌZ@ïÊ€ù *¢DîÆ½@ˆ ‚¹;÷J"2>J@dù7àa·… ˆŒÃ½ÁOK´P"ã1{ DiÀ<Œ”È8ÌZùŸ´ßíSÀ[dW`èx¹žÙJ  E`îúÉ"ƒnfÀüb1\–ˆ¬g–è ­¹H=Y@dÀ:Ža㠈ܞÙI  ‡ xïÁËU€Ÿ‹ YÁåG‚Óv½¹=³“Í!€/?jÉŸ»>ëD·b°@# ¹=³“@Òüî£ëX¦ÈØFðgL2Ö •)"ë™d‚<"ÈÛîúl#Ø |öM'"ÖýÅŸÿY;ß/OD®fvÈAdÀ]$࢓%üúåoγ€ðFy"r5³“tEЗYÓd¯0\–ˆ\Í,%ÖÉ /2„¡ãEäzf-À@"]  2÷B¡/ rwî•Dd|”€È†SU¼æË{ÿ¡í"RŸj@Ôìç¿2™U$$øóªOˆLOq Dû³¿lçiîÛÍ Òïô9 ½#‚È › ƒÃCº —'"ãSL]ä½>ˬ'ýïÊ€ È:Æp„ ‘z‘@R}>Œ À”Þ~œ0£ø<pLÛ' •€u"µ("€ÀÿêÃ÷ÛÁAhëÏ2é?Óî#Ç $Á~Y'"å)"ŒÀ3?O6 Àœí<ëØÐ-˜©©G Üè  X—:À£Ç-ǃåŠÈø‘ÁO`0@%à»`¦¯^}Ô”a& R‹"€¤ûC2 3 è ÀÇ‘ú“@X'ƒ ¦D¦¥¸B_H@ˆLO5 „~P"ÓR]"2/”€È†SU¼"ädv™Õ$€¶¿ÞmëÒ@ˆLO D?m˜§±m”È´—@@+AZ’ 0ígAˆÔ§¨ºwý®XŽ 89S¶#æTD¤Å$€ði ôÍ‹Û €;~_éLÄx5&R"èfŒ'žƒˆ€´Ÿ`'+èÊ€FCÌ¿÷à¥ƒŠˆT¤ˆrwgŠ€&Ãd¶ç!2@ìg+B‘z‘w}îþ<ãð,s·çQ€`Ø– BÀ2SÄ0T®ˆŒO p7D@Àsîî<÷÷ëX&ðÙ—õd ''J@¤E$Ô>Ý…û2È[‚Üù™†““'M'o”)"e("èy_Tö3€•¬©M1 „u2`^ˆLOq „TF‚™–jÉ ^g¾™’ê‘y¡D6œªàD<X 2ªI }º(‘é©"®”ȼ(.=o˜ç …†dà ""õ)*z^2N@w]dÁD€ö*"Rbè; „ø÷ä4b¬îøÙN«AdLÁAEDêRD]d<º#¶%+èÊ€zÖ#ŠÅÂD"µ("‚™´ž;|wPÖsÇ'èÙÖ•ód *"R—"èöL°3OÀ3ŸeDÁ "@¦*"R"ànNêÏ#ÁNÅMÊŸàO=@WìÏ "§§J@¤E$Às>MOA„°ód ÙÃJ [M¾*©E AÎH{ðMü"µ)&Úÿ® zæ€Èô—@è˨7P"ÓRM!2 @dZªK@Dæ…ÙpªJ€ôŸÊ@[ŠÌ‡j@Ô•+X 25U$€h@ðGœTˆLOq D:݈óGÌ DæAQ D>„è,„ü® ¸Ö#ƒŸÿb_ˆT¤˜r§OÿZ"NF×a‚žy¶ý „ƒŠˆÔ¥ˆÜ_>~tiP‘>Ͼ‘b`=û™ ˆÔ£ˆHóS ˜f¯›/Ú²™AAìË~¾B©G Üu2à$¤ü¬ê x\@<* z" ö*WDƧ˜¸£3MF@°ç Aºì]°¿ƒŠˆÔ¥ˆxÞï>te@à2`}Wl?=ÝoʰN@¤E$ˆ€‚×É`X¿HmŠI ¬“óAˆLKq „¾ €y 2-Õ$¨ ¤> (‘i©.™J@dé**i(d‹@‘ùPM€QAHC!e 2ªHÐD˜à§]‚T*‘é).€·é;@OÁÈ ™Áª÷ 2©MQ Di˜ €ÀïÊ€u0ž€]‰EêQL©@ï=xÙ<" óP²‚°ód*"R—"àîN°Ü (òí§[ Ðd˜l€õLÙ7ŠëéEècH=ŠH€´xèö ‰0©>wz¶³Gƒ€eöS"õ(":Ïûˆ€p·'¸Ù¬C@à#$0Øw¨\Ÿb ¨™rÇGÜñÉp¨ è €)ggJ@¤E$ÀŸàNOÁ® |D0$.DˆÔ¥ˆ w}‚{H,¿)€á²D¤Å$™A_ÌGyS "õ).ЗA0™–j‘AÚGDêQ]"2/”€È†SU¼ RÐÆ@"ó¡šÀªÁÐâ¢Ó2™ž*@¼d 4¢¿@d`¡Èt—@ÀY° g¼îü̳S% R" ˜¹ëG©ý¶ëYF‘ Ì/Ž' R‹" õçŽÔüGd>R`]* #æWƒŠ˜ ˆÔ¢ˆ€L€»ú u}ä-ˆÔ£˜2ˆº2è>(‘é(.З`¸1Þ(‘é¨&ЗmD¦¥ºDd^(‘ §ªhäó¿È¼¨&À[Úä¿ í'"u©"€¥¡¯ Ódxè©Cq D;¯#2e 2=E%@§AŽA¿õ óÀ>´$*KDÊPL€€O‹À4b}ƈ þ¬sP‘º‘@2Æ E H§"Ö%+èÊ€cX¯DêRD6­Tþ3íÊ€cºвNDÊSDIóé*œf¬ãD>°€gžõì“}•€H=ŠH€à†ˆ`uÇ_ *‚ ’ °Ì4 Î€ãTD¤E$@ðØ@ÀG«!ÆO/½5è àÕ«š2ÌDjQDtHdU¨+DêSLa xe¨D¦§¸B_H€uˆ@ˆLG5 „È P?0´ŸˆÔ¡ºDd^(‘ §ªh-È›Ú m‘úT“m¶¿Þmëx=ÈI•ÈôT‘àU` 1¯ DæAq $à-- ~¦f"ó ¨è@àwEPGЕÁªOÁpy"2>Å$Ð@èTrÇïË€>LO@¤.E$ç~ÀXi.Ì4w{²‚® h5Èú÷¼l$àcH-ŠH ww¦T„Öß¼øøRBˆ Ò¥˜ýlF,R"à®Ï¸6Oÿ¦d ~²öÉ:À2SÄ0T®ˆŒO p7G<ä¹ÛA¬g_êèLÄ:˜W"õ("T¦»0wxîö«†VÿŠŒe$Á~l'S`ºTää2E¤ E$I÷û2àF]ð†`¨,)G1 ‚ ïË€ù¬ ™†â}D @dZªI Df"ó ºDd^(‘ §ª8¯} 2ªI è¢ D¦§Šº U`W@K¡ãD¤<Å%ð } h)؃2™Ž¢H çu ýAÖñºp83.ODƧ˜º Щ(£ G´$3 Sh;ýè÷­,†Ê‘ñ)" L£ Œ'ÐT„ ÏC‘`½ƒŠˆÔ¥ˆ’êw%@÷b– v¶ð,§›1ËHÂAEDêRDÝîÃ:0ÏÝŸ`–™2®Û@¦Ž' R"øÉO>¼X„`'å'°3˜(ÁO6À¿+ög²“% R‹" è lz ve@}ا/¦''Ï›2TD¤E$yžóû2X/+EjSLa ˜W"ÓS\PuBˆLG5 „È (‘i©.™J@dé*Tú 2ªIê”È<¨"ŸöÚ ÐpHˆLOq D´¤qþáHD`f 2-E%@ðO§": q2‚?2`{.üüûÊ@¤"Å$´Ÿ`§-'£ë0YAWÉTD¤.E$Ð;|üèbPZr2‚ž¬ 2`wÖ³Ÿ™€H=ŠH€`&µçY¿Ûg w|2æÉ 2ÂËì‹(Øg¨\Ÿ"è*È€ÔŸ.Ä;  #àîÂpP‘z‘w}îèÈ ¯3¨'c°®+ŽcP‘ÓS% R‹"à9¿ûЗpRÖwÀöÓÓݦ ëDjQD€H÷×É`X¿HmŠI äU`_ÌóH D¦¥¸B_À¼™–j‘AP"ÓR]"2/”€È†SU¼ 2Ð"ó¡š'¢N ­•ÈôT‘ À ò?•È´—@@“a: ѧ€¦Â¼äÄ—3߈Ԧ¨"€´$àɘFìÃ:>ÿ£#e R‘bHÚdš¶=Á Ø–>*"R—"@éÀ "ß~ú°½ëÓ³,€õ€} x@ 4!f3‘z‘@žñ»ˆÀÖövà©dóË*"R—"  IóÉ;ÁMYG=  †Ê‘ñ)&‚š)w|‚›`ÏNÈú¾˜2¨ÈÙ™©E p''­Ïc@_lÙÁÙÙáå‰H9ŠHèw_<&°þM —%"å(&°NÌGym8t¼ˆ”¥¸•‚]3‘i©&„¡}D¤Õ% "óB ˆl8U%À›ôÚ."õ©&@¿Ø´à?!2ƒt'V"õ)* ƒ˜'ÐøiAÈ2ý XÇx‹Åpy"2>Å$@pS@ ¿÷àåŦ™p²‚?ã Ðf8¨ˆH]ŠHÐàþêÃ÷—ß?~Ð;B@ ¬gÊ¾È ƒˆ zúX RbÈ£@š ÷³€6õoÖ#$Àö DêQDÜñ¹óSùGá«·ÿi›$`™Ô?ÁÏ$ˆÇ„¡rEd|ŠI€ NÅ_ÞDÌ#‰¾˜þö»÷›2”€H-ŠH€”Ÿ»}˜ïÊ`õ6`ñ†Øvvf—b‘š‘  ¸‡du—0\–ˆ”£˜º É€ùŒ*¤D¦£ŠBWAˆLKU ˆÈüP"ŽÙpªJ€7T ÚHd>T“ q•ƒ4V"ó ŠÍ„ þÈ€õÊ@dzŠK€ çi|¿…;?ÿ¾Œþ¬ç?³o²æYºe‰ÈõÌNüö×»mªßý'¦,“¤’02`óìK¶0T¦ˆ¬gv ˜ó¼Hõ øÜísÑd>ǰÂ*SDÖ3K 쌀JAžûIýyT@,wÀô›–)"뙸ëÐy èË o úà8;Û£<¹šÙI…†d0,€á²Däjf)€ ¸ë÷eÀ¼‡YK ôe @dî…Bd†ö‘ÛÁk÷{#$J@dC¹7 m …G@d\x˜½­©`ž¦ÅÊ@dðf+‚žJ@¦´@4"+@CLjÈÍ™õÛ€»>™]‡ |Ú Ö)‘»ˆ³ÙI  =p÷'è# 3Xõ+X´"p<‘Ûƒ¸©ÎJ€`'øT„g>m"ƒ´ ¤3"P"·gv@ÔTô_>~´üþñƒVôட¬€}“0Ïz{ŠÜžÙI€€Ïðbi"Œ¨½LÝ@2ƒ *Â<°ßP™"²žÙI€`Λ.vR~¶‘þsÁ©#ˆÈÆP™"²žÙI w~‚Œàuåß깟 &hëÎ;EÇÇ?,SDÖ3; Üö:„7@=Áª~@DnÎì$«ôu§’Á°†Ë‘«™¥ÂÅ«¿ž ˜W"ã0k „¾ ‚¹;÷Ba%ƒ× í#"·ã^I@DÆG ˆl8÷F´  RÐÇ‘q¡’}öHã  ÷ 2pP‘q˜½̓ ~šóF(‘q@³O"æy è¬2ƒ×ëúÇŠÈÍ Æ¸¹ÎN@Ú0ŸWƒ]$`ÙñDnO²ëYI u­šÀf™yj1ÙÞ•͇ٗõJ@äöÌN<Ï'ý·Ÿ>\ž}öA»Ž&Ã\,뙲/"Èq¬wP‘Û3K æw¸Û' àbÛÔÿ\ˆ€y$Á~ýòDäjf' Ðc0¬ç"#„¤ÿ¼Öˆ’=tË‘ë™r °‘A²‚¤þÌ“ pá}¼zõÑå‰ÈÕÌN€r×ïË€ î>DüCe‰ÈÕÌRà’A@=¹³–@èË€ù *¢DîÆ½@ˆ ‚¹;÷J"2>J@dÃQ"ŽÙp”€ˆ´(‘ G ˆl8? õ pøà¿~ù› ø·_tæ´Ø[ÇŸˆÈ†o>œ-ÿ?èÎÕ)¿¬Ý6IEND®B`‚listparser-0.20/docs/specifications/opml-1.0.rst000066400000000000000000000205331460153066600215650ustar00rootroot00000000000000OPML 1.0 ******** .. note:: The OPML 1.0 specification is available at: ``_ It is copied here for posterity under the terms at the bottom of this page. About this document ===================================== This document describes a format for storing outlines in XML 1.0 called *Outline Processor Markup Language* or OPML. For the purposes of this document, an outline is a tree, where each node contains a set of named attributes with string values. Timeline ======== Outlines have been a popular way to organize information on computers for a long time. While the history of outlining software is unclear, a rough timeline is possible. Probably the first outliner was developed by Doug Engelbart, as part of the Augment system in the 1960s. Living Videotext, 1981-87, developed several popular outliners for personal computers. They are archived on a UserLand website, outliners.com. Frontier, first shipped in 1992, is built around outlining. The text, menu and script editors in Frontier are outliners, as is the object database browser. XML 1.0, the format that OPML is based on, is a recommendation of the W3C. Radio UserLand, first shipped in March 2001, is an outliner whose native file format is OPML. OPML is used for directories in Manila. Examples ======== Outlines can be used for specifications, legal briefs, product plans, presentations, screenplays, directories, diaries, discussion groups, chat systems and stories. Outliners are programs that allow you to read, edit and reorganize outlines. .. image:: opml-example.png :alt: A screenshot of an outliner application with a hierarchical "To do" list. Examples of OPML documents: `play list`_, `specification`_, `presentation`_. Goals of the OPML format ======================== The purpose of this format is to provide a way to exchange information between outliners and Internet services that can be browsed or controlled through an outliner. The design goal is to have a transparently simple, self-documenting, extensible and human readable format that's capable of representing a wide variety of data that's easily browsed and edited. As the format evolves this goal will be preserved. It should be possible for a reasonably technical person to fully understand the format with a quick read of a single Web page. It's an open format, meaning that other outliner vendors and service developers are free to use the format to be compatible with Radio UserLand or for any other purpose. What is an ? ================== is an XML element, with a single required attribute, version; a element and a element, both of which are required. The version attribute is a version string, of the form, x.y, where x and y are both numeric strings. What is a ? ================= A contains zero or more optional elements, described below. is the title of the document. <dateCreated> is a date-time, indicating when the document was created. <dateModified> is a date-time, indicating when the document was last modified. <ownerName> is a string, the owner of the document. <ownerEmail> is a string, the email address of the owner of the document. <expansionState> is a comma-separated list of line numbers that are expanded. The line numbers in the list tell you which headlines to expand. The order is important. For each element in the list, X, starting at the first summit, navigate flatdown X times and expand. Repeat for each element in the list. <vertScrollState> is a number, saying which line of the outline is displayed on the top line of the window. This number is calculated with the expansion state already applied. <windowTop> is a number, the pixel location of the top edge of the window. <windowLeft> is a number, the pixel location of the left edge of the window. <windowBottom> is a number, the pixel location of the bottom edge of the window. <windowRight> is a number, the pixel location of the right edge of the window. <head> notes ============ All the sub-elements of <head> may be ignored by the processor. If an outline is opened within another outline, the processor must ignore the windowXxx elements, those elements only control the size and position of outlines that are opened in their own windows. All date-times conform to the Date and Time Specification of `RFC 822`_. If you load an OPML document into your client, you may choose to respect expansionState, or not. We're not in any way trying to dictate user experience. The expansionState info is there because it's needed in certain contexts. It's easy to imagine contexts where it would make sense to completely ignore it. What is a <body>? ================= A <body> contains one or more <outline> elements. What is an <outline>? ===================== An <outline> is an XML element, possibly containing one or more attributes, and containing any number of <outline> sub-elements. Common attributes ================= text is the string of characters that's displayed when the outline is being browsed or edited. There is no specific limit on the length of the text attribute. type is a string, it says how the other attributes of the <outline> are interpreted. isComment is a string, either "true" or "false", indicating whether the outline is commented or not. By convention if an outline is commented, all subordinate outlines are considered to be commented as well. If it's not present, the value is false. isBreakpoint is a string, either "true" or "false", indicating whether a breakpoint is set on this outline. This attribute is mainly necessary for outlines used to edit scripts that execute. If it's not present, the value is false. Compatibility ============= Before the 1.0 format was frozen the top-level element of the format was called outlineDocument. Radio UserLand will continue to read such documents. Limits ====== There are no documented limits to the number of attributes an <outline> element can have, or the number of <outline> elements it can contain. Notes ===== OPML is a file format, not a protocol. When you click on a link in an HTML document it doesn't in any way change the document stored on the server. OPML is used in much the same way. Wayne Steele did a `DTD`_ for OPML 1.0. Thank you. In general, the mimetype for an OPML document, when accessed over HTTP, is ``text/xml``. This allows Web browsers to use XML formatting conventions to display an OPML document. Radio UserLand's built-in HTTP server looks at the Accept header of the request to determine how it processes an OPML document. If the Accept header says that the client understands ``text/x-opml``, we return the unprocessed XML text. If it is not present, we return the text in the outline with the mimetype ``text/html``. Copyright and disclaimer ======================== Copyright 2000 UserLand Software, Inc. All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and these paragraphs are included on all such copies and derivative works. This document may not be modified in any way, such as by removing the copyright notice or references to UserLand or other organizations. Further, while these copyright restrictions apply to the written OPML specification, no claim of ownership is made by UserLand to the format it describes. Any party may, for commercial or non-commercial purposes, implement this protocol without royalty or license fee to UserLand. The limited permissions granted herein are perpetual and will not be revoked by UserLand or its successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and USERLAND DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. .. _play list: http://2005.opml.org/examples/playlist.opml .. _specification: http://2005.opml.org/examples/specification.opml .. _presentation: http://2005.opml.org/examples/presentation.opml .. _RFC 822: https://datatracker.ietf.org/doc/html/rfc822#section-5 .. _DTD: http://2005.opml.org/examples/opmlDtd.txt ���������������������������������������������������������������������������������������������������������������������������������������������������������������������listparser-0.20/docs/specifications/opml-2.0.rst����������������������������������������������������0000664�0000000�0000000�00000033570�14601530666�0021573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OPML 2.0 ******** .. note:: The OPML 2.0 specification is available at: `<http://2005.opml.org/spec2.html>`_ It is copied here for posterity under the terms at the bottom of this page. Goals of the OPML format ======================== This document describes a format for storing outlines in XML 1.0 called *Outline Processor Markup Language* or OPML. The purpose of this format is to provide a way to exchange information between outliners and Internet services that can be browsed or controlled through an outliner. OPML is also the file format for an outliner application, which is why OPML files may contain information about the size, position and expansion state of the window the outline is displayed in. OPML has also become popular as a format for exchanging :ref:`subscription lists <Subscription lists>` between feed readers and aggregators. The design goal is to have a transparently simple, self-documenting, extensible and human readable format that's capable of representing a wide variety of data that's easily browsed and edited. It should be possible for a reasonably technical person to understand the format with a quick read of a few web pages. It's an open format, meaning that other outliner and service developers are welcome to use the format to be compatible with Radio UserLand, the OPML Editor, or for any other purpose. What is an outline? =================== An outline is a tree, where each node contains a set of named attributes with string values. What is an <opml>? ================== <opml> is an XML element, with a single required attribute, version; a <head> element and a <body> element, both of which are required. The version attribute is a version string, of the form, x.y, where x and y are both numeric strings. The value of version may be :doc:`1.0 <opml-1.0>`, if it conforms to the earlier version of this specification, published in 2000; or 2.0 if it conforms to this specification. If you see a version `1.1`_ file, treat it as if it were a version 1.0 file. What is a <head>? ================= A <head> contains zero or more optional elements, described below. <title> is the title of the document. <dateCreated> is a date-time, indicating when the document was created. <dateModified> is a date-time, indicating when the document was last modified. <ownerName> is a string, the owner of the document. <ownerEmail> is a string, the email address of the owner of the document. <ownerId> is the http address of a web page that contains information that allows a human reader to communicate with the author of the document via email or other means. It also may be used to identify the author. No two authors have the same ownerId. <docs> is the http address of documentation for the format used in the OPML file. It's probably a pointer to this page for people who might stumble across the file on a web server 25 years from now and wonder what it is. <expansionState> is a comma-separated list of line numbers that are expanded. The line numbers in the list tell you which headlines to expand. The order is important. For each element in the list, X, starting at the first summit, navigate flatdown X times and expand. Repeat for each element in the list. <vertScrollState> is a number, saying which line of the outline is displayed on the top line of the window. This number is calculated with the expansion state already applied. <windowTop> is a number, the pixel location of the top edge of the window. <windowLeft> is a number, the pixel location of the left edge of the window. <windowBottom> is a number, the pixel location of the bottom edge of the window. <windowRight> is a number, the pixel location of the right edge of the window. What is a <body>? ================= A <body> contains one or more <outline> elements. What is an <outline>? ===================== An <outline> is an XML element containing at least one required attribute, text, and zero or more additional attributes. An <outline> may contain zero or more <outline> sub-elements. No attribute may be repeated within the same <outline> element. Text attribute ============== Every outline element must have at least a text attribute, which is what is displayed when an `outliner`_ opens the OPML file. To omit the text attribute would render the outline useless in an outliner. This is what :download:`the user would see <no-text-attributes.png>` -- clearly an unacceptable user experience. Part of the purpose of producing OPML is to give users the power to accumulate and organize related information in an outliner. This is as important a use for OPML as data interchange. A missing text attribute in any outline element is an error. Text attributes may contain encoded HTML markup. Other special attributes ======================== type is a string, it says how the other attributes of the <outline> are interpreted. isComment is a string, either "true" or "false", indicating whether the outline is commented or not. By convention if an outline is commented, all subordinate outlines are considered to also be commented. If it's not present, the value is false. isBreakpoint is a string, either "true" or "false", indicating whether a breakpoint is set on this outline. This attribute is mainly necessary for outlines used to edit scripts. If it's not present, the value is false. created is the date-time that the outline node was created. category is a string of comma-separated slash-delimited category strings, in the format defined by the `RSS 2.0 category`_ element. To represent a "tag," the category string should contain no slashes. Examples: 1. ``category="/Boston/Weather"`` 2. ``category="/Harvard/Berkman,/Politics"`` .. _subscription lists: Subscription lists ================== A subscription list is a possibly multiple-level list of subscriptions to feeds. Each sub-element of the body of the OPML document is a node of type *rss* or an outline element that contains nodes of type *rss*. Today, most subscription lists are a flat sequence of *rss* nodes, but some aggregators allow categorized subscription lists that are arbitrarily structured. A validator may flag these files, warning that some processors may not understand and preserve the structure. Required attributes: type, text, xmlUrl. For outline elements whose type is *rss*, the *text* attribute should initially be the top-level title element in the feed being pointed to, however since it is user-editable, processors should not depend on it always containing the title of the feed. *xmlUrl* is the http address of the feed. Optional attributes: description, htmlUrl, language, title, version. These attributes are useful when presenting a list of subscriptions to a user, except for version, they are all derived from information in the feed itself. *description* is the top-level description element from the feed. *htmlUrl* is the top-level link element. *language* is the value of the top-level language element. *title* is probably the same as text, it should not be omitted. *title* contains the top-level title element from the feed. *version* varies depending on the version of RSS that's being supplied. It was invented at a time when we thought there might be some processors that only handled certain versions, but that hasn't turned out to be a major issue. The values it can have are: RSS1 for RSS 1.0; RSS for 0.91, 0.92 or 2.0; scriptingNews for scriptingNews format. There are no known values for Atom feeds, but they certainly could be provided. Inclusion ========= An outline element whose type is *link* must have a *url* attribute whose value is an http address. The *text* element is, as usual, what's displayed in the outliner; it's also what is displayed in an HTML rendering. When a *link* element is expanded in an outliner, if the address ends with ".opml", the outline expands in place. This is called inclusion. If the address does not end with ".opml" the link is assumed to point to something that can be displayed in a web browser. In OPML 2.0 a new type is introduced. An outline element whose type is *include* must have a *url* attribute that points to the OPML file to be included. The *text* attribute is, as usual, what's displayed in the outliner, and it's also what is displayed in an HTML rendering. The difference between link and include is that link may point to something that is displayed in a web browser, and include always points to an OPML file. Directories =========== A directory may contain an arbitrary structure of outline elements with type *include*, *link* or *rss*, and possibly other types. A wide variety of software can be used to display directories, including outliners such as the OPML Editor. Extending OPML ============== An OPML file may contain elements and attributes not described on this page, only if those elements are defined in a namespace, as `specified`_ by the W3C. OPML can also be extended by the addition of new values for the type attribute. When specifying such an extension, following the example of this specification, say which attributes are required and which are optional, and explain the roles each of the attributes plays, how they relate to each other, and what rules they must conform to. There is a mechanism in the OPML Editor that is based on this form of extension. Developers should, whenever possible, use capabilities that are already in use by others, or included in this spec, or recommendations or guidelines. Examples ======== * `<http://hosting.opml.org/dave/spec/subscriptionList.opml>`_ * `<http://hosting.opml.org/dave/spec/states.opml>`_ * `<http://hosting.opml.org/dave/spec/simpleScript.opml>`_ * `<http://hosting.opml.org/dave/spec/placesLived.opml>`_ * `<http://hosting.opml.org/dave/spec/directory.opml>`_ * `<http://hosting.opml.org/dave/spec/category.opml>`_ Notes ===== 1. All date-times conform to the Date and Time Specification of `RFC 822`_, with the exception that the year may be expressed with two characters or four characters (four preferred). 2. The page in <ownerId> may contain link elements pointing to other documents containing information about the owner. For example, you may have a link element pointing to a FOAF document describing the owner and his or her network of friends; or an RSS feed with news from the owner, possibly even related via the RSS 2.0 category element to parts of the OPML document. In other words, all the extension mechanisms of HTML can come into play. 3. The value of type attributes are not case-sensitive, that is ``type="LINK"`` has the same meaning as ``type="link"``. 4. Outline attributes generally do not contain encoded HTML markup, unless their are specifically said to include markup. 5. Processors should ignore any attributes they do not understand. 6. There are no documented limits to the number of attributes an <outline> element can have, or the number of <outline> elements it can contain or the size of any attribute. 7. Each sub-element of <head> may appear once or not at all. No sub-element of <head> may be repeated. 8. If an HTML page is generated using an OPML document, you may use an HTML link element to provide for "auto-discovery" of the OPML. The rel attribute value is "outline", the type "text/x-opml", and of course the href attribute contains the address of the OPML document. 9. You may include elements of OPML 2.0 in other XML-based formats. The URI for the namespace is ``http://opml.org/spec2``. The namespace declaration should look like this: ``xmlns:opml="http://opml.org/spec2"``. However, for backward compatibility, the core elements (those defined by this spec) in an OPML 2.0 document are not in a namespace. Here's an `example`_ of an RSS 2.0 file that contains an outline in one of its items. Roadmap ======= Version 2.0 is the last version of OPML. Any further development will take place in namespaces, new outline types, per the Extending OPML section of this specification; or in formats derived from OPML with different names. Copyright and disclaimer ======================== | Copyright 2000 UserLand Software, Inc. All Rights Reserved. | Copyright 2006-2007 Scripting News, Inc. All Rights Reserved. UserLand Software, Inc. and Scripting News, Inc. are referred to in the following as "the Companies." This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and these paragraphs are included on all such copies and derivative works. This document may not be modified in any way, such as by removing the copyright notice or references to the Companies or other organizations. Further, while these copyright restrictions apply to the written OPML specification, no claim of ownership is made by the Companies to the format it describes. Any party may, for commercial or non-commercial purposes, implement this format without royalty or license fee to the Companies. The limited permissions granted herein are perpetual and will not be revoked by the Companies or their successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and THE COMPANIES DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. .. _1.1: https://web.archive.org/web/20070221092352/http://www.opml.org:80/stories/storyReader$11 .. _outliner: https://web.archive.org/web/20071017025441/http://support.opml.org/basicOutlining .. _RSS 2.0 category: https://cyber.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt .. _specified: https://www.w3.org/TR/REC-xml-names/ .. _RFC 822: https://datatracker.ietf.org/doc/html/rfc822#section-5 .. _example: http://scripting.com/stories/2007/08/28/rssexample1.xml ����������������������������������������������������������������������������������������������������������������������������������������listparser-0.20/docs/specifications/opml-example.png������������������������������������������������0000664�0000000�0000000�00000011477�14601530666�0022705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���Ä���Ù���îŸ.���sRGB�®Îé���gAMA��± üa��� pHYs��Ä��Ä•+��ÔIDATx^íÿ¯eWYÆçg5jÔø=j+ §‚BÀ‚" ˆB€Àˆ¥ÛJ¡tZJ±µ­J)mÚÒ¦(‚ˆ SHÕQ1â—ø=~_ã°ì;õIŸyg­µ×»ö>gŸ³îóIÞ½Ö»Ö:ûÞó<Ù÷̽¤‡¾ðùû’$I÷¥sÏ=7„+®¸âÌ@pR$é I$R1_ôóÓûËÓ—œsAúÒ‡¾"}Ùw^˜¾üa¯L_ñðW¥¯|ıôUü¹ôÕº(}Í£/N_{ø’ôõùùô ½4}ã÷¼6}Óã^—¾ùñ—¥où¾×§o}ÂåéÛŽO9÷ÊtÎß¾ãIW¥‡þàÓw=ùêô°§¼)=ü©×¤G<íÚôÈó~1=êé×¥G?ãútøGoHyæ/¥Ç>ëÆôÝ?þ–ô¸g¿5=þ97¥ï}îÛÒžwsúþŸº%yþ;Ò¼àDzâ ß™žô¢w¥zñ­éÉ/¹-=奷§§þÌéi/»3ýðùïNOù]éÜ~äÂ{Ò3_ùžôc¯þåô¬cïM?qÑûÒ³/~zÎ%¿’~ò5HÏ»ô×ÒO¿îƒéù—}(½àò_O/<þáô¢+?’^ü†ßL/¹ê£é¥W,}Óo¥—]óñô³×þN:ÿºO¤ ®ÿdzÅ 'Ó…o¾7½êÆO¥W¿õÓéØMŸIÝüÙtñÛ7]rËçÒkNÜ—.}çï¥×Þúé²ÛN¥×ßþ‡éò;þ(]qç§+ïú|ºêî?Io¼çOÓÕïùBú…÷þYºæ}ž®}ÿ_¤ë~õ/Óõø«tÃÿ:½ùC“nüðߦ·üÆß¥›>ò÷émý‡tóÇþ1ÝòñJïøíN'>ñ/é]Ÿü×tëÉK·}êßÓíŸþtÇgþ3ÝùÙÿJw}î¿ÓÝ÷ýOºç÷ÿwH…qäÈ‘tüøñ¬èº¿:kŸIP v]á@˜ñKXRz@‡ b¼@ÌÁ 7Û¿Ô}àœ…qäþÿ=ôÿ5~bð^bÌ@À+Áps÷/u8ÇÏ7âØ±céèÑ£éäÉ“§Ç†õlŽÅOŠ]ص@€Ña^™’ß¿Ô}à??ˆÃ‡Ÿ6ú©S§Nëĉ§ÃaÂ˾ìU hÓðëKó³0XøI�Ù¼¡@L©^ãU32Ÿyóû±i±@XÍaO…Ý�ö" �ð 9, ‘£O)"‘ÃÁ(gŠû-0!ïÏ~d²jðgÿüŠPØM`¯à ZÁOÀÀà 8xB�€'ð�xB�ž€'À(Šû­0•Â`šˆ£çN§NÜÿãÑýÕ°�X(¬â·ÕÇÎ;½¦~dòðL96¡'ÄÊÂcs¶Æd7½¹'„ÿ áA ÀZOˆÒg @Ä´@øø±i#È=!zr?2]P Î6"LçÅkL›DéuýüFá©"÷¡ÚèùPmô‚iýPmø@ÌýP­@œˆ)ùý¥ûhÕâ°Б'ÄÚÿì rÿìºôoªÁÜvÕoªÏÖÎ¢ç ¡@Œ3Oˆ\¯E>¹5-Z$øW¤’v5ú[¦ea¦YSKÝÎ âð9_— @I ÄØMá@Ì‘¡@ìº&!IQÅ@ؤ$íƒØ·=²3p] „ûü€ï1Þ¿­R Äxÿ¶JCâýÛª®@Øï¤v‰íãýÛ*b ÛÇû—e&ÏÍ›ˆ-Hlï_È ùú;ˆ¹õõžÁûø:*±}¼MˆûX³X ìOÀsóQÕ^£G½çõìËíÛÇû—Cà…5X·h –Eí5zÔ{^Ͼܱ}¼1.5 ÄÜP”^ƒÉõY�×¹žÁó^ÜçkŒAmÎÏ‹íàýÛªbN(r¯áçü˜Å=ëåÆ,î•®§z˜ÛÅû·U Do(r¯áçü˜å{<æëܘŽҵW®'¶÷o«†Bø1_çÆ,îñ5ÆÀÏósb»ÀÌ=ÚH rýVå^ÃÏù1Ë÷xÌ×¹1‹{|íÅ=¾æ9±]̯s´h r½ˆr¯áçü˜å{<æëܘŽÒõTsbû°‡£šˆÜ|T¥×`r}–§Ôãy/îó5Æ 7Ÿ›ÛƒýÛ[M³!å%Ö=­±‰íÃþí­&bëÀŽV¨)Bì:ìßÞjR Ä0°‡£R İ{«IÃÀŽVHCÀþí­&B {8Z!B û··šö>›ü@ïyK߇hƒ=­Ðj¸÷Þÿ{j½äŒ1ãÔÚ]2¶BV‡ýÛ[M«bN(ji51ìáh…VDo("±µÈÍyÐ+­Ã¼ïñ×~bJãÒzñ ìßÞjÚ‰@ô„¢Õ~§Î°~i½ß[[׳ϨõÄÙ°‡£Ú™@DC‘3 äÖßóÔözJçöžaÔÎgÂþí­¦!ŸÞH^€¯sø~nÌ¥kÙÚÚÚ>ñ�ìáh…†ü Ñj¤)“ù~íÜR¯¶ÎÓz¦8öoo5­ˆ9äLR3•1Yë^»®õ˜Ú8ÒgÃŽVhÕ@,…•£Ô¯í1rëÞϽÒ5À¾©÷ýXœ û··šV „KÃŽVHCÀþí­&B {8Z!B û··š1 ìáh…1ìßÞjR Ä0°‡£R İ{«i'¡_8‰%`G+´z , §N)bìßÞjZ5Ô ÛyzΛ"zféÞ–dÓçÛx¹°‡£Z-ö æ0ô„¯/ÁœûÛÛxÍ5¾®)Ø¿½Õ´J ì ù0@S”Öð|Ë9Q"gnâõ§ØÆk®ñuµÀŽVhodjY‹5Vsë1éMAniŽ™Zo`œ«SšÏQZ‹q­Ÿ›7j½¥`ÿöVÓðàu¥k£§ç×yZÎŒÎvíÇ ¶Ïðc¦¶¶tm´î3üxIØÃÑ ˆ'àq¤Ç W[ZÎ1rgú½Kô ?fZ{‘ר¹$ìßÞjZ=^­´¬õkxŒ×c¾öäÖ—ðkx/÷p›sz^5Jëxœëy1¥ù¥aG+´j æPúæò¼_Së1­½Ú:£¶6׋®­½(¥sç¼Æœû©Áþí­¦½ „1õÆÔÆKôü:OmŸß÷ö ?fZÏ:3²vIØÃÑ ííL ¶×Ï寵½¹ÞԘɭÅ\Ë9X?µ–Ç¥õ¹ù¥µ<.õsóF­·ìßÞjÚÛÕBxØÃÑ íýBƒýÛ[M{ýB†=­!†€ýÛ[M „öp´B „öoo5)bØÃÑ )bØ¿½Õ¤@ˆa`G+¤@ˆ!`ÿöVÓÞ"÷K=Ó&¨Ë½ÒµØìáh…V Ä&þc)Æ&ŒØz¦±ìßÞjZ5sB¡@{8Z¡ÕÑŠÖ@Øb¦Æ zSç”® ìõóbØ¿½Õ´è EÉX<ï×´ö<Ö+í^~,–=­ÐÎ" 3UN5|ãÞ}FôZlöoo5 ÷„ðØ:ƒ±Ÿ÷”ö-׆!±ØÃÑ ÷‚É™’Áxê¬Ú9-מZOôÁþí­¦ÕÑK‹©¼As{¢çþ\Ðrmø±Xöp´B«b­¦²uX›ÛÓr΀—® ìõóbØ¿½Õ´Z vtØÃÑ ) İ{«éÀB?¾Œ{8Z¡ÿ„cÀþí­&B {8Z!B û··š1 ìáh…1ìßÞjR Ä0°‡£R İ{«iïß',õ{…¥ÎÙ|¯ûtß›€=­Ðj˜û·LFÎ�sL±†R €ýÛ[M«bSíÚk b¿aG+´z zCyóm-T"·&7g`ìç™Ò^Æ÷jcœ5µ†ñã‘aÿöVÓN¢'­ovÄ$Ü«í³ëÖsŒÒÚ©u×Öµ\ØÃÑ íL ¢¡È™„rëJ´î«‘£å5K5÷r×µ½£Âþí­¦!Ÿ%ÃKì«l «z¥ lÌþÚ÷ìáh…†ü áMÂ,±¯v†QÛëA¯T ¿¿Ô«íöoo5­ˆ9äÞt?75f¸WÛW;Ãðk#ëÚØŸ×r}P`G+´j –�rLõï—öMcð¾©õ-ç—Î+]~<2ìßÞjZ-B, {8Z!B û··š1 ìáh…1ìßÞjR Ä0°‡£R İ{«IÃÀŽVHCÀþí­&B {8Z!B û··šV „ýi—½°‡£šˆmüqž-°{«iv æ„¢d~…BôÀŽVh‘@ô†¢56†Œs=ó¹ž öoo5-ˆžP”ŒÊó~ï•ÖÖö‰1aG+´h ¢¡(™´Õؽ=1ìßÞjÚû'ã{^blØÃÑ íüg¿¦·'ƆýÛ[M‹¢—œik&7j¦oí‰1aG+4;s0“zå(õ[ƹ}b<Ø¿½Õ4+Bììáh…1ìßÞjR Ä0°‡£R İ{«IÃÀŽVHCÀþí­&B {8Z!B û··š1 ìáh…1ìßÞjÚË@”þc©ù(ûþ§!S÷¿/_{8Z¡Õ±©? ¬õjÌyÓ[÷úus^Ó3÷þKûk½]‚ýÛ[M«bÉP`\šŸbΛÞûs^Ó3÷þKûk½]ƒ=­Ðêè …“0.Í~ Wˆ)Í{Zöù9?µy®LmOnÞƒ~n?W`cˆáõÜË­]öoo5íD zBá¿Á—æ »öcPÛgø1ÓzÎÔ™S{}Ÿ‰œåA¯´‡çKk »žoöp´B;ˆ9¡ðßè©yÀãÈZOë9SgFöz¢g3¹×ÉÍþœÖžáÇKÂþí­¦½}Bø—¾ñSos=¯Üó{|ɽ�_çðý©1“{ÜœáÏií~¼4ìáh…öö3„opé?õ†ðxjmÚ9ÌÔkDöz–:×¹9ßÓÚ3üxIØ¿½Õ´z æRú&çækoP­gäέçLÙ뉞ÍDööö ?^öp´B«b JßäÜ|í ²ë\?7ïiÝççjkró5J{róžÜ>&7†˜èxIØ¿½Õ´Z „Xöp´B „öoo5)bØÃÑ )bØ¿½Õ¤@ˆa`G+¤@ˆ!`ÿöV“!†=­!†€ýÛ[M „öp´B „öoo5ím ð§9-ÁRç”Øôùöp´B«bîß2Í5ÔÔ~b¿`ÿöVÓª˜ BxØÃÑ ­ˆÞP´ÊÖA 7çñë™\Ϫ_g`Þ÷0ÎõDöoo5íD zB#åøÚ¨õ<‘sJk{÷‰>ØÃÑ íL ¢¡è1PÄ€µµ½=¦¶OÄaÿöVÓ^?!Z°u,0µ¿¶¶·ÇÔö‰>ØÃÑ ý¢fº©ýµµ½=¦¶OÄaÿöVÓêè¥Å@Þœ~\£¶vÓ=Ñ{8Z¡U13PI Ïq/·–ñ=¬÷ûrë˜Ücj,b°{«iµ@±$ðmÌë¸V ÄÀ¿Q�ø^CàýÛ*B ‰÷o«1$Þ¿­R Äxÿ¶JCâýkÆ. k°NÃáýkâ@€×(b8¼¡ZÐW Äpxÿ²Ja@OÃáýëUê)bH¼[¥@ˆ!ñþm•!†Äû·U „˜¹G „óëMbŽøÖî_›ênÖ9ZÒoÕ@ôÀ µÁ½üõ©înÂ~[B“àq‹| |›ÊÝ‹ênVÝ*" Ü îCu?jD;û?Åçæw1v{QÝÍꯡ’Ï %Á¯Û»É} îCu?*«æ5ÓÜ@vÆá@à÷)v{QÝÍꯡˆß¢2l¿ s8¯)|s-7è÷o[¸܇ê~TÖ”çØo¶×Äó8OOÕU«¿†"~k•aûL£‡óôBug*«æ5û­E†í1ñ}œ „iŸa׸Õݬþª…ÁÄïñ” [oÊM8/ˆ’p ]÷ì_R¸܇ê~ÔˆØo5¶Ö”C8oè@Ø5îEu7«¿n¿Ç%¶ÎTc-ÎÓBugjDì·œ [cª±Þ®‹Àƨpƒ¹Þ¶å麛Ք{ÿZT „5-c“]ga“£_—êîÖÜû‘Ã2jæ÷cÈÆv^ö !IM „$‘Î!’tu:ö?’$™ÎMÿ_ìzâ^ ˆ����IEND®B`‚�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������listparser-0.20/feeds.opml��������������������������������������������������������������������������0000664�0000000�0000000�00000001045�14601530666�0015611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0"?> <opml version="2.0"> <head> <title>listparser project feeds Kurt McKee contactme@kurtmckee.org https://kurtmckee.org/ listparser-0.20/pyproject.toml000066400000000000000000000034641460153066600165550ustar00rootroot00000000000000[tool.poetry] name = "listparser" version = "0.20" description = "Parse OPML subscription lists" authors = ["Kurt McKee "] license = "MIT" readme = "README.rst" repository = "https://github.com/kurtmckee/listparser/" documentation = "https://listparser.readthedocs.io/en/latest/" keywords = ["opml", "foaf", "igoogle", "feed"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Text Processing :: Markup :: XML", ] [tool.poetry.dependencies] python = ">=3.8" # The dependencies here must match the minimums tested in `tox.ini`. requests = {version = "^2.25.1", optional = true} lxml = {version = ">=4.6.2,<6.0.0", optional = true} [tool.poetry.extras] http = ["requests"] lxml = ["lxml"] [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.coverage.run] relative_files = true parallel = true branch = true source = [ "listparser", "tests", ] [tool.coverage.paths] source = [ "src", "*/site-packages", ] [tool.coverage.report] skip_covered = true fail_under = 100 [tool.coverage.html] directory = "htmlcov/" skip_covered = false [tool.scriv] version = "literal: pyproject.toml: tool.poetry.version" categories = [ "Python support", "Added", "Fixed", "Removed", "Changed", "Deprecated", "Security", ] entry_title_template = "{{ version }} - {{ date.strftime('%Y-%m-%d') }}" format = "rst" fragment_directory = "changelog.d" insert_marker = "scriv-insert-here" main_branches = ["main", "releases"] new_fragment_template = "file: fragment-template.rst.txt" [tool.isort] profile = "black" [tool.mypy] packages = "listparser" strict = true sqlite_cache = true [tool.pytest.ini_options] addopts = "--color=yes" filterwarnings = [ "error", ] listparser-0.20/requirements/000077500000000000000000000000001460153066600163555ustar00rootroot00000000000000listparser-0.20/requirements/README.rst000066400000000000000000000026111460153066600200440ustar00rootroot00000000000000``requirements/`` ################# This directory contains the files that manage dependencies for the project. At the time of writing, Poetry supports discrete dependency groups but always resolves dependencies coherently across all groups. However, in some cases, dependencies do not need to be coherently resolved; for example, mypy's dependencies do not need to be resolved together with Sphinx's dependencies. Each subdirectory in this directory contains a ``pyproject.toml`` file with purpose-specific dependencies listed. How it's used ============= Tox is configured to use the exported ``requirements.txt`` files as needed. In addition, Read the Docs is configured to use ``docs/requirements.txt``. This helps ensure reproducible testing, linting, and documentation builds. How it's updated ================ A tox label, ``update``, ensures that dependencies can be easily updated, and that ``requirements.txt`` files are consistently re-exported. This can be invoked by running: .. code-block:: tox run -m update How to add dependencies ======================= New dependencies can be added to a given subdirectory's ``pyproject.toml`` by either manually modifying the file, or by running a command like: .. code-block:: poetry add --lock --directory "requirements/$DIR" $DEPENDENCY_NAME Either way, the dependencies must be re-exported: .. code-block:: tox run -m update listparser-0.20/requirements/docs/000077500000000000000000000000001460153066600173055ustar00rootroot00000000000000listparser-0.20/requirements/docs/poetry.lock000066400000000000000000001123331460153066600215040ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = ">=3.6" files = [ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] [[package]] name = "babel" version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "certifi" version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] [[package]] name = "importlib-metadata" version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "jinja2" version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "packaging" version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytz" version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "sphinx" version = "7.1.2" description = "Python documentation generator" optional = false python-versions = ">=3.8" files = [ {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.13" requests = ">=2.25.0" snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] [[package]] name = "sphinxcontrib-applehelp" version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.8" files = [ {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.8" files = [ {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "urllib3" version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "zipp" version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.8" content-hash = "1ce270dc0313d418be8794572c3f580211677584900271b35b743649527ebdaa" listparser-0.20/requirements/docs/pyproject.toml000066400000000000000000000001771460153066600222260ustar00rootroot00000000000000[tool.poetry.dependencies] python = ">=3.8" sphinx = "*" [tool.poetry] name = "" version = "0" description = "" authors = [] listparser-0.20/requirements/docs/requirements.txt000066400000000000000000000023621460153066600225740ustar00rootroot00000000000000alabaster==0.7.13 ; python_version >= "3.8" babel==2.14.0 ; python_version >= "3.8" certifi==2024.2.2 ; python_version >= "3.8" charset-normalizer==3.3.2 ; python_version >= "3.8" colorama==0.4.6 ; python_version >= "3.8" and sys_platform == "win32" docutils==0.20.1 ; python_version >= "3.8" idna==3.6 ; python_version >= "3.8" imagesize==1.4.1 ; python_version >= "3.8" importlib-metadata==7.1.0 ; python_version < "3.10" and python_version >= "3.8" jinja2==3.1.3 ; python_version >= "3.8" markupsafe==2.1.5 ; python_version >= "3.8" packaging==24.0 ; python_version >= "3.8" pygments==2.17.2 ; python_version >= "3.8" pytz==2024.1 ; python_version < "3.9" and python_version >= "3.8" requests==2.31.0 ; python_version >= "3.8" snowballstemmer==2.2.0 ; python_version >= "3.8" sphinx==7.1.2 ; python_version >= "3.8" sphinxcontrib-applehelp==1.0.4 ; python_version >= "3.8" sphinxcontrib-devhelp==1.0.2 ; python_version >= "3.8" sphinxcontrib-htmlhelp==2.0.1 ; python_version >= "3.8" sphinxcontrib-jsmath==1.0.1 ; python_version >= "3.8" sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.8" sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.8" urllib3==2.2.1 ; python_version >= "3.8" zipp==3.18.1 ; python_version < "3.10" and python_version >= "3.8" listparser-0.20/requirements/mypy/000077500000000000000000000000001460153066600173535ustar00rootroot00000000000000listparser-0.20/requirements/mypy/poetry.lock000066400000000000000000000173411460153066600215550ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "lxml-stubs" version = "0.5.1" description = "Type annotations for the lxml package" optional = false python-versions = "*" files = [ {file = "lxml-stubs-0.5.1.tar.gz", hash = "sha256:e0ec2aa1ce92d91278b719091ce4515c12adc1d564359dfaf81efa7d4feab79d"}, {file = "lxml_stubs-0.5.1-py3-none-any.whl", hash = "sha256:1f689e5dbc4b9247cb09ae820c7d34daeb1fdbd1db06123814b856dae7787272"}, ] [package.extras] test = ["coverage[toml] (>=7.2.5)", "mypy (>=1.2.0)", "pytest (>=7.3.0)", "pytest-mypy-plugins (>=1.10.1)"] [[package]] name = "mypy" version = "1.9.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "types-requests" version = "2.31.0.20240311" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ {file = "types-requests-2.31.0.20240311.tar.gz", hash = "sha256:b1c1b66abfb7fa79aae09097a811c4aa97130eb8831c60e47aee4ca344731ca5"}, {file = "types_requests-2.31.0.20240311-py3-none-any.whl", hash = "sha256:47872893d65a38e282ee9f277a4ee50d1b28bd592040df7d1fdaffdf3779937d"}, ] [package.dependencies] urllib3 = ">=2" [[package]] name = "types-toml" version = "0.10.8.20240310" description = "Typing stubs for toml" optional = false python-versions = ">=3.8" files = [ {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, ] [[package]] name = "typing-extensions" version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] name = "urllib3" version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = ">=3.8" content-hash = "359c336cd04ebb45307d1f68a814bec9f202162c330c5787b61e8d07f66a9bc3" listparser-0.20/requirements/mypy/pyproject.toml000066400000000000000000000002641460153066600222710ustar00rootroot00000000000000[tool.poetry.dependencies] python = ">=3.8" mypy = "*" lxml-stubs = "*" types-requests = "*" types-toml = "*" [tool.poetry] name = "" version = "0" description = "" authors = [] listparser-0.20/requirements/mypy/requirements.txt000066400000000000000000000006231460153066600226400ustar00rootroot00000000000000lxml-stubs==0.5.1 ; python_version >= "3.8" mypy-extensions==1.0.0 ; python_version >= "3.8" mypy==1.9.0 ; python_version >= "3.8" tomli==2.0.1 ; python_version < "3.11" and python_version >= "3.8" types-requests==2.31.0.20240311 ; python_version >= "3.8" types-toml==0.10.8.20240310 ; python_version >= "3.8" typing-extensions==4.10.0 ; python_version >= "3.8" urllib3==2.2.1 ; python_version >= "3.8" listparser-0.20/requirements/test/000077500000000000000000000000001460153066600173345ustar00rootroot00000000000000listparser-0.20/requirements/test/poetry.lock000066400000000000000000000343271460153066600215410ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "importlib-metadata" version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "packaging" version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pluggy" version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pytest" version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.4,<2.0" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-randomly" version = "3.15.0" description = "Pytest plugin to randomly order tests and control random.seed." optional = false python-versions = ">=3.8" files = [ {file = "pytest_randomly-3.15.0-py3-none-any.whl", hash = "sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6"}, {file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"}, ] [package.dependencies] importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} pytest = "*" [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "zipp" version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.8" content-hash = "ebfffd680b7ea84d2bdbedf40a6aff77b3e645155f439d3016b84f2d6ff32992" listparser-0.20/requirements/test/pyproject.toml000066400000000000000000000003031460153066600222440ustar00rootroot00000000000000[tool.poetry.dependencies] python = ">=3.8" coverage = {extras = ["toml"], version = "*"} pytest = "*" pytest-randomly = "*" [tool.poetry] name = "" version = "0" description = "" authors = [] listparser-0.20/requirements/test/requirements.txt000066400000000000000000000011711460153066600226200ustar00rootroot00000000000000colorama==0.4.6 ; python_version >= "3.8" and sys_platform == "win32" coverage[toml]==7.4.4 ; python_version >= "3.8" exceptiongroup==1.2.0 ; python_version < "3.11" and python_version >= "3.8" importlib-metadata==7.1.0 ; python_version < "3.10" and python_version >= "3.8" iniconfig==2.0.0 ; python_version >= "3.8" packaging==24.0 ; python_version >= "3.8" pluggy==1.4.0 ; python_version >= "3.8" pytest-randomly==3.15.0 ; python_version >= "3.8" pytest==8.1.1 ; python_version >= "3.8" tomli==2.0.1 ; python_full_version <= "3.11.0a6" and python_version >= "3.8" zipp==3.18.1 ; python_version < "3.10" and python_version >= "3.8" listparser-0.20/src/000077500000000000000000000000001460153066600144215ustar00rootroot00000000000000listparser-0.20/src/listparser/000077500000000000000000000000001460153066600166115ustar00rootroot00000000000000listparser-0.20/src/listparser/__init__.py000066400000000000000000000064321460153066600207270ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import io import typing as t try: import requests import urllib3.exceptions except ImportError: requests = None # type: ignore[assignment] urllib3 = None # type: ignore[assignment] try: import lxml.etree except ImportError: lxml = None # type: ignore[assignment] from . import common, foaf, igoogle, opml, xml_handler from .exceptions import ListparserError __author__ = "Kurt McKee " __url__ = "https://github.com/kurtmckee/listparser" __version__ = "0.19" Handler = type( "Handler", ( opml.OpmlMixin, foaf.FoafMixin, igoogle.IgoogleMixin, xml_handler.XMLHandler, ), {}, ) def parse(parse_obj: str | bytes) -> common.SuperDict: """Parse a subscription list and return a dict containing the results. *parse_obj* must be one of the following: * a string containing a URL * a string or bytes object containing an XML document The dictionary returned will contain all of the parsed information, HTTP response headers (if applicable), and any exception encountered. """ guarantees: dict[str, t.Any] = { "bozo": False, "bozo_exception": None, "feeds": [], "lists": [], "opportunities": [], "meta": common.SuperDict(), "version": "", } content, info = get_content(parse_obj) guarantees.update(info) if not content: return common.SuperDict(guarantees) handler = Handler() handler.harvest.update(guarantees) if lxml is not None: content_file = io.BytesIO(content) parser = lxml.etree.HTMLParser(target=handler, recover=True) lxml.etree.parse(content_file, parser) else: handler.feed(content.decode()) harvest = common.SuperDict(handler.harvest) handler.close() del handler return harvest def get_content(obj: bytes | str) -> tuple[bytes | None, dict[str, t.Any]]: if isinstance(obj, bytes): return obj, {"bozo": False, "bozo_exception": None} elif not isinstance(obj, str): # Only str and bytes objects can be parsed. error = ListparserError("parse() called with unparsable object") return None, {"bozo": True, "bozo_exception": error} elif not obj.startswith(("http://", "https://")): # It's not a URL, so it must be treated as an XML document. return obj.encode("utf8"), { "bozo": False, "bozo_exception": None, } # It's a URL. Confirm requests is installed. elif requests is None: message = f"requests is not installed so {obj} cannot be retrieved" return None, { "bozo": True, "bozo_exception": ListparserError(message), } headers = {"user-agent": f"listparser/{__version__} +{__url__}"} try: response = requests.get(obj, headers=headers, timeout=30) except ( requests.exceptions.RequestException, urllib3.exceptions.HTTPError, ) as error: return None, {"bozo": True, "bozo_exception": error} return response.text.encode("utf8"), { "bozo": False, "bozo_exception": None, } listparser-0.20/src/listparser/common.py000066400000000000000000000033621460153066600204570ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import typing as t from .exceptions import ListparserError from .xml_handler import XMLHandler class SuperDict(t.Dict[str, t.Any]): """ SuperDict is a dictionary object with keys posing as instance attributes. .. code-block:: pycon >>> i = SuperDict() >>> i.one = 1 >>> i {'one': 1} """ def __getattribute__(self, name: str) -> t.Any: if name in self: return self[name] else: return dict.__getattribute__(self, name) class Common(XMLHandler): def __init__(self) -> None: super().__init__() self.harvest: dict[str, t.Any] = {} self.hierarchy: list[str] = [] self.flag_feed = False # found_urls = {url: (append_to_key, obj)} self.found_urls: dict[str, tuple[str, SuperDict]] = {} def raise_bozo(self, error: str) -> None: self.harvest["bozo"] = True self.harvest["bozo_exception"] = ListparserError(error) def expect_text(self, _: t.Any) -> None: """Flag that text content is anticipated. Many start_* methods only need to prepare for text content. This method exists so those start_* methods can be declared as aliases for this method. """ self.flag_expect_text = True self.text = [] def get_text(self) -> str: """Get text content.""" text = "".join(self.text).strip() self.text = [] return text def close(self) -> None: super().close() self.hierarchy = [] self.flag_feed = False self.found_urls = {} listparser-0.20/src/listparser/dates.py000066400000000000000000000103241460153066600202630ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import datetime months: dict[str, int] = { "jan": 1, "feb": 2, "mar": 3, "apr": 4, "may": 5, "jun": 6, "jul": 7, "aug": 8, "sep": 9, "oct": 10, "nov": 11, "dec": 12, } timezones: dict[str, int] = { # Universal Time "ut": 0, "utc": 0, "gmt": 0, # North America "est": -5, "edt": -4, "cst": -6, "cdt": -5, "mst": -7, "mdt": -6, "pst": -8, "pdt": -7, # Military "z": 0, "a": -1, "n": +1, "m": -12, "y": +12, } def parse_rfc822(date: str) -> datetime.datetime | None: """Parse RFC 822 dates and times. https://tools.ietf.org/html/rfc822#section-5 The basic format is: .. code-block:: text [ day "," ] dd mmm [yy]yy hh:mm[:ss] zzz Note that RFC 822 only specifies an explicit comma, but fails to make whitespace mandatory. Some non-standard formatting differences are allowed: * Whitespace is assumed to separate each part of the timestamp. * Years may be two or four digits. This is explicitly allowed in the OPML specification. * The month name and day can be swapped. * Timezones may be prefixed with "Etc/". * If the time and/or timezone are missing, midnight and GMT will be assumed. * "UTC" is supported as a timezone name. """ parts = date.rpartition(",")[2].lower().split() if len(parts) == 3: # Assume that the time is missing. parts.append("00:00:00") if len(parts) == 4: # Assume that the timezone is missing. parts.append("gmt") elif len(parts) != 5: # If there are not exactly five parts then this isn't an # RFC 822 date and time. return None # Parse the day and month. try: day = int(parts[0]) except ValueError: # Check if the day and month are swapped. if months.get(parts[0][:3]): try: day = int(parts[1]) except ValueError: return None else: parts[1] = parts[0] else: return None month = months.get(parts[1][:3]) if month is None: return None # Parse the year. try: year = int(parts[2]) except ValueError: return None # Normalize two-digit years: # # * Anything in the 90's is interpreted as the 1990's. # * Anything 89 or before is interpreted as 2089 or before. # if year < 100: if year >= 90: year += 1900 else: year += 2000 # Parse the time. time_parts = parts[3].split(":") time_parts += ["0"] * (3 - len(time_parts)) try: hour, minute, second = map(int, time_parts) except ValueError: return None # Parse named timezones. tz_min = 0 # Strip 'Etc/' from the timezone name. timezone = parts[4] if timezone.startswith("etc/"): timezone = timezone[4:] or "gmt" # Normalize timezones that start with 'gmt': # # * gmt-05:00 => -05:00 # * gmt => gmt # if timezone.startswith("gmt"): timezone = timezone[3:] or "gmt" tz_hour = timezones.get(timezone) # Parse numeric timezones like '-0500' and '+0500'. if tz_hour is None: try: tz_left, tz_right = timezone.split(":") tz_hour = int(tz_left) tz_min = int(tz_right) except ValueError: # Perhaps there was no ':' in *timezone*. try: tz_hour = int(timezone[:-2]) tz_min = int(timezone[-2:]) except ValueError: return None if tz_hour < 0: tz_min = tz_min * -1 # Create the datetime and timezone offset return values. try: return datetime.datetime( year, month, day, hour, minute, second, tzinfo=datetime.timezone( datetime.timedelta(minutes=(tz_hour * 60) + tz_min) ), ) except (ValueError, OverflowError): return None listparser-0.20/src/listparser/exceptions.py000066400000000000000000000002541460153066600213450ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # class ListparserError(Exception): pass listparser-0.20/src/listparser/foaf.py000066400000000000000000000122551460153066600201030ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import copy import typing as t from . import common # Attribute key constants RDF_ABOUT = "rdf:about" RDF_RESOURCE = "rdf:resource" class FoafMixin(common.Common): def __init__(self) -> None: super().__init__() self.flag_agent = False self.flag_group = False self.flag_new_title = False self.flag_opportunity = False # group_objs = [(append_to_key, obj)] self.group_objs: list[tuple[str, common.SuperDict]] = [] self.agent_feeds: list[str] = [] self.agent_lists: list[str] = [] self.agent_opps: list[str] = [] self.foaf_name: list[str] = [] def close(self) -> None: super().close() self.flag_agent = False self.flag_group = False self.flag_new_title = False self.flag_opportunity = False self.group_objs = [] self.agent_feeds = [] self.agent_lists = [] self.agent_opps = [] self.foaf_name = [] def start_rdf_rdf(self, _: t.Any) -> None: self.harvest["version"] = "rdf" def start_rss_channel(self, attrs: dict[str, str]) -> None: if attrs.get(RDF_ABOUT, "").strip(): # We now have a feed URL, so forget about any opportunity URL. if self.flag_opportunity: self.flag_opportunity = False self.agent_opps.pop() agent_feed = attrs[RDF_ABOUT].strip() self.agent_feeds.append(agent_feed) def start_ya_feed(self, attrs: dict[str, str]) -> None: if attrs.get(RDF_RESOURCE, "").strip(): # This is a feed URL agent_feed = attrs[RDF_RESOURCE].strip() self.agent_feeds.append(agent_feed) def _clean_found_objs(self) -> None: if self.foaf_name: title = self.foaf_name[-1] else: title = "" for url in self.agent_feeds: obj = common.SuperDict({"url": url, "title": title}) self.group_objs.append(("feeds", obj)) for url in self.agent_lists: obj = common.SuperDict({"url": url, "title": title}) self.group_objs.append(("lists", obj)) for url in self.agent_opps: obj = common.SuperDict({"url": url, "title": title}) self.group_objs.append(("opportunities", obj)) def start_foaf_agent(self, _: t.Any) -> None: self.flag_agent = True self.flag_feed = True self.flag_new_title = True def end_foaf_agent(self) -> None: if self.flag_agent: self.flag_agent = False self._clean_found_objs() if self.foaf_name: self.foaf_name.pop() self.agent_feeds = [] self.agent_lists = [] self.agent_opps = [] self.flag_agent = False self.flag_feed = False self.flag_opportunity = False def start_foaf_person(self, _: t.Any) -> None: self.flag_feed = True self.flag_new_title = True self._clean_found_objs() end_foaf_person = end_foaf_agent def start_rdfs_seealso(self, attrs: dict[str, str]) -> None: if attrs.get(RDF_RESOURCE, "").strip(): # This is a subscription list URL agent_list = attrs[RDF_RESOURCE].strip() self.agent_lists.append(agent_list) def start_foaf_group(self, _: t.Any) -> None: self.flag_group = True def end_foaf_group(self) -> None: self.flag_group = False for key, obj in self.group_objs: # Check for duplicates if obj["url"] in self.found_urls: obj = self.found_urls[obj["url"]][1] else: self.found_urls[obj["url"]] = (key, obj) self.harvest[key].append(obj) # Create or consolidate categories and tags obj.setdefault("categories", []) obj.setdefault("tags", []) if self.hierarchy and self.hierarchy not in obj["categories"]: obj["categories"].append(copy.copy(self.hierarchy)) if len(self.hierarchy) == 1 and self.hierarchy[0] not in obj["tags"]: obj["tags"].extend(copy.copy(self.hierarchy)) self.group_objs = [] # Maintain the hierarchy if self.hierarchy: self.hierarchy.pop() end_rdf_rdf = end_foaf_group start_foaf_name = common.Common.expect_text def end_foaf_name(self) -> None: value = self.get_text() if self.flag_feed and self.flag_new_title: self.foaf_name.append(value) self.flag_new_title = False elif self.flag_group and value: self.hierarchy.append(value) self.flag_group = False start_foaf_member_name = common.Common.expect_text end_foaf_member_name = end_foaf_name def start_foaf_document(self, attrs: dict[str, str]) -> None: if attrs.get(RDF_ABOUT, "").strip(): # Flag this as an opportunity (but ignore if a feed URL is found). self.flag_opportunity = True agent_opp = attrs[RDF_ABOUT].strip() self.agent_opps.append(agent_opp) listparser-0.20/src/listparser/igoogle.py000066400000000000000000000024521460153066600206130ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import copy import typing as t from . import common class IgoogleMixin(common.Common): def start_gtml_gadgettabml(self, _: t.Any) -> None: self.harvest["version"] = "igoogle" def start_gtml_tab(self, attrs: dict[str, str]) -> None: if attrs.get("title", "").strip(): self.hierarchy.append(attrs["title"].strip()) def end_gtml_tab(self) -> None: if self.hierarchy: self.hierarchy.pop() def start_igoogle_module(self, attrs: dict[str, str]) -> None: if attrs.get("type", "").strip().lower() == "rss": self.flag_feed = True def end_igoogle_module(self) -> None: self.flag_feed = False def start_igoogle_moduleprefs(self, attrs: dict[str, str]) -> None: if self.flag_feed and attrs.get("xmlurl", "").strip(): obj = common.SuperDict({"url": attrs["xmlurl"].strip()}) obj["title"] = "" if self.hierarchy: obj["categories"] = [copy.copy(self.hierarchy)] if len(self.hierarchy) == 1: obj["tags"] = copy.copy(self.hierarchy) self.harvest["feeds"].append(obj) listparser-0.20/src/listparser/opml.py000066400000000000000000000113061460153066600201330ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import copy from . import common, dates class OpmlMixin(common.Common): def start_opml_opml(self, attrs: dict[str, str]) -> None: self.harvest["version"] = "opml" if attrs.get("version") in ("1.0", "1.1"): self.harvest["version"] = "opml1" elif attrs.get("version") == "2.0": self.harvest["version"] = "opml2" def start_opml_outline(self, attrs: dict[str, str]) -> None: # Find an appropriate title in @text or @title (else empty) if attrs.get("text", "").strip(): title = attrs["text"].strip() else: title = attrs.get("title", "").strip() url = None append_to = None # Determine whether the outline is a feed or subscription list if "xmlurl" in attrs: # It's a feed url = attrs.get("xmlurl", "").strip() append_to = "feeds" if attrs.get("type", "").strip().lower() == "source": # Actually, it's a subscription list! append_to = "lists" elif attrs.get("type", "").lower() in ("link", "include"): # It's a subscription list append_to = "lists" url = attrs.get("url", "").strip() elif title: # Assume that this is a grouping node self.hierarchy.append(title) return # Look for an opportunity URL if not url and "htmlurl" in attrs: url = attrs["htmlurl"].strip() append_to = "opportunities" if not url: # Maintain the hierarchy self.hierarchy.append("") return if url not in self.found_urls and append_to: # This is a brand-new URL obj = common.SuperDict({"url": url, "title": title}) self.found_urls[url] = (append_to, obj) self.harvest[append_to].append(obj) else: obj = self.found_urls[url][1] # Handle categories and tags obj.setdefault("categories", []) if "category" in attrs.keys(): for i in attrs["category"].split(","): tmp = [j.strip() for j in i.split("/") if j.strip()] if tmp and tmp not in obj["categories"]: obj["categories"].append(tmp) # Copy the current hierarchy into `categories` if self.hierarchy and self.hierarchy not in obj["categories"]: obj["categories"].append(copy.copy(self.hierarchy)) # Copy all single-element `categories` into `tags` obj["tags"] = [i[0] for i in obj["categories"] if len(i) == 1] self.hierarchy.append("") def end_opml_outline(self) -> None: self.hierarchy.pop() start_opml_title = common.Common.expect_text def end_opml_title(self) -> None: value = self.get_text() if value: self.harvest["meta"]["title"] = value start_opml_ownerid = common.Common.expect_text def end_opml_ownerid(self) -> None: value = self.get_text() if value: self.harvest["meta"].setdefault("author", common.SuperDict()) self.harvest["meta"]["author"]["url"] = value start_opml_owneremail = common.Common.expect_text def end_opml_owneremail(self) -> None: value = self.get_text() if value: self.harvest["meta"].setdefault("author", common.SuperDict()) self.harvest["meta"]["author"]["email"] = value start_opml_ownername = common.Common.expect_text def end_opml_ownername(self) -> None: value = self.get_text() if value: self.harvest["meta"].setdefault("author", common.SuperDict()) self.harvest["meta"]["author"]["name"] = value start_opml_datecreated = common.Common.expect_text def end_opml_datecreated(self) -> None: value = self.get_text() if value: self.harvest["meta"]["created"] = value timestamp = dates.parse_rfc822(value) if timestamp: self.harvest["meta"]["created_parsed"] = timestamp else: self.raise_bozo("dateCreated is not an RFC 822 datetime") start_opml_datemodified = common.Common.expect_text def end_opml_datemodified(self) -> None: value = self.get_text() if value: self.harvest["meta"]["modified"] = value timestamp = dates.parse_rfc822(value) if timestamp: self.harvest["meta"]["modified_parsed"] = timestamp else: self.raise_bozo("dateModified is not an RFC 822 datetime") listparser-0.20/src/listparser/py.typed000066400000000000000000000000001460153066600202760ustar00rootroot00000000000000listparser-0.20/src/listparser/xml_handler.py000066400000000000000000000174541460153066600214730ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # from __future__ import annotations import collections import dataclasses import html.parser import typing prefixes = { "http://opml.org/spec2": "opml", "http://www.google.com/ig": "igoogle", "http://schemas.google.com/GadgetTabML/2008": "gtml", "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", "http://www.w3.org/2000/01/rdf-schema#": "rdfs", "http://xmlns.com/foaf/0.1/": "foaf", "http://purl.org/dc/elements/1.1/": "dc", "http://purl.org/rss/1.0/": "rss", "http://blogs.yandex.ru/schema/foaf/": "ya", } uris = {v: k for k, v in prefixes.items()} @dataclasses.dataclass class Node: """Track information parsed from start tags. When a handler method for an end tag is called, it will use this information to manage the namespace stack. """ tag: str standard_prefix: str name: str namespace_prefixes: set[str] class XMLHandler(html.parser.HTMLParser): """ Handle parsing events emitted by an HTML parser. Although this class inherits from the Python's built-in HTMLParser, it deliberately favors the lxml API for the sake of speed. The HTMLParser API is supported using methods that alias the lxml API methods, or transform the input parameters and then call the lxml API methods. """ def __init__(self) -> None: super().__init__() # {prefix: [uri1, ...]} self.uris: dict[str, list[str]] = {} self.node_stack: collections.deque[Node] = collections.deque() # Cache element-to-method name lookups. self.start_methods: dict[ tuple[str, str], typing.Callable[[dict[str, str]], None] | None ] = {} self.end_methods: dict[tuple[str, str], typing.Callable[[], None] | None] = {} # *flag_expect_text* is set by `start_*()` methods that want to capture text. # While set, text is captured in chunks in the *text* attribute. # It is unset by `end_*()` methods. self.flag_expect_text: bool = False self.text: list[str] = [] def start(self, tag: str, attrs: dict[str, str]) -> None: """Handle the start of an XML element.""" # Extract XML namespaces from the attributes dictionary. # # The HTML parser converts attribute keys to lowercase. # # ========================= =========================== # Deployed XML Key/value in *attrs* # ========================= =========================== # {"xmlns": "URI"} # {"xmlns:prefix": "uri"} # {"xmlns": ""} # ========================= =========================== # attrs_excluding_xmlns = {} namespace_prefixes = set() for key, value in attrs.items(): if key.startswith("xmlns"): if value: _, _, declared_prefix = key.partition(":") self.uris.setdefault(declared_prefix, []).append(value) namespace_prefixes.add(declared_prefix) else: attrs_excluding_xmlns[key] = value # The tag will be in the form "name" or "prefix:name". deployed_prefix, _, name = tag.rpartition(":") # The deployed prefix used in the XML document is arbitrary, # but handler methods are named using standard prefix names. # The code below tries to map from the deployed prefix # to a declared URI (if any), and then to a standard prefix. # However, if there's no URI associated with the prefix, # the only identifier available is the deployed prefix itself # (which might be an empty string). identifier_list = self.uris.get( deployed_prefix, [uris.get(deployed_prefix, deployed_prefix)] ) if identifier_list: identifier = identifier_list[-1] else: identifier = "= sentinel: no identifier =" standard_prefix = prefixes.get(identifier, deployed_prefix) # Namespaces must be associated with the tags that introduce them # so the corresponding end tag can remove them from the list. node = Node( tag=tag, standard_prefix=standard_prefix, name=name, namespace_prefixes=namespace_prefixes, ) self.node_stack.append(node) try: start_method = self.start_methods[(standard_prefix, name)] except KeyError: if standard_prefix: start_method_name = f"start_{standard_prefix}_{name}" end_method_name = f"end_{standard_prefix}_{name}" else: start_method_name = f"start_opml_{name}" end_method_name = f"end_opml_{name}" start_method = getattr(self, start_method_name, None) end_method = getattr(self, end_method_name, None) self.start_methods[(standard_prefix, name)] = start_method self.end_methods[(standard_prefix, name)] = end_method if start_method is not None: start_method(attrs_excluding_xmlns) def end(self, tag: str) -> None: """Handle the end of an XML element.""" while True: try: node = self.node_stack.pop() except IndexError: deployed_prefix, _, name = tag.rpartition(":") identifier_list = self.uris.get( deployed_prefix, [uris.get(deployed_prefix, deployed_prefix)] ) if identifier_list: identifier = identifier_list[-1] else: identifier = "= sentinel: no identifier =" standard_prefix = prefixes.get(identifier, deployed_prefix) node = Node( tag=tag, standard_prefix=standard_prefix, name=name, namespace_prefixes=set(), ) for prefix in node.namespace_prefixes: self.uris[prefix].pop() end_method = self.end_methods.get((node.standard_prefix, node.name)) if end_method is not None: end_method() if node.tag == tag: break def data(self, data: str) -> None: """Handle text content of an element.""" if self.flag_expect_text: self.text.append(data) def close(self) -> None: """Reset the handler.""" super().close() self.start_methods = {} self.end_methods = {} self.flag_expect_text = False self.text = [] # # Everything below this comment is a compatibility shim # to support the HTMLParser API in Python's standard library. # def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: """Handle the start of an XML element.""" # The HTML parser converts attribute names to lowercase. # However, attribute values may be `None`. # # ========================= =========================== # Deployed XML Tuple in *attrs* # ========================= =========================== # ("xmlns", "URI") # ("xmlns:prefix", "uri") # ("xmlns", None) # ========================= =========================== # # *attrs* must be modified to match what the LXML parser expects. return self.start(tag, {key: value or "" for key, value in attrs}) # These HTML and LXML methods' signatures and code are identical, # but the method names differ. handle_endtag = end handle_data = data listparser-0.20/tests/000077500000000000000000000000001460153066600147745ustar00rootroot00000000000000listparser-0.20/tests/igoogle/000077500000000000000000000000001460153066600164215ustar00rootroot00000000000000listparser-0.20/tests/igoogle/GadgetTabML.xml000066400000000000000000000003461460153066600212210ustar00rootroot00000000000000 listparser-0.20/tests/igoogle/ModulePrefs_xmlUrl-wrong_type.xml000066400000000000000000000006051460153066600251270ustar00rootroot00000000000000 listparser-0.20/tests/igoogle/ModulePrefs_xmlUrl.xml000066400000000000000000000005621460153066600227360ustar00rootroot00000000000000 listparser-0.20/tests/igoogle/hierarchy-bad.xml000066400000000000000000000007031460153066600216450ustar00rootroot00000000000000 listparser-0.20/tests/igoogle/hierarchy-multiple-tabs.xml000066400000000000000000000014131460153066600237000ustar00rootroot00000000000000 listparser-0.20/tests/igoogle/hierarchy.xml000066400000000000000000000007241460153066600211240ustar00rootroot00000000000000 listparser-0.20/tests/malformed/000077500000000000000000000000001460153066600167425ustar00rootroot00000000000000listparser-0.20/tests/malformed/concatenated-documents.xml000066400000000000000000000012451460153066600241150ustar00rootroot00000000000000 listparser-0.20/tests/malformed/end-element-missing.xml000066400000000000000000000005301460153066600233260ustar00rootroot00000000000000 listparser-0.20/tests/malformed/end-element-unexpected-with-prefix.xml000066400000000000000000000007251460153066600262730ustar00rootroot00000000000000 listparser-0.20/tests/malformed/end-element-unexpected.xml000066400000000000000000000005761460153066600240330ustar00rootroot00000000000000 listparser-0.20/tests/malformed/html-entity-attribute.xml000066400000000000000000000006651460153066600237520ustar00rootroot00000000000000 listparser-0.20/tests/malformed/html-entity-text-1.xml000066400000000000000000000006031460153066600230610ustar00rootroot00000000000000 archæology archæology listparser-0.20/tests/malformed/html-entity-text-2.xml000066400000000000000000000006061460153066600230650ustar00rootroot00000000000000 1 μs is brief 1 μs is brief listparser-0.20/tests/malformed/undefined-entity-attribute.xml000066400000000000000000000004541460153066600247430ustar00rootroot00000000000000 listparser-0.20/tests/malformed/undefined-entity-text.xml000066400000000000000000000004461460153066600237250ustar00rootroot00000000000000 arch&egregiouslyundefined;ology listparser-0.20/tests/malformed/xmlns-without-value.xml000066400000000000000000000004531460153066600234420ustar00rootroot00000000000000 xmlns without attribute listparser-0.20/tests/opml/000077500000000000000000000000001460153066600157435ustar00rootroot00000000000000listparser-0.20/tests/opml/categories/000077500000000000000000000000001460153066600200705ustar00rootroot00000000000000listparser-0.20/tests/opml/categories/cats-1-single_standalone.xml000066400000000000000000000006611460153066600253740ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/cats-2-single_hierarchy.xml000066400000000000000000000007161460153066600252240ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/cats-3-multiple.xml000066400000000000000000000010151460153066600235320ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/cats-4-bad.xml000066400000000000000000000012451460153066600224330ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/duplication.xml000066400000000000000000000010411460153066600231210ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/hier-1-single.xml000066400000000000000000000010151460153066600231530ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/hier-2-nested.xml000066400000000000000000000010471460153066600231620ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/hier-3-whitespace.xml000066400000000000000000000006521460153066600240360ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/hier-4-mixed.xml000066400000000000000000000011001460153066600227760ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/hier-5-title.xml000066400000000000000000000007651460153066600230320ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/tags-1-single.xml000066400000000000000000000007261460153066600231720ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/tags-2-multiple.xml000066400000000000000000000011011460153066600235310ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/tags-3-cats_mixed.xml000066400000000000000000000010611460153066600240240ustar00rootroot00000000000000 listparser-0.20/tests/opml/categories/tags-4-bad.xml000066400000000000000000000011121460153066600224300ustar00rootroot00000000000000 listparser-0.20/tests/opml/head-empty_tags.xml000066400000000000000000000010751460153066600215430ustar00rootroot00000000000000 listparser-0.20/tests/opml/head_dateCreated-invalid.xml000066400000000000000000000005251460153066600233010ustar00rootroot00000000000000 sometime last week-ish, I think listparser-0.20/tests/opml/head_dateCreated-whitespace.xml000066400000000000000000000006731460153066600240130ustar00rootroot00000000000000 Sun, 21 Jun 2009 12:00:00 GMT listparser-0.20/tests/opml/head_dateCreated.xml000066400000000000000000000006161460153066600216560ustar00rootroot00000000000000 Sun, 21 Jun 2009 12:00:00 GMT listparser-0.20/tests/opml/head_dateModified-invalid.xml000066400000000000000000000005311460153066600234470ustar00rootroot00000000000000 sometime last week-ish, I think listparser-0.20/tests/opml/head_dateModified-whitespace.xml000066400000000000000000000007001460153066600241530ustar00rootroot00000000000000 Sun, 21 Jun 2009 12:00:00 GMT listparser-0.20/tests/opml/head_dateModified.xml000066400000000000000000000006231460153066600220250ustar00rootroot00000000000000 Sun, 21 Jun 2009 12:00:00 GMT listparser-0.20/tests/opml/head_ownerEmail-whitespace.xml000066400000000000000000000005741460153066600237100ustar00rootroot00000000000000 ownerEmail@example.com listparser-0.20/tests/opml/head_ownerEmail.xml000066400000000000000000000005171460153066600215530ustar00rootroot00000000000000 ownerEmail@example.com listparser-0.20/tests/opml/head_ownerId-whitespace.xml000066400000000000000000000005711460153066600232120ustar00rootroot00000000000000 http://example.com/contact listparser-0.20/tests/opml/head_ownerId.xml000066400000000000000000000005141460153066600210550ustar00rootroot00000000000000 http://example.com/contact listparser-0.20/tests/opml/head_ownerName-whitespace.xml000066400000000000000000000005401460153066600235320ustar00rootroot00000000000000 authorname listparser-0.20/tests/opml/head_ownerName.xml000066400000000000000000000004631460153066600214040ustar00rootroot00000000000000 authorname listparser-0.20/tests/opml/head_title-whitespace.xml000066400000000000000000000005111460153066600227160ustar00rootroot00000000000000 listtitle listparser-0.20/tests/opml/head_title.xml000066400000000000000000000004341460153066600205700ustar00rootroot00000000000000 listtitle listparser-0.20/tests/opml/include/000077500000000000000000000000001460153066600173665ustar00rootroot00000000000000listparser-0.20/tests/opml/include/outline_text-whitespace.xml000066400000000000000000000005601460153066600247660ustar00rootroot00000000000000 listparser-0.20/tests/opml/include/outline_text.xml000066400000000000000000000005031460153066600226310ustar00rootroot00000000000000 listparser-0.20/tests/opml/include/outline_type-case_insensitive.xml000066400000000000000000000004601460153066600261610ustar00rootroot00000000000000 listparser-0.20/tests/opml/include/outline_type-include.xml000066400000000000000000000004521460153066600242520ustar00rootroot00000000000000 listparser-0.20/tests/opml/include/outline_type-no_url.xml000066400000000000000000000003331460153066600241230ustar00rootroot00000000000000 listparser-0.20/tests/opml/include/outline_url-whitespace.xml000066400000000000000000000005501460153066600246030ustar00rootroot00000000000000 listparser-0.20/tests/opml/include/outline_url.xml000066400000000000000000000005171460153066600224540ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/000077500000000000000000000000001460153066600167005ustar00rootroot00000000000000listparser-0.20/tests/opml/link/outline_text-whitespace.xml000066400000000000000000000005561460153066600243050ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/outline_text.xml000066400000000000000000000005011460153066600221410ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/outline_type-case_insensitive.xml000066400000000000000000000004611460153066600254740ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/outline_type-link.xml000066400000000000000000000004501460153066600230740ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/outline_type-no_url.xml000066400000000000000000000003251460153066600234360ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/outline_url-whitespace.xml000066400000000000000000000005521460153066600241170ustar00rootroot00000000000000 listparser-0.20/tests/opml/link/outline_url.xml000066400000000000000000000005211460153066600217610ustar00rootroot00000000000000 listparser-0.20/tests/opml/opml_version-1.xml000066400000000000000000000003431460153066600213370ustar00rootroot00000000000000 listparser-0.20/tests/opml/opml_version-11.xml000066400000000000000000000003451460153066600214220ustar00rootroot00000000000000 listparser-0.20/tests/opml/opml_version-2.xml000066400000000000000000000003431460153066600213400ustar00rootroot00000000000000 listparser-0.20/tests/opml/opml_version-missing.xml000066400000000000000000000003411460153066600226460ustar00rootroot00000000000000 listparser-0.20/tests/opml/opml_version-unknown.xml000066400000000000000000000003641460153066600227010ustar00rootroot00000000000000 listparser-0.20/tests/opml/opportunities/000077500000000000000000000000001460153066600206675ustar00rootroot00000000000000listparser-0.20/tests/opml/opportunities/opml-htmlUrl-empty.xml000066400000000000000000000004501460153066600251400ustar00rootroot00000000000000 listparser-0.20/tests/opml/opportunities/opml-htmlUrl-miscapitalized.xml000066400000000000000000000007211460153066600270050ustar00rootroot00000000000000 listparser-0.20/tests/opml/opportunities/opml-htmlUrl-valid_feed.xml000066400000000000000000000005031460153066600260630ustar00rootroot00000000000000 listparser-0.20/tests/opml/opportunities/opml-htmlUrl.xml000066400000000000000000000006611460153066600240100ustar00rootroot00000000000000 listparser-0.20/tests/opml/opportunities/opml-htmlUrl_alone.xml000066400000000000000000000005121460153066600251610ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline-duplication-1.xml000066400000000000000000000005571460153066600226220ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline-duplication-2.xml000066400000000000000000000013031460153066600226110ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_text-overrides_title.xml000066400000000000000000000005041460153066600244100ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_text-whitespace.xml000066400000000000000000000005411460153066600233420ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_text.xml000066400000000000000000000004431460153066600212110ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_title-text_empty.xml000066400000000000000000000005271460153066600235510ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_title-text_missing-whitespace.xml000066400000000000000000000005651460153066600262200ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_title-text_missing.xml000066400000000000000000000005041460153066600240570ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-case_insensitive.xml000066400000000000000000000004441460153066600245400ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-link.xml000066400000000000000000000006561460153066600221470ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-missing.xml000066400000000000000000000005051460153066600226540ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-pie.xml000066400000000000000000000005301460153066600217560ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-rss.xml000066400000000000000000000004321460153066600220110ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-source.xml000066400000000000000000000011501460153066600225000ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_type-unknown.xml000066400000000000000000000004441460153066600227040ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_xmlUrl-empty.xml000066400000000000000000000005141460153066600226430ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_xmlUrl-miscapitalized.xml000066400000000000000000000005621460153066600245120ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_xmlUrl-whitespace.xml000066400000000000000000000005061460153066600236420ustar00rootroot00000000000000 listparser-0.20/tests/opml/outline_xmlUrl.xml000066400000000000000000000004551460153066600215130ustar00rootroot00000000000000 listparser-0.20/tests/rdf/000077500000000000000000000000001460153066600155475ustar00rootroot00000000000000listparser-0.20/tests/rdf/foaf_agent-multiple_weblogs.xml000066400000000000000000000032201460153066600237320ustar00rootroot00000000000000 User listparser-0.20/tests/rdf/foaf_agent-title-1.xml000066400000000000000000000013351460153066600216410ustar00rootroot00000000000000 User listparser-0.20/tests/rdf/foaf_agent-title-2.xml000066400000000000000000000013351460153066600216420ustar00rootroot00000000000000 User listparser-0.20/tests/rdf/foaf_agent-title-whitespace.xml000066400000000000000000000013601460153066600236330ustar00rootroot00000000000000 User listparser-0.20/tests/rdf/foaf_agent-url-2.xml000066400000000000000000000014431460153066600213230ustar00rootroot00000000000000 listparser-0.20/tests/rdf/foaf_agent-url-whitespace.xml000066400000000000000000000014061460153066600233150ustar00rootroot00000000000000 listparser-0.20/tests/rdf/foaf_agent-url.xml000066400000000000000000000013571460153066600211700ustar00rootroot00000000000000 listparser-0.20/tests/rdf/foaf_group-1.xml000066400000000000000000000017401460153066600205600ustar00rootroot00000000000000 Planet X User listparser-0.20/tests/rdf/foaf_group-2.xml000066400000000000000000000017401460153066600205610ustar00rootroot00000000000000 User Planet X listparser-0.20/tests/rdf/foaf_group-duplication-1.xml000066400000000000000000000027651460153066600231010ustar00rootroot00000000000000 i18n User L10n User listparser-0.20/tests/rdf/foaf_group-duplication-2.xml000066400000000000000000000027631460153066600231000ustar00rootroot00000000000000 i18n User i18n User listparser-0.20/tests/rdf/foaf_group-no_name.xml000066400000000000000000000017141460153066600220350ustar00rootroot00000000000000 User listparser-0.20/tests/rdf/foaf_knows-foaf_membername.xml000066400000000000000000000011671460153066600235330ustar00rootroot00000000000000 Bogus member listparser-0.20/tests/rdf/foaf_knows-foaf_name.xml000066400000000000000000000011601460153066600223340ustar00rootroot00000000000000 Bogus member listparser-0.20/tests/rdf/opportunities/000077500000000000000000000000001460153066600204735ustar00rootroot00000000000000listparser-0.20/tests/rdf/opportunities/rdf-foaf_Document-empty.xml000066400000000000000000000012011460153066600256650ustar00rootroot00000000000000 listparser-0.20/tests/rdf/opportunities/rdf-foaf_Document-missing.xml000066400000000000000000000011651460153066600262110ustar00rootroot00000000000000 listparser-0.20/tests/rdf/opportunities/rdf-foaf_Document-woven-1.xml000066400000000000000000000026431460153066600260360ustar00rootroot00000000000000 opp feed opp listparser-0.20/tests/rdf/opportunities/rdf-foaf_Document-woven-2.xml000066400000000000000000000026431460153066600260370ustar00rootroot00000000000000 feed opp feed listparser-0.20/tests/rdf/opportunities/rdf-foaf_Document.xml000066400000000000000000000014571460153066600245460ustar00rootroot00000000000000 opportunity listparser-0.20/tests/rdf/rss_channel-empty.xml000066400000000000000000000011651460153066600217270ustar00rootroot00000000000000 listparser-0.20/tests/rdf/version.xml000066400000000000000000000003261460153066600177570ustar00rootroot00000000000000 listparser-0.20/tests/rdf/yandex/000077500000000000000000000000001460153066600170375ustar00rootroot00000000000000listparser-0.20/tests/rdf/yandex/ya_blogActivity-ensure_name.xml000066400000000000000000000021671460153066600252170ustar00rootroot00000000000000 name friend listparser-0.20/tests/rdf/yandex/ya_blogActivity-ya_Comments-url.xml000066400000000000000000000014111460153066600257630ustar00rootroot00000000000000 listparser-0.20/tests/rdf/yandex/ya_blogActivity-ya_Posts-url.xml000066400000000000000000000014001460153066600253040ustar00rootroot00000000000000 listparser-0.20/tests/rdf/yandex/ya_feed-rdf_resource-empty.xml000066400000000000000000000011301460153066600247640ustar00rootroot00000000000000 listparser-0.20/tests/test_dates.py000066400000000000000000000124531460153066600175120ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # import datetime import pytest import listparser.dates @pytest.mark.parametrize( "date, expected_values", [ ("Sun, 14 Jun 2009 11:47:32 GMT", (2009, 6, 14, 11, 47, 32)), ("Sun, Dec 16 2012 11:15:01 GMT", (2012, 12, 16, 11, 15, 1)), ("Sun, Dec 16 2012", (2012, 12, 16, 0, 0, 0)), ("Thu, 5 Apr 2012 10:00:00 GMT", (2012, 4, 5, 10, 0, 0)), ("Sun, 21 Jun 2009 12:00 GMT", (2009, 6, 21, 12, 0, 0)), ], ) def test_format_variations(date, expected_values): keys = ("year", "month", "day", "hour", "minute", "second") result = listparser.dates.parse_rfc822(date) for key, expected_value in zip(keys, expected_values): assert getattr(result, key) == expected_value assert result is not None assert result.tzinfo == datetime.timezone(datetime.timedelta(0)) @pytest.mark.parametrize( "date, expected_year", [ ("Wed, 21 Jun 00 12:00:00 GMT", 2000), ("Wed, 21 Jun 89 12:00:00 GMT", 2089), ("Thu, 21 Jun 90 12:00:00 GMT", 1990), ("Mon, 21 Jun 99 12:00:00 GMT", 1999), ], ) def test_two_digit_years(date, expected_year): result = listparser.dates.parse_rfc822(date) assert result is not None assert result.year == expected_year @pytest.mark.parametrize( "date, expected_month", [ ("21 Jan 2009 12:00:00 GMT", 1), ("21 Feb 2009 12:00:00 GMT", 2), ("21 Mar 2009 12:00:00 GMT", 3), ("21 Apr 2009 12:00:00 GMT", 4), ("21 May 2009 12:00:00 GMT", 5), ("21 Jun 2009 12:00:00 GMT", 6), ("21 Jul 2009 12:00:00 GMT", 7), ("21 Aug 2009 12:00:00 GMT", 8), ("21 Sep 2009 12:00:00 GMT", 9), ("21 Oct 2009 12:00:00 GMT", 10), ("21 Nov 2009 12:00:00 GMT", 11), ("21 Dec 2009 12:00:00 GMT", 12), ], ) def test_month_names(date, expected_month): result = listparser.dates.parse_rfc822(date) assert result is not None assert result.month == expected_month @pytest.mark.parametrize( "date, hour, minute, offset", [ # Universal timezones ("Mon, 22 Jun 2009 13:15:17 UT", 13, 15, 0), ("Mon, 22 Jun 2009 13:15:17 GMT", 13, 15, 0), # North American timezones ("Mon, 22 Jun 2009 13:15:17 EST", 13, 15, -5 * 60), ("Mon, 22 Jun 2009 13:15:17 EDT", 13, 15, -4 * 60), ("Mon, 22 Jun 2009 13:15:17 CST", 13, 15, -6 * 60), ("Mon, 22 Jun 2009 13:15:17 CDT", 13, 15, -5 * 60), ("Mon, 22 Jun 2009 13:15:17 MST", 13, 15, -7 * 60), ("Mon, 22 Jun 2009 13:15:17 MDT", 13, 15, -6 * 60), ("Mon, 22 Jun 2009 13:15:17 PST", 13, 15, -8 * 60), ("Mon, 22 Jun 2009 13:15:17 PDT", 13, 15, -7 * 60), # Military timezones ("Mon, 22 Jun 2009 13:15:17 Z", 13, 15, 0), ("Mon, 22 Jun 2009 13:15:17 A", 13, 15, -1 * 60), ("Mon, 22 Jun 2009 13:15:17 N", 13, 15, +1 * 60), ("Mon, 22 Jun 2009 13:15:17 M", 13, 15, -12 * 60), ("Mon, 22 Jun 2009 13:15:17 Y", 13, 15, +12 * 60), # Numeric timezones ("Mon, 22 Jun 2009 13:15:17 -0430", 13, 15, (-4 * 60) - 30), ("Mon, 22 Jun 2009 13:15:17 +0545", 13, 15, (5 * 60) + 45), ("Mon, 22 Jun 2009 13:15:17 0545", 13, 15, (5 * 60) + 45), # Non-standard timezones ("Mon, 22 Jun 2009 13:15:17 UTC", 13, 15, 0), ("Mon, 22 Jun 2009 13:15:17 Etc/GMT", 13, 15, 0), ("Mon, 22 Jun 2009 13:15:17 Etc/", 13, 15, 0), ], ) def test_timezones(date, hour, minute, offset): result = listparser.dates.parse_rfc822(date) assert result is not None assert result.hour == hour assert result.minute == minute tz_info = datetime.timezone(datetime.timedelta(minutes=offset)) assert result.tzinfo == tz_info @pytest.mark.parametrize( "date", [ "Sun, 99 Jun 2009 12:00:00 GMT", # range day high "Sun, 00 Jun 2009 12:00:00 GMT", # range day low "Sun, 01 Jun 2009 99:00:00 GMT", # range hour "Sun, 01 Jun 2009 00:99:00 GMT", # range minute "Sun, 01 Jun 2009 00:00:99 GMT", # range second "Sun, 31 Dec 9999 23:59:59 -9999", # range year high "Sun, 01 Jan 0000 00:00:00 +9999", # range year low "yesterday", # too few parts "Sun, 16 Dec 2012 1:2:3:4 GMT", # too many time parts "Sun, 16 zzz 2012 11:47:32 GMT", # bad month name "Sun, Dec xx 2012 11:47:32 GMT", # swapped bad day "Sun, zzz 16 2012 11:47:32 GMT", # swapped bad month "Sun, 16 Dec zz 11:47:32 GMT", # bad year # Corrupt timezones "Sun, 31 Dec 9999 23:59:59 -999999999999999999999", # timezone range "Sun, 16 Dec 2012 11:47:32 +$$:00", # bad timezone hour with colon "Sun, 16 Dec 2012 11:47:32 -00:$$", # bad timezone minute with colon "Sun, 16 Dec 2012 11:47:32 :", # bad timezone minute with only a colon "Sun, 16 Dec 2012 11:47:32 -00:00:00", # bad timezone with extra colons "Sun, 16 Dec 2012 11:47:32 +$$00", # bad timezone hour without colon "Sun, 16 Dec 2012 11:47:32 +00$$", # bad timezone minute without colon "Sun, 16 Dec 2012 11:47:32 $", # bad negative timezone minute ], ) def test_invalid_dates(date): assert listparser.dates.parse_rfc822(date) is None listparser-0.20/tests/test_http.py000066400000000000000000000025301460153066600173640ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # import unittest.mock import pytest import listparser try: import requests except ImportError: requests = None empty_doc = '' @pytest.fixture def http(): def get(url, *args, **kwargs): if url == "http://": raise requests.exceptions.InvalidURL("no host supplied") else: mock = unittest.mock.Mock() mock.text = empty_doc return mock with unittest.mock.patch("listparser.requests.get", get): yield @pytest.mark.skipif(requests is None, reason="requests must be installed") def test_requests_success(http): content, info = listparser.get_content("http://example") assert content assert not info["bozo"] @pytest.mark.skipif(requests is None, reason="requests must be installed") def test_requests_error(http): content, info = listparser.get_content("http://") assert not content assert info["bozo"] @pytest.mark.skipif(bool(requests), reason="requests must NOT be installed") def test_requests_not_present(): content, info = listparser.get_content("http://example") assert not content assert info["bozo"] assert isinstance(info["bozo_exception"], listparser.ListparserError) listparser-0.20/tests/test_super_dict.py000066400000000000000000000006131460153066600205460ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # import pytest from listparser.common import SuperDict def test_attr_get(): sample = SuperDict() dict.__setitem__(sample, "a", 1) assert sample.a == 1 def test_attr_get_error(): with pytest.raises(AttributeError): assert SuperDict().bogus listparser-0.20/tests/test_xml.py000066400000000000000000000043031460153066600172050ustar00rootroot00000000000000# This file is part of listparser. # Copyright 2009-2024 Kurt McKee # SPDX-License-Identifier: MIT # import datetime # noqa: F401 (required by evals) import pathlib import unittest.mock import pytest import listparser tests_path = pathlib.Path(__file__).parent @pytest.fixture(scope="module") def use_dict(): with unittest.mock.patch("listparser.common.SuperDict", dict): yield def test_return_guarantees(use_dict): result = listparser.parse(0) assert result["bozo"] empty_doc = '' @pytest.mark.parametrize( "src", [ empty_doc, # str empty_doc.encode("utf8"), # bytes ], ) def test_get_content_good(use_dict, src): content, info = listparser.get_content(src) assert content is not None assert not info["bozo"] def test_get_content_bad(use_dict): content, info = listparser.get_content(123) assert content is None assert info["bozo"] tests = [] for _file in tests_path.rglob("**/*.xml"): _info = {} _assertions = [] blob = _file.read_text("utf8", errors="replace") for _line in blob.splitlines(): # pragma: no branch if "-->" in _line: break if _line.lstrip().startswith("Eval:"): _assertions.append(_line.partition(":")[2].strip()) elif ": " in _line: _key, _, _value = _line.strip().partition(": ") _info[_key] = _value description = _info.get("Description", "") if not description: # pragma: no cover message = f"Description not found in test {_file}" raise ValueError(message) if not _assertions: # pragma: no cover message = f"Eval not found in test {_file}" raise ValueError(message) tests.append( pytest.param( blob, _assertions, id=str(_file.relative_to(tests_path)), ) ) @pytest.mark.parametrize("src, assertions", tests) def test_file(use_dict, src, assertions): # `result` must exist in the local scope for the assertions to run. result = listparser.parse(src) # noqa: F841 lxml = listparser.lxml # noqa: F841 for assertion in assertions: assert eval(assertion) listparser-0.20/tests/unknown-namespace.xml000066400000000000000000000003111460153066600211420ustar00rootroot00000000000000 listparser-0.20/tox.ini000066400000000000000000000046411460153066600151520ustar00rootroot00000000000000[tox] min_version = 4.3.5 envlist = coverage_erase py{312, 311, 310, 39, 38}{-http-lxml,} py38-minimum_dependencies pypy3{-http,} coverage_report mypy docs labels = update=update skip_missing_interpreters = True isolated_build = True [testenv] package = wheel wheel_build_env = build_wheel depends = py{312, 311, 310, 39, 38, py3}{-http,}{-lxml,}{-minimum_dependencies,}: coverage_erase deps = -rrequirements/test/requirements.txt # The dependencies here must match the minimums declared in `pyproject.toml`. minimum_dependencies: requests==2.25.1 minimum_dependencies: lxml==4.6.2 extras = http: http lxml: lxml commands = coverage run -m pytest [testenv:mypy] deps = -rrequirements/mypy/requirements.txt commands = mypy [testenv:docs] base_python = py3.12 skipsdist = true skip_install = true deps = -rrequirements/docs/requirements.txt commands = sphinx-build -aWEnqb html docs/ build/docs [testenv:coverage_erase] skipsdist = true skip_install = true deps = -rrequirements/test/requirements.txt commands = coverage erase [testenv:coverage_report] depends = py{312, 311, 310, 39, 38, py3}{-http,}{-lxml,}{-minimum_dependencies,} skipsdist = true skip_install = true deps = -rrequirements/test/requirements.txt commands_pre = coverage combine coverage html --fail-under=0 commands = coverage report [testenv:update] base_python = py3.12 recreate = true description = Update tool dependency versions skip_install = true setenv = # The actionlint pre-commit hook needs the GOCACHE environment variables. GOCACHE={env_dir}/.gocache deps = poetry poetry-plugin-export pre-commit upadup commands = # Update test requirements poetry update --directory="requirements/docs" --lock poetry export --directory="requirements/docs" --output="requirements/docs/requirements.txt" --without-hashes poetry update --directory="requirements/mypy" --lock poetry export --directory="requirements/mypy" --output="requirements/mypy/requirements.txt" --without-hashes poetry update --directory="requirements/test" --lock poetry export --directory="requirements/test" --output="requirements/test/requirements.txt" --without-hashes # Update pre-commit hook versions pre-commit autoupdate upadup # Run pre-commit immediately, but ignore its exit code - pre-commit run -a [flake8] max-line-length = 88 extend-ignore = E203